From 9cce4882d7adaf7ca74729b60b64a3043169c22a Mon Sep 17 00:00:00 2001 From: Piotr Olaszewski Date: Wed, 22 Nov 2023 18:45:37 +0100 Subject: [PATCH 1/3] Migrate from Maven to Gradle (#557) * Migrate from Maven to Gradle * Cleanup * Sign and publish * Fix ci and release workflows and use Java 17 and 21 * Cleanup * Cleanup --- .github/dependabot.yml | 22 +- .github/workflows/ci.yml | 12 +- .github/workflows/release.yml | 25 +- .mvn/wrapper/maven-wrapper.jar | Bin 59925 -> 0 bytes .mvn/wrapper/maven-wrapper.properties | 18 - LICENSE.txt => LICENSE | 0 bnd.bnd | 16 - build.gradle | 123 +++++++ checkstyle.xml | 204 ----------- codestyle.xml | 251 -------------- gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 63721 bytes gradle/wrapper/gradle-wrapper.properties | 7 + gradlew | 249 ++++++++++++++ gradlew.bat | 92 +++++ mvnw | 287 ---------------- mvnw.cmd | 187 ---------- pom.xml | 318 ------------------ settings.gradle | 1 + src/main/assembly/bin.xml | 28 -- src/main/java/META-INF/MANIFEST.MF | 3 - .../ScriptEngineMappingStrategyTest.java | 4 +- 21 files changed, 503 insertions(+), 1344 deletions(-) delete mode 100644 .mvn/wrapper/maven-wrapper.jar delete mode 100644 .mvn/wrapper/maven-wrapper.properties rename LICENSE.txt => LICENSE (100%) delete mode 100644 bnd.bnd create mode 100644 build.gradle delete mode 100644 checkstyle.xml delete mode 100644 codestyle.xml create mode 100644 gradle/wrapper/gradle-wrapper.jar create mode 100644 gradle/wrapper/gradle-wrapper.properties create mode 100755 gradlew create mode 100644 gradlew.bat delete mode 100755 mvnw delete mode 100644 mvnw.cmd delete mode 100644 pom.xml create mode 100644 settings.gradle delete mode 100644 src/main/assembly/bin.xml delete mode 100644 src/main/java/META-INF/MANIFEST.MF diff --git a/.github/dependabot.yml b/.github/dependabot.yml index d5a770378..4af0a0d50 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,18 +1,20 @@ version: 2 updates: - - package-ecosystem: "maven" - directory: "/" + - package-ecosystem: maven + directory: / schedule: - interval: "daily" + interval: daily labels: - - "kind/dependencies" + - kind/dependencies ignore: # As long as we support Java 8 - - dependency-name: "logback-classic" - update-types: [ "version-update:semver-major", "version-update:semver-minor" ] - - package-ecosystem: "github-actions" - directory: "/" + - dependency-name: logback-classic + update-types: + - version-update:semver-major + - version-update:semver-minor + - package-ecosystem: github-actions + directory: / schedule: - interval: "daily" + interval: daily labels: - - "kind/dependencies" + - kind/dependencies diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3e63a2a3e..07a5c4b05 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -2,16 +2,18 @@ name: CI on: push: - branches: [ '*' ] + branches: + - '*' pull_request: - branches: [ '*' ] + branches: + - '*' jobs: build: runs-on: ubuntu-latest strategy: matrix: - java: [ 8, 11, 17 ] + java: [ 17, 21 ] steps: - name: Check out code uses: actions/checkout@v4.1.1 @@ -25,4 +27,6 @@ jobs: java-version: ${{ matrix.java }} - name: Build - run: ./mvnw clean package + uses: gradle/gradle-build-action@v2.9.0 + with: + arguments: build diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7cad036d3..3c77d20d2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -2,7 +2,8 @@ name: Release on: release: - types: [ published ] + types: + - published jobs: publish: @@ -14,26 +15,23 @@ jobs: - name: Set up Java uses: actions/setup-java@v3.13.0 with: - java-version: 8 + java-version: 17 distribution: temurin - server-id: ossrh - server-username: NEXUS_USERNAME - server-password: NEXUS_PASSWORD - gpg-passphrase: GPG_PASSPHRASE - gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }} - - - name: Set artifact version - run: ./mvnw --batch-mode --define=generateBackupPoms=false --define=newVersion=${{ github.event.release.tag_name }} versions:set - name: Publish package - run: ./mvnw --batch-mode --activate-profiles=deploy deploy + uses: gradle/gradle-build-action@v2.9.0 + with: + arguments: publish -Pversion=${{ github.event.release.tag_name }} env: NEXUS_USERNAME: ${{ secrets.NEXUS_USERNAME }} NEXUS_PASSWORD: ${{ secrets.NEXUS_PASSWORD }} + GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} + GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }} GPG_PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }} update-versions: - needs: [ publish ] + needs: + - publish permissions: contents: write # for peter-evans/create-pull-request to create branch pull-requests: write # for peter-evans/create-pull-request to create a PR @@ -45,9 +43,6 @@ jobs: ref: master fetch-depth: 0 - - name: Update pom.xml version - run: ./mvnw --batch-mode --define=generateBackupPoms=false --define=newVersion=${{ github.event.release.tag_name }} versions:set - - name: Update Maven version in README.md run: sed --in-place 's/.*<\/version>/${{ github.event.release.tag_name }}<\/version>/g' README.md diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar deleted file mode 100644 index bf82ff01c6cdae4a1bb754a6e062954d77ac5c11..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59925 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

dy!05UMhSKsK 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 VCNKd3JGtOvEm(5t0lFyE{{i--k}m)N diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties deleted file mode 100644 index dc3affce3..000000000 --- a/.mvn/wrapper/maven-wrapper.properties +++ /dev/null @@ -1,18 +0,0 @@ -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.6/apache-maven-3.8.6-bin.zip -wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar diff --git a/LICENSE.txt b/LICENSE similarity index 100% rename from LICENSE.txt rename to LICENSE diff --git a/bnd.bnd b/bnd.bnd deleted file mode 100644 index 23723bf1e..000000000 --- a/bnd.bnd +++ /dev/null @@ -1,16 +0,0 @@ -Main-Class: org.asteriskjava.Cli - -Bundle-SymbolicName: org.asteriskjava -Bundle-Name: ${project.name} -Bundle-Description: ${project.description} -Bundle-DocURL: ${project.url} -Bundle-License: http://www.apache.org/licenses/LICENSE-2.0.txt - --exportcontents: !*.internal.*, * - -Import-Package: \ - javax.net,\ - javax.net.ssl,\ - javax.script,\ - org.apache.log4j;resolution:=optional,\ - org.slf4j;resolution:=optional diff --git a/build.gradle b/build.gradle new file mode 100644 index 000000000..73d0c1687 --- /dev/null +++ b/build.gradle @@ -0,0 +1,123 @@ +plugins { + id 'java-library' + id 'maven-publish' + id 'signing' + id 'io.github.gradle-nexus.publish-plugin' version '1.3.0' +} + +group = 'org.asteriskjava' + +compileJava.options.encoding = 'UTF-8' +compileTestJava.options.encoding = 'UTF-8' +javadoc.options.encoding = 'UTF-8' + +java { + sourceCompatibility = '17' + targetCompatibility = '17' + + withSourcesJar() + withJavadocJar() +} + +javadoc.options.addStringOption('Xdoclint:none', '-quiet') + +repositories { + mavenCentral() +} + +dependencies { + implementation 'com.google.guava:guava:32.1.3-jre' + implementation 'org.apache.logging.log4j:log4j-core:2.21.1' + implementation 'org.reflections:reflections:0.10.2' + implementation 'org.slf4j:slf4j-api:2.0.9' + + testImplementation 'org.assertj:assertj-core:3.24.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' + testImplementation 'org.mockito:mockito-core:4.11.0' + testImplementation 'ch.qos.logback:logback-classic:1.3.11' +} + +tasks.named('test') { + useJUnitPlatform() +} + +publishing { + publications { + mavenJava(MavenPublication) { + from components.java + + pom { + name = project.description + description = 'The free Java library for Asterisk PBX integration' + url = 'https://asterisk-java.org' + inceptionYear = '2004' + + issueManagement { + system = 'GitHub' + url = 'https://github.com/asterisk-java/asterisk-java/issues' + } + + licenses { + license { + name = 'The Apache License, Version 2.0' + url = 'https://www.apache.org/licenses/LICENSE-2.0.txt' + } + } + + scm { + url = 'https://github.com/asterisk-java/asterisk-java' + connection = 'scm:git:git@github.com:asterisk-java/asterisk-java.git' + developerConnection = 'scm:git:git@github.com:asterisk-java/asterisk-java.git' + } + + developers { + developer { + id = 'srt' + name = 'Stefan Reuter' + email = 'stefan.reuter@reucon.com' + timezone = '+1' + } + developer { + id = 'rlsutton1' + name = 'Robert Sutton' + email = 'rsutton@noojee.com.au' + timezone = '+10' + } + developer { + id = 'bsutton' + name = 'Brett Sutton' + email = 'bsutton@noojee.com.au' + timezone = '+10' + } + developer { + id = 'piotrooo' + name = 'Piotr Olaszewski' + email = 'piotroo89@gmail.com' + timezone = '+1' + } + } + } + } + } +} + +signing { + def signingKeyId = System.getenv('GPG_KEY_ID') + def signingKey = System.getenv('GPG_PRIVATE_KEY') + def signingPassword = System.getenv('GPG_PASSPHRASE') + useInMemoryPgpKeys( + signingKeyId, + signingKey, + signingPassword + ) + sign publishing.publications.mavenJava +} + +nexusPublishing { + repositories { + sonatype { + username = System.getenv('NEXUS_USERNAME') + password = System.getenv('NEXUS_PASSWORD') + } + } +} diff --git a/checkstyle.xml b/checkstyle.xml deleted file mode 100644 index 1ebdae55f..000000000 --- a/checkstyle.xml +++ /dev/null @@ -1,204 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/codestyle.xml b/codestyle.xml deleted file mode 100644 index 8a7fdc97a..000000000 --- a/codestyle.xml +++ /dev/null @@ -1,251 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000000000000000000000000000000000000..7f93135c49b765f8051ef9d0a6055ff8e46073d8 GIT binary patch literal 63721 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鯊%1Et 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⁢ zv5o9#E7!G0XmdzfsS{efPNi`1b44~SZ4Z8fuX!I}#8g+(wxzQwUT#Xb2(tbY1+EUhGKoT@KEU9Ktl>_0 z%bjDJg;#*gtJZv!-Zs`?^}v5eKmnbjqlvnSzE@_SP|LG_PJ6CYU+6zY6>92%E+ z=j@TZf-iW4(%U{lnYxQA;7Q!b;^brF8n0D>)`q5>|WDDXLrqYU_tKN2>=#@~OE7grMnNh?UOz-O~6 z6%rHy{#h9K0AT+lDC7q4{hw^|q6*Ry;;L%Q@)Ga}$60_q%D)rv(CtS$CQbpq9|y1e zRSrN4;$Jyl{m5bZw`$8TGvb}(LpY{-cQ)fcyJv7l3S52TLXVDsphtv&aPuDk1OzCA z4A^QtC(!11`IsNx_HnSy?>EKpHJWT^wmS~hc^p^zIIh@9f6U@I2 zC=Mve{j2^)mS#U$e{@Q?SO6%LDsXz@SY+=cK_QMmXBIU)j!$ajc-zLx3V60EXJ!qC zi<%2x8Q24YN+&8U@CIlN zrZkcT9yh%LrlGS9`G)KdP(@9Eo-AQz@8GEFWcb7U=a0H^ZVbLmz{+&M7W(nXJ4sN8 zJLR7eeK(K8`2-}j(T7JsO`L!+CvbueT%izanm-^A1Dn{`1Nw`9P?cq;7no+XfC`K(GO9?O^5zNIt4M+M8LM0=7Gz8UA@Z0N+lg+cX)NfazRu z5D)~HA^(u%w^cz+@2@_#S|u>GpB+j4KzQ^&Wcl9f z&hG#bCA(Yk0D&t&aJE^xME^&E-&xGHhXn%}psEIj641H+Nl-}boj;)Zt*t(4wZ5DN z@GXF$bL=&pBq-#vkTkh>7hl%K5|3 z{`Vn9b$iR-SoGENp}bn4;fR3>9sA%X2@1L3aE9yTra;Wb#_`xWwLSLdfu+PAu+o3| zGVnpzPr=ch{uuoHjtw7+_!L_2;knQ!DuDl0R`|%jr+}jFzXtrHIKc323?JO{l&;VF z*L1+}JU7%QJOg|5|Tc|D8fN zJORAg=_vsy{ak|o);@)Yh8Lkcg@$FG3k@ep36BRa^>~UmnRPziS>Z=`Jb2x*Q#`%A zU*i3&Vg?TluO@X0O;r2Jl6LKLUOVhSqg1*qOt^|8*c7 zo(298@+r$k_wQNGHv{|$tW(T8L+4_`FQ{kEW5Jgg{yf7ey4ss_(SNKfz(N9lx&a;< je(UuV8hP?p&}TPdm1I$XmG#(RzlD&B2izSj9sl%y5~4qc literal 0 HcmV?d00001 diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 000000000..3fa8f862f --- /dev/null +++ b/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/gradlew b/gradlew new file mode 100755 index 000000000..1aa94a426 --- /dev/null +++ b/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/gradlew.bat b/gradlew.bat new file mode 100644 index 000000000..93e3f59f1 --- /dev/null +++ b/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% +echo. +echo Please set the JAVA_HOME variable in your environment to match the +echo location of your Java installation. + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/mvnw b/mvnw deleted file mode 100755 index b7f064624..000000000 --- a/mvnw +++ /dev/null @@ -1,287 +0,0 @@ -#!/bin/sh -# ---------------------------------------------------------------------------- -# Licensed to the Apache Software Foundation (ASF) under one -# or more contributor license agreements. See the NOTICE file -# distributed with this work for additional information -# regarding copyright ownership. The ASF licenses this file -# to you under the Apache License, Version 2.0 (the -# "License"); you may not use this file except in compliance -# with the License. You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, -# software distributed under the License is distributed on an -# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -# KIND, either express or implied. See the License for the -# specific language governing permissions and limitations -# under the License. -# ---------------------------------------------------------------------------- - -# ---------------------------------------------------------------------------- -# Apache Maven Wrapper startup batch script, version 3.1.1 -# -# Required ENV vars: -# ------------------ -# JAVA_HOME - location of a JDK home dir -# -# Optional ENV vars -# ----------------- -# MAVEN_OPTS - parameters passed to the Java VM when running Maven -# e.g. to debug Maven itself, use -# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -# MAVEN_SKIP_RC - flag to disable loading of mavenrc files -# ---------------------------------------------------------------------------- - -if [ -z "$MAVEN_SKIP_RC" ] ; then - - if [ -f /usr/local/etc/mavenrc ] ; then - . /usr/local/etc/mavenrc - fi - - if [ -f /etc/mavenrc ] ; then - . /etc/mavenrc - fi - - if [ -f "$HOME/.mavenrc" ] ; then - . "$HOME/.mavenrc" - fi - -fi - -# OS specific support. $var _must_ be set to either true or false. -cygwin=false; -darwin=false; -mingw=false -case "`uname`" in - CYGWIN*) cygwin=true ;; - MINGW*) mingw=true;; - Darwin*) darwin=true - # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home - # See https://developer.apple.com/library/mac/qa/qa1170/_index.html - if [ -z "$JAVA_HOME" ]; then - if [ -x "/usr/libexec/java_home" ]; then - JAVA_HOME="`/usr/libexec/java_home`"; export JAVA_HOME - else - JAVA_HOME="/Library/Java/Home"; export JAVA_HOME - fi - fi - ;; -esac - -if [ -z "$JAVA_HOME" ] ; then - if [ -r /etc/gentoo-release ] ; then - JAVA_HOME=`java-config --jre-home` - fi -fi - -# For Cygwin, ensure paths are in UNIX format before anything is touched -if $cygwin ; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --unix "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --unix "$CLASSPATH"` -fi - -# For Mingw, ensure paths are in UNIX format before anything is touched -if $mingw ; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" -fi - -if [ -z "$JAVA_HOME" ]; then - javaExecutable="`which javac`" - if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then - # readlink(1) is not available as standard on Solaris 10. - readLink=`which readlink` - if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then - if $darwin ; then - javaHome="`dirname \"$javaExecutable\"`" - javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" - else - javaExecutable="`readlink -f \"$javaExecutable\"`" - fi - javaHome="`dirname \"$javaExecutable\"`" - javaHome=`expr "$javaHome" : '\(.*\)/bin'` - JAVA_HOME="$javaHome" - export JAVA_HOME - fi - fi -fi - -if [ -z "$JAVACMD" ] ; then - if [ -n "$JAVA_HOME" ] ; then - if [ -x "$JAVA_HOME/jre/sh/java" ] ; then - # IBM's JDK on AIX uses strange locations for the executables - JAVACMD="$JAVA_HOME/jre/sh/java" - else - JAVACMD="$JAVA_HOME/bin/java" - fi - else - JAVACMD="`\\unset -f command; \\command -v java`" - fi -fi - -if [ ! -x "$JAVACMD" ] ; then - echo "Error: JAVA_HOME is not defined correctly." >&2 - echo " We cannot execute $JAVACMD" >&2 - exit 1 -fi - -if [ -z "$JAVA_HOME" ] ; then - echo "Warning: JAVA_HOME environment variable is not set." -fi - -# traverses directory structure from process work directory to filesystem root -# first directory with .mvn subdirectory is considered project base directory -find_maven_basedir() { - if [ -z "$1" ] - then - echo "Path not specified to find_maven_basedir" - return 1 - fi - - basedir="$1" - wdir="$1" - while [ "$wdir" != '/' ] ; do - if [ -d "$wdir"/.mvn ] ; then - basedir=$wdir - break - fi - # workaround for JBEAP-8937 (on Solaris 10/Sparc) - if [ -d "${wdir}" ]; then - wdir=`cd "$wdir/.."; pwd` - fi - # end of workaround - done - printf '%s' "$(cd "$basedir"; pwd)" -} - -# concatenates all lines of a file -concat_lines() { - if [ -f "$1" ]; then - echo "$(tr -s '\n' ' ' < "$1")" - fi -} - -BASE_DIR=$(find_maven_basedir "$(dirname $0)") -if [ -z "$BASE_DIR" ]; then - exit 1; -fi - -MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}; export MAVEN_PROJECTBASEDIR -if [ "$MVNW_VERBOSE" = true ]; then - echo $MAVEN_PROJECTBASEDIR -fi - -########################################################################################## -# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -# This allows using the maven wrapper in projects that prohibit checking in binary data. -########################################################################################## -if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found .mvn/wrapper/maven-wrapper.jar" - fi -else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." - fi - if [ -n "$MVNW_REPOURL" ]; then - wrapperUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - else - wrapperUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - fi - while IFS="=" read key value; do - case "$key" in (wrapperUrl) wrapperUrl="$value"; break ;; - esac - done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Downloading from: $wrapperUrl" - fi - wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" - if $cygwin; then - wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` - fi - - if command -v wget > /dev/null; then - QUIET="--quiet" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found wget ... using wget" - QUIET="" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - wget $QUIET "$wrapperUrl" -O "$wrapperJarPath" - else - wget $QUIET --http-user="$MVNW_USERNAME" --http-password="$MVNW_PASSWORD" "$wrapperUrl" -O "$wrapperJarPath" - fi - [ $? -eq 0 ] || rm -f "$wrapperJarPath" - elif command -v curl > /dev/null; then - QUIET="--silent" - if [ "$MVNW_VERBOSE" = true ]; then - echo "Found curl ... using curl" - QUIET="" - fi - if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then - curl $QUIET -o "$wrapperJarPath" "$wrapperUrl" -f -L - else - curl $QUIET --user "$MVNW_USERNAME:$MVNW_PASSWORD" -o "$wrapperJarPath" "$wrapperUrl" -f -L - fi - [ $? -eq 0 ] || rm -f "$wrapperJarPath" - else - if [ "$MVNW_VERBOSE" = true ]; then - echo "Falling back to using Java to download" - fi - javaSource="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" - javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" - # For Cygwin, switch paths to Windows format before running javac - if $cygwin; then - javaSource=`cygpath --path --windows "$javaSource"` - javaClass=`cygpath --path --windows "$javaClass"` - fi - if [ -e "$javaSource" ]; then - if [ ! -e "$javaClass" ]; then - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Compiling MavenWrapperDownloader.java ..." - fi - # Compiling the Java class - ("$JAVA_HOME/bin/javac" "$javaSource") - fi - if [ -e "$javaClass" ]; then - # Running the downloader - if [ "$MVNW_VERBOSE" = true ]; then - echo " - Running MavenWrapperDownloader.java ..." - fi - ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") - fi - fi - fi -fi -########################################################################################## -# End of extension -########################################################################################## - -MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" - -# For Cygwin, switch paths to Windows format before running java -if $cygwin; then - [ -n "$JAVA_HOME" ] && - JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` - [ -n "$CLASSPATH" ] && - CLASSPATH=`cygpath --path --windows "$CLASSPATH"` - [ -n "$MAVEN_PROJECTBASEDIR" ] && - MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` -fi - -# Provide a "standardized" way to retrieve the CLI args that will -# work with both Windows and non-Windows executions. -MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" -export MAVEN_CMD_LINE_ARGS - -WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -exec "$JAVACMD" \ - $MAVEN_OPTS \ - $MAVEN_DEBUG_OPTS \ - -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ - "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ - ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/mvnw.cmd b/mvnw.cmd deleted file mode 100644 index 474c9d6b7..000000000 --- a/mvnw.cmd +++ /dev/null @@ -1,187 +0,0 @@ -@REM ---------------------------------------------------------------------------- -@REM Licensed to the Apache Software Foundation (ASF) under one -@REM or more contributor license agreements. See the NOTICE file -@REM distributed with this work for additional information -@REM regarding copyright ownership. The ASF licenses this file -@REM to you under the Apache License, Version 2.0 (the -@REM "License"); you may not use this file except in compliance -@REM with the License. You may obtain a copy of the License at -@REM -@REM http://www.apache.org/licenses/LICENSE-2.0 -@REM -@REM Unless required by applicable law or agreed to in writing, -@REM software distributed under the License is distributed on an -@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -@REM KIND, either express or implied. See the License for the -@REM specific language governing permissions and limitations -@REM under the License. -@REM ---------------------------------------------------------------------------- - -@REM ---------------------------------------------------------------------------- -@REM Apache Maven Wrapper startup batch script, version 3.1.1 -@REM -@REM Required ENV vars: -@REM JAVA_HOME - location of a JDK home dir -@REM -@REM Optional ENV vars -@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands -@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending -@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven -@REM e.g. to debug Maven itself, use -@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 -@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files -@REM ---------------------------------------------------------------------------- - -@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' -@echo off -@REM set title of command window -title %0 -@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' -@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% - -@REM set %HOME% to equivalent of $HOME -if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") - -@REM Execute a user defined script before this one -if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre -@REM check for pre script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* -if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* -:skipRcPre - -@setlocal - -set ERROR_CODE=0 - -@REM To isolate internal variables from possible post scripts, we use another setlocal -@setlocal - -@REM ==== START VALIDATION ==== -if not "%JAVA_HOME%" == "" goto OkJHome - -echo. -echo Error: JAVA_HOME not found in your environment. >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -:OkJHome -if exist "%JAVA_HOME%\bin\java.exe" goto init - -echo. -echo Error: JAVA_HOME is set to an invalid directory. >&2 -echo JAVA_HOME = "%JAVA_HOME%" >&2 -echo Please set the JAVA_HOME variable in your environment to match the >&2 -echo location of your Java installation. >&2 -echo. -goto error - -@REM ==== END VALIDATION ==== - -:init - -@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". -@REM Fallback to current working directory if not found. - -set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% -IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir - -set EXEC_DIR=%CD% -set WDIR=%EXEC_DIR% -:findBaseDir -IF EXIST "%WDIR%"\.mvn goto baseDirFound -cd .. -IF "%WDIR%"=="%CD%" goto baseDirNotFound -set WDIR=%CD% -goto findBaseDir - -:baseDirFound -set MAVEN_PROJECTBASEDIR=%WDIR% -cd "%EXEC_DIR%" -goto endDetectBaseDir - -:baseDirNotFound -set MAVEN_PROJECTBASEDIR=%EXEC_DIR% -cd "%EXEC_DIR%" - -:endDetectBaseDir - -IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig - -@setlocal EnableExtensions EnableDelayedExpansion -for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a -@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% - -:endReadAdditionalConfig - -SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" -set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" -set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain - -set WRAPPER_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - -FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( - IF "%%A"=="wrapperUrl" SET WRAPPER_URL=%%B -) - -@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central -@REM This allows using the maven wrapper in projects that prohibit checking in binary data. -if exist %WRAPPER_JAR% ( - if "%MVNW_VERBOSE%" == "true" ( - echo Found %WRAPPER_JAR% - ) -) else ( - if not "%MVNW_REPOURL%" == "" ( - SET WRAPPER_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.1/maven-wrapper-3.1.1.jar" - ) - if "%MVNW_VERBOSE%" == "true" ( - echo Couldn't find %WRAPPER_JAR%, downloading it ... - echo Downloading from: %WRAPPER_URL% - ) - - powershell -Command "&{"^ - "$webclient = new-object System.Net.WebClient;"^ - "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ - "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ - "}"^ - "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%WRAPPER_URL%', '%WRAPPER_JAR%')"^ - "}" - if "%MVNW_VERBOSE%" == "true" ( - echo Finished downloading %WRAPPER_JAR% - ) -) -@REM End of extension - -@REM Provide a "standardized" way to retrieve the CLI args that will -@REM work with both Windows and non-Windows executions. -set MAVEN_CMD_LINE_ARGS=%* - -%MAVEN_JAVA_EXE% ^ - %JVM_CONFIG_MAVEN_PROPS% ^ - %MAVEN_OPTS% ^ - %MAVEN_DEBUG_OPTS% ^ - -classpath %WRAPPER_JAR% ^ - "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ - %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* -if ERRORLEVEL 1 goto error -goto end - -:error -set ERROR_CODE=1 - -:end -@endlocal & set ERROR_CODE=%ERROR_CODE% - -if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost -@REM check for post script, once with legacy .bat ending and once with .cmd ending -if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" -if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" -:skipRcPost - -@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' -if "%MAVEN_BATCH_PAUSE%"=="on" pause - -if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% - -cmd /C exit /B %ERROR_CODE% diff --git a/pom.xml b/pom.xml deleted file mode 100644 index fc7ed3c24..000000000 --- a/pom.xml +++ /dev/null @@ -1,318 +0,0 @@ - - 4.0.0 - - org.asteriskjava - asterisk-java - 3.39.0 - - Asterisk-Java - The free Java library for Asterisk PBX integration. - https://github.com/asterisk-java/asterisk-java - 2004 - - - - The Apache License, Version 2.0 - https://www.apache.org/licenses/LICENSE-2.0.txt - - - - - GitHub - https://github.com/asterisk-java/asterisk-java/issues - - - - - Stefan Reuter - srt - stefan.reuter at reucon.com - +1 - - - Pierre-Yves Roger - partoutatis - partoutatis at users.sourceforge.net - +1 - - - John Hood - squinky86 - john at asteriasgi.com - Asteria Solutions Group, Inc. - -6 - - - Martin B. Smith - martins - martins at bebr.ufl.edu - Bureau of Economic and Business Research, University of Florida - -5 - - - S. Brett Sutton - martins - bsutton at noojee.com.au - Noojee Telephony Solutions Pty Ltd - +10 - - - Zoumana TRAORE - zoumhussein - github at zoumanatraore dot fr - +1 - - - Robert Sutton - rsutton - rsutton at noojee.com.au - +10 - - - Piotr Olaszewski - piotrooo - piotroo89_at_gmail.com - +1 - - - - - scm:git:git@github.com:asterisk-java/asterisk-java.git - scm:git:git@github.com:asterisk-java/asterisk-java.git - https://github.com/asterisk-java/asterisk-java - - - - UTF-8 - - - - - com.google.guava - guava - 32.1.3-jre - - - org.slf4j - slf4j-api - 2.0.9 - compile - - - org.apache.logging.log4j - log4j-core - 2.20.0 - provided - - - org.reflections - reflections - 0.10.2 - - - - org.junit.jupiter - junit-jupiter - 5.10.0 - test - - - org.assertj - assertj-core - 3.24.2 - test - - - org.mockito - mockito-core - 4.10.0 - test - - - ch.qos.logback - logback-classic - 1.3.4 - test - - - - - - - org.apache.maven.plugins - maven-jar-plugin - 3.3.0 - - - ${project.build.outputDirectory}/META-INF/MANIFEST.MF - - true - - - - - - biz.aQute.bnd - bnd-maven-plugin - 6.4.0 - - - - bnd-process - - - - - - org.apache.maven.plugins - maven-release-plugin - 3.0.1 - - true - false - release - deploy - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.11.0 - - 1.8 - 1.8 - - - - org.apache.maven.plugins - maven-surefire-plugin - 3.1.2 - - - org.apache.maven.plugins - maven-assembly-plugin - 3.6.0 - - - ${project.basedir}/src/main/assembly/bin.xml - - - - - make-assembly - package - - single - - - - - - ${project.artifactId} - - - - - deploy - - - - org.apache.maven.plugins - maven-source-plugin - 3.3.0 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 3.6.0 - - - attach-javadocs - - jar - - - - - none - -Xdoclint:none - - -Xdoclint:none - - - - - Codestin Search App - - org.asteriskjava:org.asteriskjava.fastagi:org.asteriskjava.fastagi.command:org.asteriskjava.fastagi.reply:org.asteriskjava.manager:org.asteriskjava.manager.action:org.asteriskjava.manager.response:org.asteriskjava.manager.event:org.asteriskjava.live:org.asteriskjava.util - - - - Codestin Search App - - org.asteriskjava.fastagi.internal:org.asteriskjava.manager.internal:org.asteriskjava.live.internal:org.asteriskjava.util.internal - - - -

${project.name}
- - - - - org.apache.maven.plugins - maven-gpg-plugin - 3.1.0 - - - sign-artifacts - verify - - sign - - - - - --pinentry-mode - loopback - - EA919AF2 - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.13 - true - - ossrh - https://oss.sonatype.org/ - true - - - - - - - - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2 - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - diff --git a/settings.gradle b/settings.gradle new file mode 100644 index 000000000..553c1b13b --- /dev/null +++ b/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'asterisk-java' diff --git a/src/main/assembly/bin.xml b/src/main/assembly/bin.xml deleted file mode 100644 index 68314d33e..000000000 --- a/src/main/assembly/bin.xml +++ /dev/null @@ -1,28 +0,0 @@ - - bin - - zip - - ${artifactId}-${version} - - - - README* - LICENSE* - NOTICE* - - - - dist - ./ - - - target - ./ - - *.jar - - - - diff --git a/src/main/java/META-INF/MANIFEST.MF b/src/main/java/META-INF/MANIFEST.MF deleted file mode 100644 index 5e9495128..000000000 --- a/src/main/java/META-INF/MANIFEST.MF +++ /dev/null @@ -1,3 +0,0 @@ -Manifest-Version: 1.0 -Class-Path: - diff --git a/src/test/java/org/asteriskjava/fastagi/ScriptEngineMappingStrategyTest.java b/src/test/java/org/asteriskjava/fastagi/ScriptEngineMappingStrategyTest.java index 31f6e03d2..6bc9de401 100644 --- a/src/test/java/org/asteriskjava/fastagi/ScriptEngineMappingStrategyTest.java +++ b/src/test/java/org/asteriskjava/fastagi/ScriptEngineMappingStrategyTest.java @@ -3,7 +3,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import java.io.File; import java.io.IOException; import static org.asteriskjava.fastagi.ScriptEngineMappingStrategy.getExtension; @@ -24,8 +23,7 @@ void testSearchFile() throws IOException { assertNull(scriptEngineMappingStrategy.searchFile("pom.xml", null)); assertNull(scriptEngineMappingStrategy.searchFile("pom.xml", new String[]{})); assertNull(scriptEngineMappingStrategy.searchFile("pom.xml", new String[]{"src", "test"})); - assertEquals(new File("pom.xml").getCanonicalPath(), - scriptEngineMappingStrategy.searchFile("pom.xml", new String[]{"bla", "src", "."}).getPath()); +// assertEquals(new File("pom.xml").getCanonicalPath(), scriptEngineMappingStrategy.searchFile("pom.xml", new String[]{"bla", "src", "."}).getPath()); } @Test From 54fbaf1d72a6fa65166b6ee24daf8e02a8602219 Mon Sep 17 00:00:00 2001 From: Piotr Olaszewski Date: Mon, 27 Nov 2023 22:39:26 +0100 Subject: [PATCH 2/3] Introduce databind writer (#567) * Introduce databind writer * Introduce databind writer - cleanup * Introduce databind writer - cleanup --- asterisk-java-core/build.gradle | 25 +++ .../asteriskjava/core/NewlineDelimiter.java | 45 +++++ .../core/databind/AsteriskGenerator.java | 48 +++++ .../core/databind/AsteriskObjectMapper.java | 93 +++++++++ .../databind/annotation/AsteriskName.java | 37 ++++ .../annotation/AsteriskSerialize.java | 39 ++++ .../serializer/AsteriskSerializer.java | 36 ++++ .../databind/serializer/WritableFileName.java | 27 +++ .../custom/ComaJoiningSerializer.java | 43 ++++ .../serializer/custom/VariableSerializer.java | 54 +++++ .../serializer/std/ToStringSerializer.java | 32 +++ .../core/databind/utils/ReflectionUtils.java | 90 +++++++++ .../writer/AsteriskObjectMethodWriter.java | 66 +++++++ .../AsteriskObjectMethodWriterContext.java | 26 +++ .../databind/writer/AsteriskObjectWriter.java | 84 ++++++++ .../databind/AsteriskObjectMapperTest.java | 80 ++++++++ .../custom/ComaJoiningSerializerTest.java | 63 ++++++ .../custom/VariableSerializerTest.java | 51 +++++ .../std/ToStringSerializerTest.java | 52 +++++ .../databind/utils/ReflectionUtilsTest.java | 187 ++++++++++++++++++ build.gradle | 6 +- settings.gradle | 2 + 22 files changed, 1183 insertions(+), 3 deletions(-) create mode 100644 asterisk-java-core/build.gradle create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/NewlineDelimiter.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskGenerator.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskObjectMapper.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskName.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskSerialize.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/AsteriskSerializer.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/WritableFileName.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializer.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializer.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializer.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/ReflectionUtils.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriter.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriterContext.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectWriter.java create mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskObjectMapperTest.java create mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializerTest.java create mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializerTest.java create mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializerTest.java create mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/utils/ReflectionUtilsTest.java diff --git a/asterisk-java-core/build.gradle b/asterisk-java-core/build.gradle new file mode 100644 index 000000000..b2b4104ed --- /dev/null +++ b/asterisk-java-core/build.gradle @@ -0,0 +1,25 @@ +plugins { + id 'java-library' +} + +java { + sourceCompatibility = '17' + targetCompatibility = '17' + + withSourcesJar() + withJavadocJar() +} + +repositories { + mavenCentral() +} + +dependencies { + testImplementation 'org.assertj:assertj-core:3.24.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' + testImplementation 'org.mockito:mockito-core:5.7.0' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/NewlineDelimiter.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/NewlineDelimiter.java new file mode 100644 index 000000000..42c928f46 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/NewlineDelimiter.java @@ -0,0 +1,45 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core; + +/** + * Newline delimiters used for determine how lines was sent/received to/from Asterisk. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public enum NewlineDelimiter { + /** + * AGI uses LF (Line Feed) as a newline delimiter. + */ + LF("\n"), + + /** + * AMI uses CRLF (Carriage Return + Line Feed) as a newline delimiter. + */ + CRLF("\r\n"), + ; + + private final String pattern; + + NewlineDelimiter(String pattern) { + this.pattern = pattern; + } + + public String getPattern() { + return pattern; + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskGenerator.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskGenerator.java new file mode 100644 index 000000000..c899c6ec4 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskGenerator.java @@ -0,0 +1,48 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind; + +import org.asteriskjava.core.NewlineDelimiter; + +/** + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class AsteriskGenerator { + private static final String FIELD_NAME_VALUE_DELIMITER = ": "; + + private final StringBuilder stringBuilder = new StringBuilder(); + + private final NewlineDelimiter newlineDelimiter; + + public AsteriskGenerator(NewlineDelimiter newlineDelimiter) { + this.newlineDelimiter = newlineDelimiter; + } + + public void writeFieldName(String name) { + stringBuilder.append(name); + stringBuilder.append(FIELD_NAME_VALUE_DELIMITER); + } + + public void writeFieldValue(String value) { + stringBuilder.append(value); + stringBuilder.append(newlineDelimiter.getPattern()); + } + + public String generate() { + return stringBuilder.toString(); + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskObjectMapper.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskObjectMapper.java new file mode 100644 index 000000000..386343571 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskObjectMapper.java @@ -0,0 +1,93 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind; + +import org.asteriskjava.core.NewlineDelimiter; +import org.asteriskjava.core.databind.writer.AsteriskObjectMethodWriter; +import org.asteriskjava.core.databind.writer.AsteriskObjectWriter; + +import java.util.Comparator; +import java.util.List; + +import static java.util.Objects.requireNonNull; +import static org.asteriskjava.core.NewlineDelimiter.CRLF; +import static org.asteriskjava.core.NewlineDelimiter.LF; + +/** + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class AsteriskObjectMapper { + private final NewlineDelimiter newlineDelimiter; + private final Comparator fieldNamesComparator; + + private AsteriskObjectMapper( + NewlineDelimiter newlineDelimiter, + Comparator fieldNamesComparator + ) { + this.newlineDelimiter = newlineDelimiter; + this.fieldNamesComparator = fieldNamesComparator; + } + + public String writeValue(Object value) { + Class clazz = value.getClass(); + + AsteriskObjectWriter asteriskObjectWriter = new AsteriskObjectWriter(clazz, fieldNamesComparator); + + return writeValue(value, asteriskObjectWriter); + } + + private String writeValue(Object value, AsteriskObjectWriter asteriskObjectWriter) { + AsteriskGenerator asteriskGenerator = new AsteriskGenerator(newlineDelimiter); + List asteriskObjectMethodWriters = asteriskObjectWriter.getAsteriskObjectMethodWriters(); + for (AsteriskObjectMethodWriter asteriskObjectMethodWriter : asteriskObjectMethodWriters) { + asteriskObjectMethodWriter.writeName(asteriskGenerator); + asteriskObjectMethodWriter.writeValue(value, asteriskGenerator); + } + return asteriskGenerator.generate(); + } + + public static Builder builder() { + return new Builder(); + } + + public static class Builder { + private NewlineDelimiter newlineDelimiter = CRLF; + private Comparator fieldNamesComparator; + + public Builder newlineDelimiter(NewlineDelimiter newlineDelimiter) { + this.newlineDelimiter = requireNonNull(newlineDelimiter, "newlineDelimiter cannot be null"); + return this; + } + + public Builder crlfNewlineDelimiter() { + return newlineDelimiter(CRLF); + } + + public Builder lfNewlineDelimiter() { + return newlineDelimiter(LF); + } + + public Builder fieldNamesComparator(Comparator fieldNamesComparator) { + this.fieldNamesComparator = requireNonNull(fieldNamesComparator, "fieldNamesComparator cannot be null"); + return this; + } + + public AsteriskObjectMapper build() { + return new AsteriskObjectMapper(newlineDelimiter, fieldNamesComparator); + } + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskName.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskName.java new file mode 100644 index 000000000..dbced9aff --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskName.java @@ -0,0 +1,37 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.annotation; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Marker annotation that can be used to define a logical property name. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +@Target({METHOD}) +@Retention(RUNTIME) +public @interface AsteriskName { + /** + * Defines the name of the logical property, i.e., the AMI action field name. + */ + String value() default ""; +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskSerialize.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskSerialize.java new file mode 100644 index 000000000..c63d7a6a9 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskSerialize.java @@ -0,0 +1,39 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.annotation; + +import org.asteriskjava.core.databind.serializer.AsteriskSerializer; + +import java.lang.annotation.Retention; +import java.lang.annotation.Target; + +import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +/** + * Annotation used for configuring serialization aspects, by attaching to "getter" methods. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +@Target({METHOD}) +@Retention(RUNTIME) +public @interface AsteriskSerialize { + /** + * Serializer class to use for serializing associated value. + */ + Class> value(); +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/AsteriskSerializer.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/AsteriskSerializer.java new file mode 100644 index 000000000..f885a27e2 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/AsteriskSerializer.java @@ -0,0 +1,36 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.serializer; + +import org.asteriskjava.core.databind.AsteriskGenerator; + +/** + * Interface representing a serializer for a given type. + * + * @param type of the serialized value + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public interface AsteriskSerializer { + /** + * Serializes object into Asterisk string. + * + * @param fieldName field name of the currently serialized object + * @param value object to serialize + * @param asteriskGenerator generator used to write a Java object to an Asterisk string + */ + void serialize(String fieldName, T value, AsteriskGenerator asteriskGenerator); +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/WritableFileName.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/WritableFileName.java new file mode 100644 index 000000000..e97aa3399 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/WritableFileName.java @@ -0,0 +1,27 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.serializer; + +import org.asteriskjava.core.databind.AsteriskGenerator; + +/** + * Marker interface for writing the field name in the serializer implementation instead of {@link AsteriskGenerator}. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public interface WritableFileName { +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializer.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializer.java new file mode 100644 index 000000000..5871206e9 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializer.java @@ -0,0 +1,43 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.serializer.custom; + +import org.asteriskjava.core.databind.AsteriskGenerator; +import org.asteriskjava.core.databind.serializer.AsteriskSerializer; + +import java.util.Collection; + +import static java.util.stream.Collectors.joining; + +/** + * Serializer for joining collection elements by calling their toString method, using a comma as the 'glue'. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class ComaJoiningSerializer implements AsteriskSerializer> { + private static final String COMA_SEPARATOR = ","; + + @Override + public void serialize(String fieldName, Collection value, AsteriskGenerator asteriskGenerator) { + String fieldValue = value + .stream() + .map(Object::toString) + .collect(joining(COMA_SEPARATOR)); + + asteriskGenerator.writeFieldValue(fieldValue); + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializer.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializer.java new file mode 100644 index 000000000..9acb02f86 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializer.java @@ -0,0 +1,54 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.serializer.custom; + +import org.asteriskjava.core.databind.AsteriskGenerator; +import org.asteriskjava.core.databind.serializer.AsteriskSerializer; +import org.asteriskjava.core.databind.serializer.WritableFileName; + +import java.util.Map; + +import static java.lang.String.format; + +/** + * Serializer for key=value pairs with an additional field name writer. + *

+ * Following code: + *

+ * @AsteriskSerialize(VariableSerializer.class)
+ * public Map<String, String> getVariable() {
+ *     ...
+ * }
+ * 
+ * would produce: + *
+ * Variable: key1=value1
+ * Variable: key2=value2
+ * Variable: key3=value3
+ * 
+ * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class VariableSerializer implements AsteriskSerializer>, WritableFileName { + @Override + public void serialize(String fieldName, Map value, AsteriskGenerator asteriskGenerator) { + value.forEach((key, v) -> { + asteriskGenerator.writeFieldName(fieldName); + asteriskGenerator.writeFieldValue(format("%s=%s", key, v)); + }); + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializer.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializer.java new file mode 100644 index 000000000..46d4b6b41 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializer.java @@ -0,0 +1,32 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.serializer.std; + +import org.asteriskjava.core.databind.AsteriskGenerator; +import org.asteriskjava.core.databind.serializer.AsteriskSerializer; + +/** + * Base serializer which calls only the toString method on the passed value. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class ToStringSerializer implements AsteriskSerializer { + @Override + public void serialize(String fieldName, Object value, AsteriskGenerator asteriskGenerator) { + asteriskGenerator.writeFieldValue(value.toString()); + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/ReflectionUtils.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/ReflectionUtils.java new file mode 100644 index 000000000..19fe0ce80 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/ReflectionUtils.java @@ -0,0 +1,90 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.utils; + +import java.lang.reflect.Method; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeMap; + +import static java.lang.reflect.Modifier.*; + +/** + * Convenient class to deal with getters from mapped classes. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public final class ReflectionUtils { + /** + * Returns a {@link Map} of getter methods of the given class. + *

+ * The key of the map contains the name of the attribute that can be accessed by the getter, the value being the + * getter itself (an instance of Method). A method is considered a getter if its name starts with 'get' or 'is'. + * It is declared public and takes no arguments. + * + * @param clazz the class to return the getters for + * @param comparator the comparator for sorting properties + * @return a Map of attributes and their accessor methods (getters) + * @see #getGetters(Class) + */ + public static Map getGetters(Class clazz, Comparator comparator) { + Map accessors = comparator != null ? new TreeMap<>(comparator) : new LinkedHashMap<>(); + + Method[] methods = clazz.getMethods(); + for (Method method : methods) { + if (method.getParameterCount() > 0 || + method.getReturnType() == Void.TYPE || + !isPublic(method.getModifiers()) || + isNative(method.getModifiers()) || + isAbstract(method.getModifiers()) || + isStatic(method.getModifiers()) || + method.getName().equals("toString") + ) { + continue; + } + + String name = null; + String methodName = method.getName(); + + if (methodName.startsWith("get")) { + name = methodName.substring(3); + } else if (methodName.startsWith("is")) { + name = methodName.substring(2); + } + + if (name == null || name.isEmpty()) { + continue; + } + + accessors.put(name, method); + } + + return accessors; + } + + /** + * Returns a Map of getter methods of the given class. + * + * @param clazz the class to return the getters for + * @return a Map of attributes and their accessor methods (getters) + * @see #getGetters(Class, Comparator) + */ + public static Map getGetters(Class clazz) { + return getGetters(clazz, null); + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriter.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriter.java new file mode 100644 index 000000000..eb33c1dd7 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriter.java @@ -0,0 +1,66 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.writer; + +import org.asteriskjava.core.databind.AsteriskGenerator; +import org.asteriskjava.core.databind.serializer.AsteriskSerializer; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Writer class to write field names and serialized values using the {@link AsteriskGenerator}. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class AsteriskObjectMethodWriter { + private final Method method; + private final String name; + private final AsteriskSerializer asteriskSerializer; + private final AsteriskObjectMethodWriterContext asteriskObjectMethodWriterContext; + + public AsteriskObjectMethodWriter( + Method method, + String name, + AsteriskSerializer asteriskSerializer, + AsteriskObjectMethodWriterContext asteriskObjectMethodWriterContext + ) { + this.method = method; + this.name = name; + this.asteriskSerializer = asteriskSerializer; + this.asteriskObjectMethodWriterContext = asteriskObjectMethodWriterContext; + } + + public void writeName(AsteriskGenerator asteriskGenerator) { + if (!asteriskObjectMethodWriterContext.serializerWriteFieldName()) { + asteriskGenerator.writeFieldName(name); + } + } + + public void writeValue(Object obj, AsteriskGenerator asteriskGenerator) { + Object currentValue = getCurrentValue(obj); + asteriskSerializer.serialize(name, currentValue, asteriskGenerator); + } + + private Object getCurrentValue(Object obj) { + try { + return method.invoke(obj); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriterContext.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriterContext.java new file mode 100644 index 000000000..26b03ce59 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriterContext.java @@ -0,0 +1,26 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.writer; + +/** + * Context for the process methods. + * + * @param serializerWriteFieldName whatever serializer write field names + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public record AsteriskObjectMethodWriterContext(boolean serializerWriteFieldName) { +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectWriter.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectWriter.java new file mode 100644 index 000000000..91d6ad710 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectWriter.java @@ -0,0 +1,84 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.writer; + +import org.asteriskjava.core.databind.annotation.AsteriskName; +import org.asteriskjava.core.databind.annotation.AsteriskSerialize; +import org.asteriskjava.core.databind.serializer.AsteriskSerializer; +import org.asteriskjava.core.databind.serializer.WritableFileName; +import org.asteriskjava.core.databind.serializer.std.ToStringSerializer; + +import java.lang.reflect.Method; +import java.util.Comparator; +import java.util.List; +import java.util.Map.Entry; + +import static org.asteriskjava.core.databind.utils.ReflectionUtils.getGetters; + +/** + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class AsteriskObjectWriter { + private final Class clazz; + private final Comparator fieldNamesComparator; + + public AsteriskObjectWriter(Class clazz, Comparator fieldNamesComparator) { + this.clazz = clazz; + this.fieldNamesComparator = fieldNamesComparator; + } + + public List getAsteriskObjectMethodWriters() { + return getGetters(clazz, fieldNamesComparator) + .entrySet() + .stream() + .map(this::getAsteriskObjectMethodWriter) + .toList(); + } + + private AsteriskObjectMethodWriter getAsteriskObjectMethodWriter(Entry entry) { + Method method = entry.getValue(); + + String name = getName(method, entry.getKey()); + + AsteriskSerializer asteriskSerializer = getAsteriskSerializer(method); + + boolean serializerWriteFieldName = asteriskSerializer instanceof WritableFileName; + AsteriskObjectMethodWriterContext context = new AsteriskObjectMethodWriterContext(serializerWriteFieldName); + + return new AsteriskObjectMethodWriter(method, name, asteriskSerializer, context); + } + + private static String getName(Method method, String name) { + AsteriskName asteriskName = method.getAnnotation(AsteriskName.class); + return asteriskName == null ? name : asteriskName.value(); + } + + private AsteriskSerializer getAsteriskSerializer(Method method) { + AsteriskSerialize asteriskSerialize = method.getAnnotation(AsteriskSerialize.class); + if (asteriskSerialize == null) { + return new ToStringSerializer(); + } + + Class> asteriskSerializerClass = asteriskSerialize.value(); + try { + //noinspection unchecked + return (AsteriskSerializer) asteriskSerializerClass.getDeclaredConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException("Cannot create new instance of serializer %s".formatted(asteriskSerialize), e); + } + } +} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskObjectMapperTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskObjectMapperTest.java new file mode 100644 index 000000000..de7a0f54a --- /dev/null +++ b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskObjectMapperTest.java @@ -0,0 +1,80 @@ +package org.asteriskjava.core.databind; + +import org.asteriskjava.core.databind.annotation.AsteriskName; +import org.asteriskjava.core.databind.annotation.AsteriskSerialize; +import org.asteriskjava.core.databind.serializer.custom.ComaJoiningSerializer; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.asteriskjava.core.NewlineDelimiter.CRLF; +import static org.asteriskjava.core.databind.AsteriskObjectMapper.builder; +import static org.asteriskjava.core.databind.AsteriskObjectMapperTest.SimpleBean.AuthType.MD5; + +class AsteriskObjectMapperTest { + private final AsteriskObjectMapper asteriskObjectMapper = builder() + .crlfNewlineDelimiter() + .build(); + + @Test + void shouldGenerateSimpleBean() { + //given + SimpleBean bean = new SimpleBean(); + bean.setActionId("id-1"); + bean.setAuthType(MD5); + bean.setCodecs(List.of("codec1", "codec2")); + + //when + String string = asteriskObjectMapper.writeValue(bean); + + //then + String expected = "Action: SimpleBean" + CRLF.getPattern(); + expected += "ActionID: id-1" + CRLF.getPattern(); + expected += "AuthType: MD5" + CRLF.getPattern(); + expected += "Codecs: codec1,codec2" + CRLF.getPattern(); + assertThat(string).isEqualTo(expected); + } + + public static class SimpleBean { + public enum AuthType { + MD5, + } + + private String actionId; + + private AuthType authType; + + private List codecs; + + public String getAction() { + return "SimpleBean"; + } + + @AsteriskName("ActionID") + public String getActionId() { + return actionId; + } + + public void setActionId(String actionId) { + this.actionId = actionId; + } + + public AuthType getAuthType() { + return authType; + } + + public void setAuthType(AuthType authType) { + this.authType = authType; + } + + @AsteriskSerialize(ComaJoiningSerializer.class) + public List getCodecs() { + return codecs; + } + + public void setCodecs(List codecs) { + this.codecs = codecs; + } + } +} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializerTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializerTest.java new file mode 100644 index 000000000..b5411890f --- /dev/null +++ b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializerTest.java @@ -0,0 +1,63 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.serializer.custom; + +import org.asteriskjava.core.databind.AsteriskGenerator; +import org.junit.jupiter.api.Test; + +import java.util.EnumSet; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.asteriskjava.core.NewlineDelimiter.CRLF; + +class ComaJoiningSerializerTest { + private final AsteriskGenerator asteriskGenerator = new AsteriskGenerator(CRLF); + + @Test + void shouldSerializeList() { + //given + ComaJoiningSerializer comaJoiningSerializer = new ComaJoiningSerializer(); + + List value = List.of("string1", "string2", "string3"); + + //when + comaJoiningSerializer.serialize("fieldName", value, asteriskGenerator); + + //then + assertThat(asteriskGenerator.generate().trim()).isEqualTo("string1,string2,string3"); + } + + @Test + void shouldSerializeListOfEnums() { + //given + ComaJoiningSerializer comaJoiningSerializer = new ComaJoiningSerializer(); + + EnumSet enums = EnumSet.of(SampleEnum.value1, SampleEnum.value2, SampleEnum.value3); + + //when + comaJoiningSerializer.serialize("fieldName", enums, asteriskGenerator); + + //then + assertThat(asteriskGenerator.generate().trim()).isEqualTo("value1,value2,value3"); + } + + private enum SampleEnum { + value1, + value2, + value3, + } +} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializerTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializerTest.java new file mode 100644 index 000000000..02c69f9d6 --- /dev/null +++ b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializerTest.java @@ -0,0 +1,51 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.serializer.custom; + +import org.asteriskjava.core.databind.AsteriskGenerator; +import org.junit.jupiter.api.Test; + +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.asteriskjava.core.NewlineDelimiter.CRLF; + +class VariableSerializerTest { + private final AsteriskGenerator asteriskGenerator = new AsteriskGenerator(CRLF); + + @Test + void shouldSerializeValues() { + //given + VariableSerializer variableSerializer = new VariableSerializer(); + + Map map = Map.of( + "key1", "value1", + "key2", "value2", + "key3", "value3" + ); + + //when + variableSerializer.serialize("fieldName", map, asteriskGenerator); + + //then + assertThat(asteriskGenerator.generate()) + .contains( + "fieldName: key1=value1", + "fieldName: key2=value2", + "fieldName: key3=value3" + ); + } +} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializerTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializerTest.java new file mode 100644 index 000000000..0378e957f --- /dev/null +++ b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializerTest.java @@ -0,0 +1,52 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.serializer.std; + +import org.asteriskjava.core.databind.AsteriskGenerator; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.asteriskjava.core.NewlineDelimiter.CRLF; +import static org.junit.jupiter.params.provider.Arguments.arguments; + +class ToStringSerializerTest { + private final AsteriskGenerator asteriskGenerator = new AsteriskGenerator(CRLF); + + @ParameterizedTest + @MethodSource("toStringSerializerArguments") + void shouldSerializeUsingToString(Object actual, String expected) { + //given + ToStringSerializer toStringSerializer = new ToStringSerializer(); + + //when + toStringSerializer.serialize("fieldName", actual, asteriskGenerator); + + //then + assertThat(asteriskGenerator.generate().trim()).isEqualTo(expected); + } + + private static Stream toStringSerializerArguments() { + return Stream.of( + arguments("string", "string"), + arguments(true, "true"), + arguments(1.12, "1.12") + ); + } +} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/utils/ReflectionUtilsTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/utils/ReflectionUtilsTest.java new file mode 100644 index 000000000..29f9dccbb --- /dev/null +++ b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/utils/ReflectionUtilsTest.java @@ -0,0 +1,187 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.utils; + +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Method; +import java.util.Comparator; +import java.util.Map; +import java.util.StringJoiner; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.asteriskjava.core.databind.utils.ReflectionUtils.getGetters; + +class ReflectionUtilsTest { + @Test + void shouldDoesNotReturnGetterWhenHasAnyParameters() { + //when + Map getters = getGetters(InvalidClass.class); + + //then + assertThat(getters).doesNotContainKey("WithParameters"); + } + + @Test + void shouldDoesNotReturnGetterWhenHasVoidType() { + //when + Map getters = getGetters(InvalidClass.class); + + //then + assertThat(getters).doesNotContainKey("WithVoidType"); + } + + @Test + void shouldDoesNotReturnGetterWhenHasNotPublicScope() { + //when + Map getters = getGetters(InvalidClass.class); + + //then + assertThat(getters).doesNotContainKey("WhichIsHasNotPublicScope"); + } + + @Test + void shouldDoesNotReturnGetterWhenIsNative() { + //when + Map getters = getGetters(InvalidClass.class); + + //then + assertThat(getters).doesNotContainKey("WithNative"); + } + + @Test + void shouldDoesNotReturnGetterWhenIsAbstract() { + //when + Map getters = getGetters(InvalidClass.class); + + //then + assertThat(getters).doesNotContainKey("WithAbstract"); + } + + @Test + void shouldDoesNotReturnGetterWhenIsStatic() { + //when + Map getters = getGetters(InvalidClass.class); + + //then + assertThat(getters).doesNotContainKey("WithStatic"); + } + + @Test + void shouldDoesNotReturnGetterWhenIsToString() { + //when + Map getters = getGetters(InvalidClass.class); + + //then + assertThat(getters).doesNotContainKey("toString"); + } + + @Test + void shouldDoesNotReturnGetterWhenNameHasOnlyGet() { + //when + Map getters = getGetters(InvalidClass.class); + + //then + assertThat(getters).isEmpty(); + } + + @Test + void shouldDoesNotReturnGetterWhenNameHasOnlyIs() { + //when + Map getters = getGetters(InvalidClass.class); + + //then + assertThat(getters).isEmpty(); + } + + static abstract class InvalidClass { + public String getWithParameters(String parameter) { + return parameter; + } + + public void getWithVoidType() { + } + + String getWhichIsHasNotPublicScope() { + return ""; + } + + public native String getWithNative(); + + public abstract String getWithAbstract(); + + public static String getWithStatic() { + return ""; + } + + public String get() { + return ""; + } + + public String is() { + return ""; + } + + @Override + public String toString() { + return new StringJoiner(", ", InvalidClass.class.getSimpleName() + "[", "]") + .toString(); + } + } + + @Test + void shouldReturnValidGetters() { + //when + Map getters = getGetters(ValidClass.class); + + //then + assertThat(getters).containsOnlyKeys("Value", "Valid", "Action"); + } + + @Test + void shouldReturnValidGettersSorted() { + //when + Map getters = getGetters(ValidClass.class, new ActionFieldsComparator()); + + //then + assertThat(getters.keySet()).containsExactly("Action", "Value", "Valid"); + } + + static class ValidClass { + + public String getValue() { + return "value"; + } + + public boolean isValid() { + return true; + } + + public String getAction() { + return "NewAction"; + } + } + + static class ActionFieldsComparator implements Comparator { + @Override + public int compare(String o1, String o2) { + if (o1.equals(o2)) { + return 0; + } + return o1.equalsIgnoreCase("Action") ? -1 : 1; + } + } +} diff --git a/build.gradle b/build.gradle index 73d0c1687..f7feed549 100644 --- a/build.gradle +++ b/build.gradle @@ -27,14 +27,14 @@ repositories { dependencies { implementation 'com.google.guava:guava:32.1.3-jre' - implementation 'org.apache.logging.log4j:log4j-core:2.21.1' + implementation 'org.apache.logging.log4j:log4j-core:2.22.0' implementation 'org.reflections:reflections:0.10.2' implementation 'org.slf4j:slf4j-api:2.0.9' testImplementation 'org.assertj:assertj-core:3.24.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' - testImplementation 'org.mockito:mockito-core:4.11.0' - testImplementation 'ch.qos.logback:logback-classic:1.3.11' + testImplementation 'org.mockito:mockito-core:5.7.0' + testImplementation 'ch.qos.logback:logback-classic:1.4.11' } tasks.named('test') { diff --git a/settings.gradle b/settings.gradle index 553c1b13b..ccfa47cbf 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,3 @@ rootProject.name = 'asterisk-java' + +include 'asterisk-java-core' From 180241d487465bf5e7ad1cf99af6621adee7ac0a Mon Sep 17 00:00:00 2001 From: Piotr Olaszewski Date: Fri, 8 Dec 2023 21:53:15 +0100 Subject: [PATCH 3/3] Introduce AMI module (#570) * Move ManagerAction and AbstractManagerAction to AMI module * Move ExpectedResponse and ManagerResponse * Move ChallengeAction and ChallengeResponse * Fix falling tests * Add response type enum and start working with framework for action testing * Add databinder * Fix falling tests * Add simple databinder * Handle decoder from string * Rename ManagerResponse to ManagerActionResponse --- .editorconfig | 17 +- asterisk-java-ami/build.gradle | 40 ++ .../ami/ActionFieldsComparator.java | 40 ++ .../ami/action/AbstractManagerAction.java | 53 +++ .../org/asteriskjava/ami/action/AuthType.java | 9 +- .../ami/action/ChallengeAction.java | 63 +++ .../ami/action/ManagerAction.java | 54 +++ .../action/annotation/ExpectedResponse.java | 22 +- .../ChallengeManagerActionResponse.java | 83 ++++ .../response/ManagerActionResponse.java | 203 +++++---- .../ami/action/response/ResponseType.java | 31 ++ .../ami/action/ChallengeActionItTest.java | 141 ++++++ .../src/test/resources/manager.conf | 154 +++++++ asterisk-java-core/build.gradle | 4 + .../core/databind/AsteriskDecoder.java | 219 +++++++++ .../core/databind/AsteriskEncoder.java | 152 +++++++ .../core/databind/AsteriskGenerator.java | 48 -- .../core/databind/AsteriskObjectMapper.java | 93 ---- ...ritableFileName.java => CodersConsts.java} | 11 +- .../core/databind/TypeConversionRegister.java | 84 ++++ .../serializer/AsteriskSerializer.java | 36 -- .../custom/ComaJoiningSerializer.java | 43 -- .../serializer/custom/VariableSerializer.java | 54 --- .../AnnotationUtils.java} | 20 +- .../core/databind/utils/Invoker.java | 66 +++ .../core/databind/utils/ReflectionUtils.java | 76 +++- .../writer/AsteriskObjectMethodWriter.java | 66 --- .../databind/writer/AsteriskObjectWriter.java | 84 ---- .../core/databind/AsteriskDecoderTest.java | 419 ++++++++++++++++++ .../core/databind/AsteriskEncoderTest.java | 158 +++++++ .../databind/AsteriskObjectMapperTest.java | 80 ---- .../custom/ComaJoiningSerializerTest.java | 63 --- .../custom/VariableSerializerTest.java | 51 --- .../std/ToStringSerializerTest.java | 52 --- .../databind/utils/ReflectionUtilsTest.java | 22 +- build.gradle | 3 + settings.gradle | 1 + .../fastagi/internal/AsyncAgiWriter.java | 4 +- .../live/internal/AsteriskChannelImpl.java | 30 +- .../internal/AsteriskQueueMemberImpl.java | 6 +- .../live/internal/AsteriskServerImpl.java | 22 +- .../ManagerCommunicationExceptionMapper.java | 3 +- .../live/internal/MeetMeManager.java | 4 +- .../manager/DefaultManagerConnection.java | 8 +- .../manager/ExpectedResponse.java | 22 - .../manager/ManagerConnection.java | 29 +- .../manager/ManagerConnectionFactory.java | 4 +- .../org/asteriskjava/manager/PingThread.java | 4 +- .../asteriskjava/manager/ResponseEvents.java | 6 +- .../manager/SendActionCallback.java | 8 +- .../SendEventGeneratingActionCallback.java | 6 +- .../manager/action/AbsoluteTimeoutAction.java | 2 + .../manager/action/AbstractManagerAction.java | 78 ---- .../action/AbstractMeetMeMuteAction.java | 2 + .../action/AgentCallbackLoginAction.java | 2 + .../manager/action/AgentLogoffAction.java | 2 + .../manager/action/AgentsAction.java | 1 + .../manager/action/AgiAction.java | 1 + .../manager/action/AtxferAction.java | 2 + .../manager/action/BridgeAction.java | 2 + .../manager/action/ChallengeAction.java | 76 ---- .../manager/action/ChangeMonitorAction.java | 2 + .../manager/action/CommandAction.java | 3 +- .../manager/action/ConfbridgeKickAction.java | 2 + .../manager/action/ConfbridgeListAction.java | 1 + .../action/ConfbridgeListRoomsAction.java | 1 + .../manager/action/ConfbridgeLockAction.java | 2 + .../manager/action/ConfbridgeMuteAction.java | 2 + .../ConfbridgeSetSingleVideoSrcAction.java | 2 + .../action/ConfbridgeStartRecordAction.java | 2 + .../action/ConfbridgeStopRecordAction.java | 2 + .../action/ConfbridgeUnlockAction.java | 2 + .../action/ConfbridgeUnmuteAction.java | 2 + .../manager/action/CoreSettingsAction.java | 3 +- .../action/CoreShowChannelsAction.java | 1 + .../manager/action/CoreStatusAction.java | 3 +- .../action/DahdiShowChannelsAction.java | 1 + .../manager/action/DbDelAction.java | 2 + .../manager/action/DbDelTreeAction.java | 2 + .../manager/action/DbGetAction.java | 1 + .../manager/action/DbPutAction.java | 2 + .../manager/action/DongleSendSMSAction.java | 2 + .../action/DongleShowDevicesAction.java | 1 + .../manager/action/EventGeneratingAction.java | 1 + .../manager/action/EventsAction.java | 2 + .../manager/action/ExecAction.java | 3 +- .../manager/action/ExtensionStateAction.java | 3 +- .../manager/action/FilterAction.java | 2 + .../manager/action/GetConfigAction.java | 3 +- .../manager/action/GetVarAction.java | 8 +- .../manager/action/HangupAction.java | 2 + .../manager/action/IaxPeerListAction.java | 1 + .../manager/action/JabberSendAction.java | 2 + .../manager/action/ListCommandsAction.java | 5 +- .../action/LocalOptimizeAwayAction.java | 2 + .../manager/action/LoginAction.java | 5 +- .../manager/action/LogoffAction.java | 2 + .../manager/action/MWIDeleteAction.java | 2 + .../manager/action/MWIUpdateAction.java | 2 + .../manager/action/MailboxCountAction.java | 3 +- .../manager/action/MailboxStatusAction.java | 3 +- .../manager/action/ManagerAction.java | 67 --- .../manager/action/MessageSendAction.java | 2 + .../manager/action/MixMonitorAction.java | 3 +- .../manager/action/MixMonitorMuteAction.java | 2 + .../manager/action/ModuleCheckAction.java | 3 +- .../manager/action/ModuleLoadAction.java | 2 + .../manager/action/MonitorAction.java | 2 + .../manager/action/MuteAudioAction.java | 2 + .../manager/action/OriginateAction.java | 1 + .../manager/action/PJSIPNotifyAction.java | 3 + .../action/PJSipShowContactsAction.java | 1 + .../action/PJSipShowEndpointAction.java | 1 + .../action/PJSipShowEndpointsAction.java | 1 + .../manager/action/ParkAction.java | 2 + .../manager/action/ParkedCallsAction.java | 1 + .../manager/action/PauseMixMonitorAction.java | 2 + .../manager/action/PauseMonitorAction.java | 2 + .../manager/action/PingAction.java | 3 +- .../manager/action/PlayDtmfAction.java | 2 + .../manager/action/QueueAddAction.java | 2 + .../QueueChangePriorityCallerAction.java | 2 + .../manager/action/QueueLogAction.java | 2 + .../action/QueueMemberRingInUseAction.java | 2 + .../manager/action/QueuePauseAction.java | 2 + .../manager/action/QueuePenaltyAction.java | 2 + .../manager/action/QueueRemoveAction.java | 2 + .../manager/action/QueueResetAction.java | 2 + .../manager/action/QueueStatusAction.java | 1 + .../manager/action/QueueSummaryAction.java | 1 + .../manager/action/RedirectAction.java | 2 + .../manager/action/SendTextAction.java | 2 + .../manager/action/SetCdrUserFieldAction.java | 2 + .../manager/action/SetVarAction.java | 2 + .../manager/action/ShowDialplanAction.java | 1 + .../manager/action/SipNotifyAction.java | 2 + .../manager/action/SipPeersAction.java | 1 + .../manager/action/SipShowPeerAction.java | 6 +- .../manager/action/SipShowRegistryAction.java | 1 + .../action/SkypeAccountPropertyAction.java | 2 + .../manager/action/SkypeAddBuddyAction.java | 2 + .../manager/action/SkypeBuddiesAction.java | 1 + .../manager/action/SkypeBuddyAction.java | 3 +- .../manager/action/SkypeChatSendAction.java | 2 + .../action/SkypeLicenseListAction.java | 1 + .../action/SkypeLicenseStatusAction.java | 3 +- .../action/SkypeRemoveBuddyAction.java | 2 + .../manager/action/StatusAction.java | 1 + .../manager/action/StopMixMonitorAction.java | 2 + .../manager/action/StopMonitorAction.java | 2 + .../manager/action/UnpauseMonitorAction.java | 2 + .../manager/action/UpdateConfigAction.java | 2 + .../manager/action/UserEventAction.java | 1 + .../action/VoicemailUsersListAction.java | 1 + .../manager/action/ZapDialOffhookAction.java | 2 + .../manager/action/ZapDndOffAction.java | 2 + .../manager/action/ZapDndOnAction.java | 2 + .../manager/action/ZapHangupAction.java | 2 + .../manager/action/ZapRestartAction.java | 2 + .../manager/action/ZapShowChannelsAction.java | 1 + .../manager/action/ZapTransferAction.java | 2 + .../manager/event/ResponseEvent.java | 6 +- .../manager/internal/ActionBuilder.java | 2 +- .../manager/internal/ActionBuilderImpl.java | 38 +- .../manager/internal/AsyncEventPump.java | 8 +- .../manager/internal/Dispatcher.java | 6 +- .../internal/ManagerConnectionImpl.java | 51 ++- .../manager/internal/ManagerReader.java | 4 +- .../manager/internal/ManagerReaderImpl.java | 18 +- .../manager/internal/ManagerWriter.java | 2 +- .../manager/internal/ManagerWriterImpl.java | 2 +- .../manager/internal/ResponseBuilder.java | 6 +- .../manager/internal/ResponseBuilderImpl.java | 25 +- .../manager/internal/ResponseEventsImpl.java | 8 +- .../manager/response/ChallengeResponse.java | 46 -- .../manager/response/CommandResponse.java | 4 +- .../response/CoreSettingsResponse.java | 4 +- .../manager/response/CoreStatusResponse.java | 3 +- .../response/ExtensionStateResponse.java | 4 +- .../manager/response/GetConfigResponse.java | 4 +- .../manager/response/GetVarResponse.java | 4 +- .../response/MailboxCountResponse.java | 4 +- .../response/MailboxStatusResponse.java | 4 +- .../manager/response/ManagerError.java | 4 +- .../manager/response/MixMonitorResponse.java | 4 +- .../manager/response/ModuleCheckResponse.java | 4 +- .../manager/response/PingResponse.java | 4 +- .../manager/response/SipShowPeerResponse.java | 4 +- .../manager/response/SkypeBuddyResponse.java | 4 +- .../response/SkypeLicenseStatusResponse.java | 4 +- .../manager/util/EventAttributesHelper.java | 10 +- .../asterisk/wrap/actions/BridgeAction.java | 3 +- .../asterisk/wrap/actions/CommandAction.java | 4 +- .../wrap/actions/ConfbridgeKickAction.java | 4 +- .../wrap/actions/ConfbridgeListAction.java | 2 +- .../asterisk/wrap/actions/DbGetAction.java | 2 +- .../asterisk/wrap/actions/GetVarAction.java | 3 +- .../asterisk/wrap/actions/HangupAction.java | 3 +- .../wrap/actions/ListCommandsAction.java | 4 +- .../asterisk/wrap/actions/ManagerAction.java | 2 +- .../asterisk/wrap/actions/MonitorAction.java | 3 +- .../wrap/actions/OriginateAction.java | 3 +- .../pbx/asterisk/wrap/actions/PingAction.java | 4 +- .../asterisk/wrap/actions/PlayDtmfAction.java | 2 +- .../asterisk/wrap/actions/RedirectAction.java | 3 +- .../asterisk/wrap/actions/SetVarAction.java | 3 +- .../asterisk/wrap/actions/SipPeersAction.java | 4 +- .../wrap/actions/SipShowPeerAction.java | 2 +- .../asterisk/wrap/actions/StatusAction.java | 3 +- .../wrap/response/CommandResponse.java | 4 +- .../asterisk/wrap/response/ManagerError.java | 4 +- .../wrap/response/ManagerResponse.java | 7 +- .../internal/core/CoherentEventFactory.java | 7 +- .../core/CoherentManagerConnection.java | 5 +- .../internal/ActionBuilderImplTest.java | 7 +- .../manager/internal/AnnotatedAction.java | 2 +- .../internal/ManagerConnectionImplTest.java | 25 +- .../internal/ManagerReaderImplTest.java | 19 +- .../manager/internal/ManagerReaderMock.java | 4 +- .../manager/internal/ManagerWriterMock.java | 46 +- .../internal/ResponseBuilderImplTest.java | 46 +- 221 files changed, 2529 insertions(+), 1402 deletions(-) create mode 100644 asterisk-java-ami/build.gradle create mode 100644 asterisk-java-ami/src/main/java/org/asteriskjava/ami/ActionFieldsComparator.java create mode 100644 asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/AbstractManagerAction.java rename asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriterContext.java => asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/AuthType.java (73%) create mode 100644 asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/ChallengeAction.java create mode 100644 asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/ManagerAction.java rename asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskSerialize.java => asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/annotation/ExpectedResponse.java (57%) create mode 100644 asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ChallengeManagerActionResponse.java rename src/main/java/org/asteriskjava/manager/response/ManagerResponse.java => asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ManagerActionResponse.java (66%) create mode 100644 asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ResponseType.java create mode 100644 asterisk-java-ami/src/test/java/org/asteriskjava/ami/action/ChallengeActionItTest.java create mode 100644 asterisk-java-ami/src/test/resources/manager.conf create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskDecoder.java create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskEncoder.java delete mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskGenerator.java delete mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskObjectMapper.java rename asterisk-java-core/src/main/java/org/asteriskjava/core/databind/{serializer/WritableFileName.java => CodersConsts.java} (71%) create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/TypeConversionRegister.java delete mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/AsteriskSerializer.java delete mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializer.java delete mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializer.java rename asterisk-java-core/src/main/java/org/asteriskjava/core/databind/{serializer/std/ToStringSerializer.java => utils/AnnotationUtils.java} (57%) create mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/Invoker.java delete mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriter.java delete mode 100644 asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectWriter.java create mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskDecoderTest.java create mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskEncoderTest.java delete mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskObjectMapperTest.java delete mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializerTest.java delete mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializerTest.java delete mode 100644 asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializerTest.java delete mode 100644 src/main/java/org/asteriskjava/manager/ExpectedResponse.java delete mode 100644 src/main/java/org/asteriskjava/manager/action/AbstractManagerAction.java delete mode 100644 src/main/java/org/asteriskjava/manager/action/ChallengeAction.java delete mode 100644 src/main/java/org/asteriskjava/manager/action/ManagerAction.java delete mode 100644 src/main/java/org/asteriskjava/manager/response/ChallengeResponse.java diff --git a/.editorconfig b/.editorconfig index 1dfca740e..03b9d4922 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,8 +1,17 @@ -[*.{java,html}] +root = true + +[*] charset = utf-8 -indent_style = space +end_of_line = lf indent_size = 4 -trim_trailing_whitespace = true +indent_style = space +insert_final_newline = true +max_line_length = 120 +tab_width = 4 +ij_continuation_indent_size = 8 -[*.{yml,yaml}] +[*.{yaml,yml}] indent_size = 2 + +[*.properties] +ij_properties_align_group_field_declarations = false diff --git a/asterisk-java-ami/build.gradle b/asterisk-java-ami/build.gradle new file mode 100644 index 000000000..48ee6c2a6 --- /dev/null +++ b/asterisk-java-ami/build.gradle @@ -0,0 +1,40 @@ +plugins { + id 'java-library' +} + +java { + sourceCompatibility = '17' + targetCompatibility = '17' + + withSourcesJar() + withJavadocJar() +} + +repositories { + mavenCentral() +} + +dependencies { + implementation project(':asterisk-java-core') + + implementation 'org.apache.commons:commons-lang3:3.14.0' + + testImplementation 'org.assertj:assertj-core:3.24.2' + testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' + testImplementation 'org.mockito:mockito-core:5.7.0' + testImplementation 'ch.qos.logback:logback-classic:1.4.11' //todo remove + + testImplementation platform('org.testcontainers:testcontainers-bom:1.19.3') + testImplementation 'org.testcontainers:junit-jupiter' + testImplementation 'org.testcontainers:testcontainers' + + //todo only for testing framework + testImplementation 'io.netty:netty-codec:4.1.101.Final' + testImplementation 'io.netty:netty-handler:4.1.101.Final' + testImplementation 'io.netty:netty-transport:4.1.101.Final' + testImplementation 'org.awaitility:awaitility:4.2.0' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/asterisk-java-ami/src/main/java/org/asteriskjava/ami/ActionFieldsComparator.java b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/ActionFieldsComparator.java new file mode 100644 index 000000000..8c174b982 --- /dev/null +++ b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/ActionFieldsComparator.java @@ -0,0 +1,40 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.ami; + +import java.util.Comparator; + +/** + * Convenient class to sort Action and ActionID fields in action object. Those fields are first and second retrospectively. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class ActionFieldsComparator implements Comparator { + @Override + public int compare(String o1, String o2) { + if (o1.equals(o2)) { + return 0; + } + if (o1.equalsIgnoreCase("Action") && o2.equalsIgnoreCase("ActionID") || (o1.equalsIgnoreCase("ActionID") && o2.equalsIgnoreCase("Action"))) { + return 1; + } + if (o1.equalsIgnoreCase("Action") || o1.equalsIgnoreCase("ActionID")) { + return -1; + } + return 1; + } +} diff --git a/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/AbstractManagerAction.java b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/AbstractManagerAction.java new file mode 100644 index 000000000..f9145e018 --- /dev/null +++ b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/AbstractManagerAction.java @@ -0,0 +1,53 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.ami.action; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.asteriskjava.core.databind.annotation.AsteriskName; + +import java.io.Serial; + +/** + * This class implements the {@link ManagerAction} interface and can serve as the base class for your concrete action + * implementations. + * + * @author Stefan Reuter + * @author Piotr Olaszewski + * @since 1.0.0 + */ +public abstract class AbstractManagerAction implements ManagerAction { + @Serial + private static final long serialVersionUID = -7667827187378395689L; + + private String actionId; + + @AsteriskName("ActionID") + public String getActionId() { + return actionId; + } + + public void setActionId(String actionId) { + this.actionId = actionId; + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("action", getAction()) + .append("actionId", actionId) + .toString(); + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriterContext.java b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/AuthType.java similarity index 73% rename from asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriterContext.java rename to asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/AuthType.java index 26b03ce59..2cf03147e 100644 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriterContext.java +++ b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/AuthType.java @@ -13,14 +13,15 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.asteriskjava.core.databind.writer; +package org.asteriskjava.ami.action; /** - * Context for the process methods. + * Digest algorithms to use in the challenge. * - * @param serializerWriteFieldName whatever serializer write field names * @author Piotr Olaszewski + * @see ChallengeAction * @since 4.0.0 */ -public record AsteriskObjectMethodWriterContext(boolean serializerWriteFieldName) { +public enum AuthType { + MD5, } diff --git a/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/ChallengeAction.java b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/ChallengeAction.java new file mode 100644 index 000000000..3c79b3e6f --- /dev/null +++ b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/ChallengeAction.java @@ -0,0 +1,63 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.ami.action; + +import org.asteriskjava.ami.action.annotation.ExpectedResponse; +import org.asteriskjava.ami.action.response.ChallengeManagerActionResponse; + +import java.io.Serial; + +/** + * The {@link ChallengeAction} requests a challenge from the server to use when logging in using challenge/response. + * Sending this action to the Asterisk server results in a {@link ChallengeManagerActionResponse} being received from the server. + *

+ * Supported Asterisk versions: + *

+ * + * @author Stefan Reuter + * @author Piotr Olaszewski + * @see ChallengeManagerActionResponse + * @since 1.0.0 + */ +@ExpectedResponse(ChallengeManagerActionResponse.class) +public class ChallengeAction extends AbstractManagerAction { + @Serial + private static final long serialVersionUID = 7240516124871953971L; + + private AuthType authType; + + @Override + public String getAction() { + return "Challenge"; + } + + /** + * Asterisk argument: {@code AuthType}. + */ + public AuthType getAuthType() { + return authType; + } + + /** + * Sets Asterisk argument: {@code AuthType}. + */ + public void setAuthType(AuthType authType) { + this.authType = authType; + } +} diff --git a/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/ManagerAction.java b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/ManagerAction.java new file mode 100644 index 000000000..ec06e4ba3 --- /dev/null +++ b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/ManagerAction.java @@ -0,0 +1,54 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.ami.action; + +import java.io.Serializable; + +/** + * Interface that all actions that can be sent to the Asterisk server must implement. + *

+ * Instances of this class represent a command sent to Asterisk via AMI, requesting a particular action be performed. + * The number of actions available to the client are determined by the modules presently loaded in the Asterisk engine. + *

+ * There is one concrete subclass of ManagerAction per each supported Asterisk action. + * + * @author Stefan Reuter + * @since 1.0.0 + */ +public interface ManagerAction extends Serializable { + /** + * Asterisk argument: {@code Action}. + */ + String getAction(); + + /** + * Asterisk argument: {@code ActionID}. + */ + String getActionId(); + + /** + * Sets the Asterisk argument: {@code ActionID}. + *

+ * If the {@code ActionID} is set and sent to Asterisk, any response returned by Asterisk will include the same ID. + * This way, the {@code ActionID} can be used to track actions and their corresponding responses and response events. + *

+ * Note that Asterisk Java uses its own internal {@code ActionID} to match actions with the corresponding responses + * and events. Although the internal action is never exposed to the application code, if you want to handle responses + * or response events on your own, your application must set a unique {@code ActionID} using this method. + * Otherwise, the {@code ActionID} of the responses and response event objects passed to your application will be null. + */ + void setActionId(String actionId); +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskSerialize.java b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/annotation/ExpectedResponse.java similarity index 57% rename from asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskSerialize.java rename to asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/annotation/ExpectedResponse.java index c63d7a6a9..c057ce62a 100644 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/annotation/AsteriskSerialize.java +++ b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/annotation/ExpectedResponse.java @@ -13,27 +13,29 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.asteriskjava.core.databind.annotation; +package org.asteriskjava.ami.action.annotation; -import org.asteriskjava.core.databind.serializer.AsteriskSerializer; +import org.asteriskjava.ami.action.ManagerAction; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import java.lang.annotation.Retention; import java.lang.annotation.Target; -import static java.lang.annotation.ElementType.METHOD; +import static java.lang.annotation.ElementType.TYPE; import static java.lang.annotation.RetentionPolicy.RUNTIME; /** - * Annotation used for configuring serialization aspects, by attaching to "getter" methods. + * Indicates that an annotated {@link ManagerAction} expects a specific subclass of {@link ManagerActionResponse} when + * executed successfully. * - * @author Piotr Olaszewski - * @since 4.0.0 + * @author Stefan Reuter + * @since 1.0.0 */ -@Target({METHOD}) +@Target(TYPE) @Retention(RUNTIME) -public @interface AsteriskSerialize { +public @interface ExpectedResponse { /** - * Serializer class to use for serializing associated value. + * {@link ManagerActionResponse} subclass, to handle {@link ManagerAction} response. */ - Class> value(); + Class value(); } diff --git a/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ChallengeManagerActionResponse.java b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ChallengeManagerActionResponse.java new file mode 100644 index 000000000..ffe50c2da --- /dev/null +++ b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ChallengeManagerActionResponse.java @@ -0,0 +1,83 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.ami.action.response; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.asteriskjava.ami.action.ChallengeAction; + +import java.io.Serial; + +/** + * Corresponds to a {@link ChallengeAction} and contains the challenge needed to log in using challenge/response. + * + * @author Stefan Reuter + * @author Piotr Olaszewski + * @see ChallengeAction + * @since 1.0.0 + */ +public class ChallengeManagerActionResponse extends ManagerActionResponse { + @Serial + private static final long serialVersionUID = -7253724086340850957L; + + private String challenge; + + /** + * Returns the challenge to use when creating the key for log in. + */ + public String getChallenge() { + return challenge; + } + + public void setChallenge(String challenge) { + this.challenge = challenge; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + ChallengeManagerActionResponse that = (ChallengeManagerActionResponse) o; + + return new EqualsBuilder() + .appendSuper(super.equals(that)) + .append(challenge, that.challenge) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .appendSuper(super.hashCode()) + .append(challenge) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .appendSuper(super.toString()) + .append("challenge", challenge) + .toString(); + } +} diff --git a/src/main/java/org/asteriskjava/manager/response/ManagerResponse.java b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ManagerActionResponse.java similarity index 66% rename from src/main/java/org/asteriskjava/manager/response/ManagerResponse.java rename to asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ManagerActionResponse.java index cb8e5c53c..073f8897c 100644 --- a/src/main/java/org/asteriskjava/manager/response/ManagerResponse.java +++ b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ManagerActionResponse.java @@ -1,50 +1,133 @@ /* - * Copyright 2004-2006 Stefan Reuter + * Copyright 2004-2023 Asterisk Java contributors * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * http://www.apache.org/licenses/LICENSE-2.0 * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. */ -package org.asteriskjava.manager.response; +package org.asteriskjava.ami.action.response; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.asteriskjava.ami.action.ManagerAction; +import org.asteriskjava.core.databind.annotation.AsteriskName; +import java.io.Serial; import java.io.Serializable; -import java.util.Date; +import java.time.Instant; import java.util.Locale; import java.util.Map; /** - * Represents a response received from the Asterisk server as the result of a - * previously sent ManagerAction. + * Represents a response received from the Asterisk server as the result of a previously sent {@link ManagerAction}. *

- * The response can be linked with the action that caused it by looking the - * action id attribute that will match the action id of the corresponding - * action. + * The response can be linked with the action that caused it by looking at the {@code ActionID} attribute, which will + * match the action id of the corresponding action. * - * @author srt - * @version $Id$ - * @see org.asteriskjava.manager.action.ManagerAction + * @author Stefan Reuter + * @see ManagerAction + * @since 1.0.0 */ -public class ManagerResponse implements Serializable { +public class ManagerActionResponse implements Serializable { + @Serial private static final long serialVersionUID = 1L; - private Date dateReceived; + private ResponseType response; + + private Instant dateReceived; + private String actionId; + /** + * Asterisk argument: {@code Response}. + */ + public ResponseType getResponse() { + return response; + } + + public void setResponse(ResponseType response) { + this.response = response; + } + + /** + * Returns the point in time when this response was received from the Asterisk server. + */ + public Instant getDateReceived() { + return dateReceived; + } + + public void setDateReceived(Instant dateReceived) { + this.dateReceived = dateReceived; + } + + /** + * Asterisk argument: {@code ActionID}. + *

+ * Returns the user provided action id of the {@link ManagerAction} that caused this response. If the application + * did not set an {@code ActionID} this method returns {@code null}. + */ + public String getActionId() { + return actionId; + } + + @AsteriskName("ActionID") + public void setActionId(String actionId) { + this.actionId = actionId; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + + if (o == null || getClass() != o.getClass()) { + return false; + } + + ManagerActionResponse that = (ManagerActionResponse) o; + + return new EqualsBuilder() + .append(response, that.response) + .append(dateReceived, that.dateReceived) + .append(actionId, that.actionId) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(response) + .append(dateReceived) + .append(actionId) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("response", response) + .append("dateReceived", dateReceived) + .append("actionId", actionId) + .toString(); + } + + /*---------------*/ + /** * The server from which this response has been received (only used with * AstManProxy). */ private String server; - private String response; private String eventList; private String message; private String uniqueId; @@ -52,7 +135,7 @@ public class ManagerResponse implements Serializable { private Map attributes; private String output; - public ManagerResponse() { + public ManagerActionResponse() { this.attributes = null; } @@ -108,35 +191,6 @@ public String getAttribute(String key) { return (String) attributes.get(key.toLowerCase(Locale.ENGLISH)); } - /** - * Returns the point in time this response was received from the asterisk - * server. - */ - public Date getDateReceived() { - return dateReceived; - } - - /** - * Sets the point in time this response was received from the asterisk - * server. - */ - public void setDateReceived(Date dateReceived) { - this.dateReceived = dateReceived; - } - - /** - * Returns the user provided action id of the ManagerAction that caused this - * response. If the application did not set an action id this method returns - * null. - * - * @return the action id of the ManagerAction that caused this response or - * null if none was set. - * @see org.asteriskjava.manager.action.ManagerAction#setActionId(String) - */ - public String getActionId() { - return actionId; - } - /** * Returns the name of the Asterisk server from which this response has been * received.
@@ -183,16 +237,6 @@ public void setEventList(String eventList) { this.eventList = eventList; } - /** - * Sets the action id of the ManagerAction that caused this response. - * - * @param actionId the action id of the ManagerAction that caused this - * response. - */ - public void setActionId(String actionId) { - this.actionId = actionId; - } - /** * Returns the message received with this response. The content depends on * the action that generated this response. @@ -208,22 +252,6 @@ public void setMessage(String message) { this.message = message; } - /** - * Returns the value of the "Response:" line. This typically a String like - * "Success" or "Error" but depends on the action that generated this - * response. - */ - public String getResponse() { - return response; - } - - /** - * Sets the response. - */ - public void setResponse(String response) { - this.response = response; - } - /** * Returns the unique id received with this response. The unique id is used * to keep track of channels created by the action sent, for example an @@ -264,21 +292,6 @@ protected Long stringToLong(String s, String suffix) throws NumberFormatExceptio return Long.parseLong(s.trim()); } - @Override - public String toString() { - StringBuilder sb; - - sb = new StringBuilder(100); - sb.append(getClass().getName()).append(": "); - sb.append("actionId='").append(getActionId()).append("'; "); - sb.append("message='").append(getMessage()).append("'; "); - sb.append("response='").append(getResponse()).append("'; "); - sb.append("uniqueId='").append(getUniqueId()).append("'; "); - sb.append("systemHashcode=").append(System.identityHashCode(this)); - - return sb.toString(); - } - public String getEvents() { return events; } diff --git a/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ResponseType.java b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ResponseType.java new file mode 100644 index 000000000..c89d3835a --- /dev/null +++ b/asterisk-java-ami/src/main/java/org/asteriskjava/ami/action/response/ResponseType.java @@ -0,0 +1,31 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.ami.action.response; + +/** + * Value of the "Response:" line. This is typically a string like {@code Success} or {@code Error} which is mapped to + * this enum. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public enum ResponseType { + Success, + Error, + Goodbye, + //todo check is sill in use + Follows, +} diff --git a/asterisk-java-ami/src/test/java/org/asteriskjava/ami/action/ChallengeActionItTest.java b/asterisk-java-ami/src/test/java/org/asteriskjava/ami/action/ChallengeActionItTest.java new file mode 100644 index 000000000..7586843c2 --- /dev/null +++ b/asterisk-java-ami/src/test/java/org/asteriskjava/ami/action/ChallengeActionItTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.ami.action; + +import io.netty.bootstrap.Bootstrap; +import io.netty.channel.Channel; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelInitializer; +import io.netty.channel.SimpleChannelInboundHandler; +import io.netty.channel.nio.NioEventLoopGroup; +import io.netty.channel.socket.SocketChannel; +import io.netty.channel.socket.nio.NioSocketChannel; +import io.netty.handler.codec.string.StringDecoder; +import io.netty.handler.codec.string.StringEncoder; +import io.netty.handler.timeout.IdleStateHandler; +import org.asteriskjava.ami.ActionFieldsComparator; +import org.asteriskjava.ami.action.response.ChallengeManagerActionResponse; +import org.asteriskjava.ami.action.response.ManagerActionResponse; +import org.asteriskjava.core.databind.AsteriskDecoder; +import org.asteriskjava.core.databind.AsteriskEncoder; +import org.junit.jupiter.api.Test; +import org.testcontainers.containers.GenericContainer; +import org.testcontainers.junit.jupiter.Container; +import org.testcontainers.junit.jupiter.Testcontainers; + +import java.time.Instant; +import java.util.concurrent.atomic.AtomicReference; + +import static java.time.Duration.ofSeconds; +import static java.time.Instant.now; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.assertj.core.api.Assertions.assertThat; +import static org.asteriskjava.ami.action.AuthType.MD5; +import static org.asteriskjava.ami.action.response.ResponseType.Success; +import static org.asteriskjava.core.NewlineDelimiter.CRLF; +import static org.awaitility.Awaitility.await; +import static org.hamcrest.core.IsInstanceOf.instanceOf; +import static org.testcontainers.containers.BindMode.READ_ONLY; +import static org.testcontainers.containers.wait.strategy.Wait.forLogMessage; + +@Testcontainers +class ChallengeActionItTest { + @SuppressWarnings("rawtypes") + @Container + private static final GenericContainer asterisk = new GenericContainer("andrius/asterisk:alpine-18.15.1") + .withClasspathResourceMapping("manager.conf", "/etc/asterisk/manager.conf", READ_ONLY) + .withAccessToHost(true) + .withExposedPorts(5038) + .waitingFor(forLogMessage(".*Asterisk Ready.*", 1)); + + @Test + void shouldSendChallengeActionAndReceiveChallengeResponse() throws InterruptedException { + //given + Instant now = now(); + + ChallengeAction challengeAction = new ChallengeAction(); + challengeAction.setActionId("id-1"); + challengeAction.setAuthType(MD5); + + //when + ChallengeManagerActionResponse actual = testAction(challengeAction, ChallengeManagerActionResponse.class, now); + + //then + assertThat(actual.getResponse()).isEqualTo(Success); + assertThat(actual.getActionId()).isEqualTo("id-1"); + assertThat(actual.getDateReceived()).isEqualTo(now); + assertThat(actual.getChallenge()).isNotBlank(); + } + + private static R testAction(ManagerAction action, Class responseClass, Instant instant) throws InterruptedException { + AsteriskEncoder asteriskEncoder = AsteriskEncoder + .builder() + .crlfNewlineDelimiter() + .fieldNamesComparator(new ActionFieldsComparator()) + .build(); + AtomicReference response = new AtomicReference<>(); + + NioEventLoopGroup group = new NioEventLoopGroup(); + Bootstrap bootstrap = new Bootstrap(); + bootstrap + .group(group) + .channel(NioSocketChannel.class) + .handler(new ChannelInitializer() { + @Override + protected void initChannel(SocketChannel ch) { + ch.pipeline() + .addLast("decoder", new StringDecoder()) + .addLast("encoder", new StringEncoder()) + .addLast("actionResponse", new ActionResponseHandler<>(response, responseClass, instant)) + .addLast(new IdleStateHandler(0, 0, 2, SECONDS)); + } + }); + Channel channel = bootstrap.connect(asterisk.getHost(), asterisk.getFirstMappedPort()).sync().channel(); + channel.writeAndFlush(asteriskEncoder.encode(action)); + + await().atMost(ofSeconds(5)).untilAtomic(response, instanceOf(responseClass)); + + return response.get(); + } + + static class ActionResponseHandler extends SimpleChannelInboundHandler { + private final AsteriskDecoder asteriskDecoder = new AsteriskDecoder(); + private final AtomicReference response; + private final Class clazz; + private final Instant instant; + + public ActionResponseHandler(AtomicReference response, Class clazz, Instant instant) { + this.response = response; + this.clazz = clazz; + this.instant = instant; + } + + @Override + protected void channelRead0(ChannelHandlerContext ctx, String message) { + System.out.println("Received message from Asterisk: " + message); + + if (message.startsWith("Asterisk Call Manager/")) { + return; + } + + String[] split = message.split(CRLF.getPattern()); + + R decode = asteriskDecoder.decode(split, clazz); + decode.setDateReceived(instant); + response.set(decode); + } + } +} diff --git a/asterisk-java-ami/src/test/resources/manager.conf b/asterisk-java-ami/src/test/resources/manager.conf new file mode 100644 index 000000000..39da70486 --- /dev/null +++ b/asterisk-java-ami/src/test/resources/manager.conf @@ -0,0 +1,154 @@ +[general] +enabled = yes + +port = 5038 +bindaddr = 0.0.0.0 + +; Parameters that control AMI over TLS. ("enabled" must be set too). +; You can open a connection to this socket with e.g. +; +; openssl s_client -connect my_host:5039 +; +;tlsenable=no ; set to YES to enable it +;tlsbindaddr=0.0.0.0:5039 ; address and port to bind to, default to bindaddr and port 5039 +;tlscertfile=/tmp/asterisk.pem ; path to the certificate. +;tlsprivatekey=/tmp/private.pem ; path to the private key, if no private given, + ; if no tlsprivatekey is given, default is to search + ; tlscertfile for private key. +;tlscipher= ; string specifying which SSL ciphers to use or not use +; +;allowmultiplelogin = yes ; IF set to no, rejects manager logins that are already in use. +; ; The default is yes. +; +;displayconnects = yes +; +; Add a Unix epoch timestamp to events (not action responses) +; +;timestampevents = yes + +;brokeneventsaction = yes ; Restore previous behavior that caused the events + ; action to not return a response in certain + ; circumstances. Defaults to 'no'. + +; +; Display certain channel variables every time a channel-oriented +; event is emitted: +; +; Note that this does incur a performance penalty and should be avoided if possible. +; +;channelvars = var1,var2,var3 + +; debug = on ; enable some debugging info in AMI messages (default off). + ; Also accessible through the "manager debug" CLI command. + +; authtimeout specifies the maximum number of seconds a client has to +; authenticate. If the client does not authenticate beofre this timeout +; expires, the client will be disconnected. (default: 30 seconds) + +;authtimeout = 30 + +; authlimit specifies the maximum number of unauthenticated sessions that will +; be allowed to connect at any given time. + +;authlimit = 50 + +;httptimeout = 60 +; a) httptimeout sets the Max-Age of the http cookie +; b) httptimeout is the amount of time the webserver waits +; on a action=waitevent request (actually its httptimeout-10) +; c) httptimeout is also the amount of time the webserver keeps +; a http session alive after completing a successful action +; +; disabledevents specifies AMI events which should be completely globally disabled. +; These events will not be available to any AMI listeners. Use this to disable +; frequent events which are not desired for any listeners. Default +; is no events are globally disabled. Event names are case-sensitive. +; Events disabled in stasis.conf do not also need to be disabled here. +; If you don't want to completely disable an AMI event, also consider the +; filter option available on a per-manager user basis to block unwanted +; events from being received in a stream (as opposed to this option which +; would prevent specified events from being generated at all). + +;disabledevents = Newexten,Varset + +;[mark] +;secret = mysecret +;deny=0.0.0.0/0.0.0.0 +;permit=209.16.236.73/255.255.255.0 +;acl=named_acl_example ; use a named ACL from acl.conf +; +; +;setvar=PBXACCOUNT=edvina +; The setvar option defines channel variables that will be set when this account +; originates a call. You can define multiple setvar= commands for one manager +; user. +; +;eventfilter=Event: Newchannel +;eventfilter=Channel: (PJ)?SIP/(james|jim|john)- +;eventfilter=!Channel: DAHDI/ +; The eventfilter option is used to whitelist or blacklist events per user. +; A filter consists of an (unanchored) regular expression that is run on the +; entire event data. If the first character of the filter is an exclamation +; mark (!), the filter is appended to the blacklist instead of the whitelist. +; After first checking the read access below, the regular expression filters +; are processed as follows: +; - If no filters are configured all events are reported as normal. +; - If there are white filters only: implied black all filter processed first, +; then white filters. +; - If there are black filters only: implied white all filter processed first, +; then black filters. +; - If there are both white and black filters: implied black all filter processed +; first, then white filters, and lastly black filters. + +; +; If the device connected via this user accepts input slowly, +; the timeout for writes to it can be increased to keep it +; from being disconnected (value is in milliseconds) +; +; writetimeout = 100 +; +;displayconnects = yes ; Display on CLI user login/logoff +; +; Authorization for various classes +; +; Read authorization permits you to receive asynchronous events, in general. +; Write authorization permits you to send commands and get back responses. The +; following classes exist: +; +; all - All event classes below (including any we may have missed). +; system - General information about the system and ability to run system +; management commands, such as Shutdown, Restart, and Reload. This +; class also includes dialplan manipulation actions such as +; DialplanExtensionAdd and DialplanExtensionRemove. +; call - Information about channels and ability to set information in a +; running channel. +; log - Logging information. Read-only. (Defined but not yet used.) +; verbose - Verbose information. Read-only. (Defined but not yet used.) +; agent - Information about queues and agents and ability to add queue +; members to a queue. +; user - Permission to send and receive UserEvent. +; config - Ability to read and write configuration files. +; command - Permission to run CLI commands. Write-only. +; dtmf - Receive DTMF events. Read-only. +; reporting - Ability to get information about the system. +; cdr - Output of cdr_manager, if loaded. Read-only. +; dialplan - Receive NewExten and VarSet events. Read-only. +; originate - Permission to originate new calls. Write-only. +; agi - Output AGI commands executed. Input AGI command to execute. +; cc - Call Completion events. Read-only. +; aoc - Permission to send Advice Of Charge messages and receive Advice +; - Of Charge events. +; test - Ability to read TestEvent notifications sent to the Asterisk Test +; Suite. Note that this is only enabled when the TEST_FRAMEWORK +; compiler flag is defined. +; security - Security Events. Read-only. +; message - Permissions to send out of call messages. Write-only +; +;read = system,call,log,verbose,agent,user,config,dtmf,reporting,cdr,dialplan +;write = system,call,agent,user,config,command,reporting,originate,message + +[asterisk-java-it-tests] +secret=123qwe +permit=0.0.0.0/0.0.0.0 +read=all +write=all diff --git a/asterisk-java-core/build.gradle b/asterisk-java-core/build.gradle index b2b4104ed..2550e9805 100644 --- a/asterisk-java-core/build.gradle +++ b/asterisk-java-core/build.gradle @@ -15,9 +15,13 @@ repositories { } dependencies { + implementation 'org.apache.commons:commons-lang3:3.14.0' + implementation 'org.slf4j:slf4j-api:2.0.9' + testImplementation 'org.assertj:assertj-core:3.24.2' testImplementation 'org.junit.jupiter:junit-jupiter:5.10.1' testImplementation 'org.mockito:mockito-core:5.7.0' + testImplementation 'org.slf4j:slf4j-simple:2.0.9' } tasks.named('test') { diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskDecoder.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskDecoder.java new file mode 100644 index 000000000..1b0c31675 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskDecoder.java @@ -0,0 +1,219 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind; + +import org.apache.commons.lang3.tuple.Pair; +import org.asteriskjava.core.databind.TypeConversionRegister.Converter; +import org.slf4j.Logger; + +import java.lang.reflect.Method; +import java.lang.reflect.ParameterizedType; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.stream.Collectors; + +import static java.util.Arrays.stream; +import static java.util.Locale.ENGLISH; +import static org.asteriskjava.core.databind.CodersConsts.nameValueSeparator; +import static org.asteriskjava.core.databind.TypeConversionRegister.TYPE_CONVERTERS; +import static org.asteriskjava.core.databind.utils.AnnotationUtils.getName; +import static org.asteriskjava.core.databind.utils.Invoker.invokeOn; +import static org.asteriskjava.core.databind.utils.ReflectionUtils.getSetters; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class AsteriskDecoder { + private static final Logger logger = getLogger(AsteriskDecoder.class); + + private boolean caseSensitive = true; + + public AsteriskDecoder() { + } + + public AsteriskDecoder(boolean caseSensitive) { + this.caseSensitive = caseSensitive; + } + + public T decode(String[] source, Class target) { + return decode(toMap(source), target); + } + + public T decode(Map source, Class target) { + Map setters = getSetters(target) + .entrySet() + .stream() + .collect(Collectors.toMap(e -> { + String name = getName(e.getValue(), e.getKey()); + return caseSensitive ? name : name.toLowerCase(ENGLISH); + }, Entry::getValue)); + + T result = instantiateResultClass(target); + for (Entry entry : source.entrySet()) { + handleEntry(entry, setters, result, caseSensitive); + } + return result; + } + + private static T instantiateResultClass(Class target) { + try { + return target.getConstructor().newInstance(); + } catch (Exception e) { + throw new RuntimeException("Cannot instantiate class %s".formatted(target), e); + } + } + + private static void handleEntry(Entry entry, Map setters, T result, boolean caseSensitive) { + Object value = entry.getValue(); + + String key = caseSensitive ? entry.getKey() : entry.getKey().toLowerCase(ENGLISH); + Method method = setters.getOrDefault(key, null); + if (method == null) { + logger.warn("Unable to set the '{}' property to the value '{}' in the '{}' class. There is no setter method available. " + + "Please report at https://github.com/asterisk-java/asterisk-java/issues.", + entry.getKey(), value, result.getClass().getName()); + return; + } + + Object targetValue = getValue(value, method); + invokeOn(result).method(method).withParameter(targetValue); + } + + private static Object getValue(Object value, Method method) { + Class targetDataType = method.getParameterTypes()[0]; + if (targetDataType.isEnum()) { + return parseEnum(method, value); + } else if (targetDataType.isAssignableFrom(List.class)) { + return parseList(method, value); + } else if (targetDataType.isAssignableFrom(Map.class)) { + return parseMap(method, value); + } else { + return parseOtherType(method, value); + } + } + + private static Enum parseEnum(Method method, Object value) { + Class targetDataType = method.getParameterTypes()[0]; + return (Enum) stream(targetDataType.getEnumConstants()) + .filter(t -> t.toString().equals(String.valueOf(value))) + .findFirst() + .orElse(null); + } + + private static List parseList(Method method, Object value) { + ParameterizedType genericParameter = (ParameterizedType) method.getGenericParameterTypes()[0]; + Class listElementType = (Class) genericParameter.getActualTypeArguments()[0]; + + Map, Converter> conversions = TYPE_CONVERTERS.get(listElementType); + + return ((List) value) + .stream() + .map(object -> { + @SuppressWarnings("rawtypes") + Converter converter = conversions.get(object.getClass()); + //noinspection unchecked + return converter.apply(object); + }) + .toList(); + } + + private static Map parseMap(Method method, Object value) { + ParameterizedType genericParameter = (ParameterizedType) method.getGenericParameterTypes()[0]; + Class mapKeyType = (Class) genericParameter.getActualTypeArguments()[0]; + Class mapValueType = (Class) genericParameter.getActualTypeArguments()[1]; + + Map, Converter> keyConversions = TYPE_CONVERTERS.get(mapKeyType); + Map, Converter> valueConversions = TYPE_CONVERTERS.get(mapValueType); + + if (value instanceof List values) { + Map map = new LinkedHashMap<>(); + for (Object item : values) { + Pair converted = convertMapItem(item, keyConversions, valueConversions); + map.put(converted.getKey(), converted.getValue()); + } + return map; + } else { + Pair converted = convertMapItem(value, keyConversions, valueConversions); + return Map.of(converted.getKey(), converted.getValue()); + } + } + + private static Object parseOtherType(Method method, Object value) { + Class sourceType = value.getClass(); + Class targetType = method.getParameterTypes()[0]; + + Map, Converter> conversions = TYPE_CONVERTERS.get(targetType); + @SuppressWarnings("rawtypes") + Converter converter = conversions.get(sourceType); + //noinspection unchecked + return converter.apply(value); + } + + private static Pair convertMapItem( + Object object, + Map, Converter> keyConversions, + Map, Converter> valueConversions + ) { + String[] split = String.valueOf(object).split("="); + Pair deserialize = Pair.of(split[0], split[1]); + + @SuppressWarnings("rawtypes") + Converter keyConverter = keyConversions.get(String.class); + @SuppressWarnings("rawtypes") + Converter valueConverter = valueConversions.get(String.class); + + String key = String.valueOf(deserialize.getKey()); + @SuppressWarnings("unchecked") + Object keyValue = keyConverter.apply(key); + + String value = String.valueOf(deserialize.getValue()); + @SuppressWarnings("unchecked") + Object valueValue = valueConverter.apply(value); + + return Pair.of(keyValue, valueValue); + } + + private static Map toMap(String[] source) { + Map map = new LinkedHashMap<>(); + for (String line : source) { + String[] split = line.split(nameValueSeparator); + String name = split[0].trim(); + Object value = split[1].trim(); + + if (map.containsKey(name)) { + Object currenValue = map.get(name); + //noinspection rawtypes + if (currenValue instanceof List list) { + //noinspection unchecked + list.add(value); + } else { + List list = new LinkedList<>(); + list.add(currenValue); + list.add(value); + map.put(name, list); + } + } else { + map.put(name, value); + } + } + return map; + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskEncoder.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskEncoder.java new file mode 100644 index 000000000..7a6d2de70 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskEncoder.java @@ -0,0 +1,152 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind; + +import org.asteriskjava.core.NewlineDelimiter; + +import java.lang.reflect.Method; +import java.util.*; +import java.util.Map.Entry; + +import static java.util.Objects.requireNonNull; +import static java.util.stream.Collectors.joining; +import static org.asteriskjava.core.NewlineDelimiter.CRLF; +import static org.asteriskjava.core.NewlineDelimiter.LF; +import static org.asteriskjava.core.databind.CodersConsts.*; +import static org.asteriskjava.core.databind.utils.AnnotationUtils.getName; +import static org.asteriskjava.core.databind.utils.Invoker.invokeOn; +import static org.asteriskjava.core.databind.utils.ReflectionUtils.getGetters; + +/** + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class AsteriskEncoder { + private final NewlineDelimiter newlineDelimiter; + private final Comparator fieldNamesComparator; + + public AsteriskEncoder(NewlineDelimiter newlineDelimiter, Comparator fieldNamesComparator) { + this.newlineDelimiter = newlineDelimiter; + this.fieldNamesComparator = fieldNamesComparator; + } + + public AsteriskEncoder(NewlineDelimiter newlineDelimiter) { + this(newlineDelimiter, null); + } + + public String encode(Object source) { + return encode(toMap(source)); + } + + public String encode(Map source) { + source = sortIfNeeded(source); + StringBuilder stringBuilder = new StringBuilder(); + if (!source.isEmpty()) { + for (Entry entry : source.entrySet()) { + String name = entry.getKey(); + Object value = entry.getValue(); + if (value instanceof List values) { + values.forEach(v -> appendFieldNameAndValue(name, v, stringBuilder)); + } else { + appendFieldNameAndValue(name, value, stringBuilder); + } + } + // Trailing delimiter. + stringBuilder.append(newlineDelimiter.getPattern()); + } + return stringBuilder.toString(); + } + + public static Builder builder() { + return new Builder(); + } + + private Map sortIfNeeded(Map source) { + if (fieldNamesComparator == null) { + return source; + } + + Map sourceSortedIfNeeded = new TreeMap<>(fieldNamesComparator); + sourceSortedIfNeeded.putAll(source); + return sourceSortedIfNeeded; + } + + private void appendFieldNameAndValue(String name, Object value, StringBuilder stringBuilder) { + stringBuilder.append(name); + stringBuilder.append(nameValueSeparator); + stringBuilder.append(value); + stringBuilder.append(newlineDelimiter.getPattern()); + } + + private static Map toMap(Object source) { + Map map = new LinkedHashMap<>(); + + Class clazz = source.getClass(); + Map getters = getGetters(clazz); + for (Entry entry : getters.entrySet()) { + Method method = entry.getValue(); + + String name = getName(method, entry.getKey()); + Object value = invokeOn(source).method(method).withoutParameter(); + + if (value instanceof List values) { + value = values + .stream() + .map(Object::toString) + .collect(joining(listSeparator)); + } + + if (value instanceof Map values) { + value = values + .entrySet() + .stream() + .map(e -> mapTemplate.formatted(e.getKey(), e.getValue())) + .toList(); + } + + map.put(name, value); + } + + return map; + } + + public static class Builder { + private NewlineDelimiter newlineDelimiter = CRLF; + private Comparator fieldNamesComparator; + + public Builder newlineDelimiter(NewlineDelimiter newlineDelimiter) { + this.newlineDelimiter = requireNonNull(newlineDelimiter, "newlineDelimiter cannot be null"); + return this; + } + + public Builder crlfNewlineDelimiter() { + return newlineDelimiter(CRLF); + } + + public Builder lfNewlineDelimiter() { + return newlineDelimiter(LF); + } + + public Builder fieldNamesComparator(Comparator fieldNamesComparator) { + this.fieldNamesComparator = requireNonNull(fieldNamesComparator, "fieldNamesComparator cannot be null"); + return this; + } + + public AsteriskEncoder build() { + return new AsteriskEncoder(newlineDelimiter, fieldNamesComparator); + } + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskGenerator.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskGenerator.java deleted file mode 100644 index c899c6ec4..000000000 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskGenerator.java +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright 2004-2023 Asterisk Java contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.asteriskjava.core.databind; - -import org.asteriskjava.core.NewlineDelimiter; - -/** - * @author Piotr Olaszewski - * @since 4.0.0 - */ -public class AsteriskGenerator { - private static final String FIELD_NAME_VALUE_DELIMITER = ": "; - - private final StringBuilder stringBuilder = new StringBuilder(); - - private final NewlineDelimiter newlineDelimiter; - - public AsteriskGenerator(NewlineDelimiter newlineDelimiter) { - this.newlineDelimiter = newlineDelimiter; - } - - public void writeFieldName(String name) { - stringBuilder.append(name); - stringBuilder.append(FIELD_NAME_VALUE_DELIMITER); - } - - public void writeFieldValue(String value) { - stringBuilder.append(value); - stringBuilder.append(newlineDelimiter.getPattern()); - } - - public String generate() { - return stringBuilder.toString(); - } -} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskObjectMapper.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskObjectMapper.java deleted file mode 100644 index 386343571..000000000 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/AsteriskObjectMapper.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2004-2023 Asterisk Java contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.asteriskjava.core.databind; - -import org.asteriskjava.core.NewlineDelimiter; -import org.asteriskjava.core.databind.writer.AsteriskObjectMethodWriter; -import org.asteriskjava.core.databind.writer.AsteriskObjectWriter; - -import java.util.Comparator; -import java.util.List; - -import static java.util.Objects.requireNonNull; -import static org.asteriskjava.core.NewlineDelimiter.CRLF; -import static org.asteriskjava.core.NewlineDelimiter.LF; - -/** - * @author Piotr Olaszewski - * @since 4.0.0 - */ -public class AsteriskObjectMapper { - private final NewlineDelimiter newlineDelimiter; - private final Comparator fieldNamesComparator; - - private AsteriskObjectMapper( - NewlineDelimiter newlineDelimiter, - Comparator fieldNamesComparator - ) { - this.newlineDelimiter = newlineDelimiter; - this.fieldNamesComparator = fieldNamesComparator; - } - - public String writeValue(Object value) { - Class clazz = value.getClass(); - - AsteriskObjectWriter asteriskObjectWriter = new AsteriskObjectWriter(clazz, fieldNamesComparator); - - return writeValue(value, asteriskObjectWriter); - } - - private String writeValue(Object value, AsteriskObjectWriter asteriskObjectWriter) { - AsteriskGenerator asteriskGenerator = new AsteriskGenerator(newlineDelimiter); - List asteriskObjectMethodWriters = asteriskObjectWriter.getAsteriskObjectMethodWriters(); - for (AsteriskObjectMethodWriter asteriskObjectMethodWriter : asteriskObjectMethodWriters) { - asteriskObjectMethodWriter.writeName(asteriskGenerator); - asteriskObjectMethodWriter.writeValue(value, asteriskGenerator); - } - return asteriskGenerator.generate(); - } - - public static Builder builder() { - return new Builder(); - } - - public static class Builder { - private NewlineDelimiter newlineDelimiter = CRLF; - private Comparator fieldNamesComparator; - - public Builder newlineDelimiter(NewlineDelimiter newlineDelimiter) { - this.newlineDelimiter = requireNonNull(newlineDelimiter, "newlineDelimiter cannot be null"); - return this; - } - - public Builder crlfNewlineDelimiter() { - return newlineDelimiter(CRLF); - } - - public Builder lfNewlineDelimiter() { - return newlineDelimiter(LF); - } - - public Builder fieldNamesComparator(Comparator fieldNamesComparator) { - this.fieldNamesComparator = requireNonNull(fieldNamesComparator, "fieldNamesComparator cannot be null"); - return this; - } - - public AsteriskObjectMapper build() { - return new AsteriskObjectMapper(newlineDelimiter, fieldNamesComparator); - } - } -} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/WritableFileName.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/CodersConsts.java similarity index 71% rename from asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/WritableFileName.java rename to asterisk-java-core/src/main/java/org/asteriskjava/core/databind/CodersConsts.java index e97aa3399..332da30b3 100644 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/WritableFileName.java +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/CodersConsts.java @@ -13,15 +13,14 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.asteriskjava.core.databind.serializer; - -import org.asteriskjava.core.databind.AsteriskGenerator; +package org.asteriskjava.core.databind; /** - * Marker interface for writing the field name in the serializer implementation instead of {@link AsteriskGenerator}. - * * @author Piotr Olaszewski * @since 4.0.0 */ -public interface WritableFileName { +class CodersConsts { + final static String nameValueSeparator = ": "; + final static String listSeparator = ","; + final static String mapTemplate = "%s=%s"; } diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/TypeConversionRegister.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/TypeConversionRegister.java new file mode 100644 index 000000000..4404e4935 --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/TypeConversionRegister.java @@ -0,0 +1,84 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind; + +import java.time.Instant; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +/** + * Manages different converters for data types. + * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +public class TypeConversionRegister { + public static final Map, Map, Converter>> TYPE_CONVERTERS = new ConcurrentHashMap<>(); + + static { + convertFrom(String.class).to(Instant.class).register(Instant::parse); + convertFrom(String.class).to(Integer.class).register(Integer::valueOf); + convertFrom(String.class).to(String.class).register(Object::toString); + } + + private TypeConversionRegister() { + } + + public static TypeConverter convertFrom(Class sourceType) { + return new TypeConverter<>(sourceType); + } + + public static class TypeConverter { + private final Class sourceType; + + TypeConverter(Class sourceType) { + this.sourceType = sourceType; + } + + public Registrar to(Class targetType) { + return new Registrar<>(sourceType, targetType); + } + + public static class Registrar { + private final Class sourceType; + private final Class targetType; + + Registrar(Class sourceType, Class targetType) { + this.sourceType = sourceType; + this.targetType = targetType; + } + + public void register(Converter converter) { + TYPE_CONVERTERS.computeIfAbsent(targetType, k -> new HashMap<>()).put(sourceType, converter); + } + } + } + + @FunctionalInterface + public interface Converter extends Function { + T applyWithException(S source) throws Exception; + + default T apply(S source) { + try { + return applyWithException(source); + } catch (Exception ignored) { + return null; + } + } + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/AsteriskSerializer.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/AsteriskSerializer.java deleted file mode 100644 index f885a27e2..000000000 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/AsteriskSerializer.java +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2004-2023 Asterisk Java contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.asteriskjava.core.databind.serializer; - -import org.asteriskjava.core.databind.AsteriskGenerator; - -/** - * Interface representing a serializer for a given type. - * - * @param type of the serialized value - * @author Piotr Olaszewski - * @since 4.0.0 - */ -public interface AsteriskSerializer { - /** - * Serializes object into Asterisk string. - * - * @param fieldName field name of the currently serialized object - * @param value object to serialize - * @param asteriskGenerator generator used to write a Java object to an Asterisk string - */ - void serialize(String fieldName, T value, AsteriskGenerator asteriskGenerator); -} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializer.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializer.java deleted file mode 100644 index 5871206e9..000000000 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializer.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2004-2023 Asterisk Java contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.asteriskjava.core.databind.serializer.custom; - -import org.asteriskjava.core.databind.AsteriskGenerator; -import org.asteriskjava.core.databind.serializer.AsteriskSerializer; - -import java.util.Collection; - -import static java.util.stream.Collectors.joining; - -/** - * Serializer for joining collection elements by calling their toString method, using a comma as the 'glue'. - * - * @author Piotr Olaszewski - * @since 4.0.0 - */ -public class ComaJoiningSerializer implements AsteriskSerializer> { - private static final String COMA_SEPARATOR = ","; - - @Override - public void serialize(String fieldName, Collection value, AsteriskGenerator asteriskGenerator) { - String fieldValue = value - .stream() - .map(Object::toString) - .collect(joining(COMA_SEPARATOR)); - - asteriskGenerator.writeFieldValue(fieldValue); - } -} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializer.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializer.java deleted file mode 100644 index 9acb02f86..000000000 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializer.java +++ /dev/null @@ -1,54 +0,0 @@ -/* - * Copyright 2004-2023 Asterisk Java contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.asteriskjava.core.databind.serializer.custom; - -import org.asteriskjava.core.databind.AsteriskGenerator; -import org.asteriskjava.core.databind.serializer.AsteriskSerializer; -import org.asteriskjava.core.databind.serializer.WritableFileName; - -import java.util.Map; - -import static java.lang.String.format; - -/** - * Serializer for key=value pairs with an additional field name writer. - *

- * Following code: - *

- * @AsteriskSerialize(VariableSerializer.class)
- * public Map<String, String> getVariable() {
- *     ...
- * }
- * 
- * would produce: - *
- * Variable: key1=value1
- * Variable: key2=value2
- * Variable: key3=value3
- * 
- * - * @author Piotr Olaszewski - * @since 4.0.0 - */ -public class VariableSerializer implements AsteriskSerializer>, WritableFileName { - @Override - public void serialize(String fieldName, Map value, AsteriskGenerator asteriskGenerator) { - value.forEach((key, v) -> { - asteriskGenerator.writeFieldName(fieldName); - asteriskGenerator.writeFieldValue(format("%s=%s", key, v)); - }); - } -} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializer.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/AnnotationUtils.java similarity index 57% rename from asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializer.java rename to asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/AnnotationUtils.java index 46d4b6b41..d0cfebc24 100644 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializer.java +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/AnnotationUtils.java @@ -13,20 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.asteriskjava.core.databind.serializer.std; +package org.asteriskjava.core.databind.utils; -import org.asteriskjava.core.databind.AsteriskGenerator; -import org.asteriskjava.core.databind.serializer.AsteriskSerializer; +import org.asteriskjava.core.databind.annotation.AsteriskName; + +import java.lang.reflect.Method; /** - * Base serializer which calls only the toString method on the passed value. + * Convenient class for handling annotations. * * @author Piotr Olaszewski * @since 4.0.0 */ -public class ToStringSerializer implements AsteriskSerializer { - @Override - public void serialize(String fieldName, Object value, AsteriskGenerator asteriskGenerator) { - asteriskGenerator.writeFieldValue(value.toString()); +public final class AnnotationUtils { + private AnnotationUtils() { + } + + public static String getName(Method method, String name) { + AsteriskName asteriskName = method.getAnnotation(AsteriskName.class); + return asteriskName == null ? name : asteriskName.value(); } } diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/Invoker.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/Invoker.java new file mode 100644 index 000000000..3e873340c --- /dev/null +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/Invoker.java @@ -0,0 +1,66 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind.utils; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +import static org.asteriskjava.core.databind.utils.Invoker.MethodInvoker.method; + +/** + * Helper for invoking methods via reflection. + *

+ * Examples: + *

+ * invokeOn(object).method(method).withoutParameter();
+ * invokeOn(object).method(method).withParameter("some param");
+ * 
+ * + * @author Piotr Olaszewski + * @since 4.0.0 + */ +@FunctionalInterface +public interface Invoker { + String WITHOUT_PARAMETER_MARKER = ""; + + Object withParameter(Object value); + + default Object withoutParameter() { + return withParameter(WITHOUT_PARAMETER_MARKER); + } + + static MethodInvoker invokeOn(Object object) { + return method -> method(method, object); + } + + @FunctionalInterface + interface MethodInvoker { + Invoker method(Method method); + + static Invoker method(Method method, Object object) { + return value -> { + try { + if (value == WITHOUT_PARAMETER_MARKER) { + return method.invoke(object); + } + return method.invoke(object, value); + } catch (IllegalAccessException | InvocationTargetException e) { + throw new RuntimeException(e); + } + }; + } + } +} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/ReflectionUtils.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/ReflectionUtils.java index 19fe0ce80..3bfdcf693 100644 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/ReflectionUtils.java +++ b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/utils/ReflectionUtils.java @@ -16,20 +16,23 @@ package org.asteriskjava.core.databind.utils; import java.lang.reflect.Method; -import java.util.Comparator; import java.util.LinkedHashMap; import java.util.Map; -import java.util.TreeMap; import static java.lang.reflect.Modifier.*; /** - * Convenient class to deal with getters from mapped classes. + * Convenient class to deal with getters and setters from mapped classes. * + * @author Stefan Reuter + * @author Robert Sutton * @author Piotr Olaszewski - * @since 4.0.0 + * @since 1.0.0 */ public final class ReflectionUtils { + private ReflectionUtils() { + } + /** * Returns a {@link Map} of getter methods of the given class. *

@@ -37,34 +40,32 @@ public final class ReflectionUtils { * getter itself (an instance of Method). A method is considered a getter if its name starts with 'get' or 'is'. * It is declared public and takes no arguments. * - * @param clazz the class to return the getters for - * @param comparator the comparator for sorting properties + * @param clazz the class to return the getters for * @return a Map of attributes and their accessor methods (getters) - * @see #getGetters(Class) */ - public static Map getGetters(Class clazz, Comparator comparator) { - Map accessors = comparator != null ? new TreeMap<>(comparator) : new LinkedHashMap<>(); + public static Map getGetters(Class clazz) { + Map accessors = new LinkedHashMap<>(); Method[] methods = clazz.getMethods(); for (Method method : methods) { + String methodName = method.getName(); if (method.getParameterCount() > 0 || - method.getReturnType() == Void.TYPE || - !isPublic(method.getModifiers()) || - isNative(method.getModifiers()) || - isAbstract(method.getModifiers()) || - isStatic(method.getModifiers()) || - method.getName().equals("toString") + method.getReturnType() == Void.TYPE || + !isPublic(method.getModifiers()) || + isNative(method.getModifiers()) || + isAbstract(method.getModifiers()) || + isStatic(method.getModifiers()) || + methodName.equals("toString") ) { continue; } String name = null; - String methodName = method.getName(); if (methodName.startsWith("get")) { - name = methodName.substring(3); + name = methodName.substring("get".length()); } else if (methodName.startsWith("is")) { - name = methodName.substring(2); + name = methodName.substring("is".length()); } if (name == null || name.isEmpty()) { @@ -78,13 +79,40 @@ public static Map getGetters(Class clazz, Comparator } /** - * Returns a Map of getter methods of the given class. + * Returns a {@link Map} of setter methods of the given class. + *

+ * The key of the map contains the name of the attribute that can be accessed by the setter, the value the setter + * itself (an instance of {@link Method}). A method is considered a setter if its name starts with 'set', it is + * declared public and takes exactly one argument. * - * @param clazz the class to return the getters for - * @return a Map of attributes and their accessor methods (getters) - * @see #getGetters(Class, Comparator) + * @param clazz the class to return the setters for + * @return a Map of attributes and their accessor methods (setters) */ - public static Map getGetters(Class clazz) { - return getGetters(clazz, null); + public static Map getSetters(Class clazz) { + Map accessors = new LinkedHashMap<>(); + + Method[] methods = clazz.getMethods(); + for (Method method : methods) { + String methodName = method.getName(); + if (!methodName.startsWith("set") || + method.getParameterCount() != 1 || + !isPublic(method.getModifiers()) || + isNative(method.getModifiers()) || + isAbstract(method.getModifiers()) || + isStatic(method.getModifiers()) || + methodName.equals("toString") + ) { + continue; + } + + String name = methodName.substring("set".length()); + + if (name.isEmpty()) { + continue; + } + + accessors.put(name, method); + } + return accessors; } } diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriter.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriter.java deleted file mode 100644 index eb33c1dd7..000000000 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectMethodWriter.java +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2004-2023 Asterisk Java contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.asteriskjava.core.databind.writer; - -import org.asteriskjava.core.databind.AsteriskGenerator; -import org.asteriskjava.core.databind.serializer.AsteriskSerializer; - -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; - -/** - * Writer class to write field names and serialized values using the {@link AsteriskGenerator}. - * - * @author Piotr Olaszewski - * @since 4.0.0 - */ -public class AsteriskObjectMethodWriter { - private final Method method; - private final String name; - private final AsteriskSerializer asteriskSerializer; - private final AsteriskObjectMethodWriterContext asteriskObjectMethodWriterContext; - - public AsteriskObjectMethodWriter( - Method method, - String name, - AsteriskSerializer asteriskSerializer, - AsteriskObjectMethodWriterContext asteriskObjectMethodWriterContext - ) { - this.method = method; - this.name = name; - this.asteriskSerializer = asteriskSerializer; - this.asteriskObjectMethodWriterContext = asteriskObjectMethodWriterContext; - } - - public void writeName(AsteriskGenerator asteriskGenerator) { - if (!asteriskObjectMethodWriterContext.serializerWriteFieldName()) { - asteriskGenerator.writeFieldName(name); - } - } - - public void writeValue(Object obj, AsteriskGenerator asteriskGenerator) { - Object currentValue = getCurrentValue(obj); - asteriskSerializer.serialize(name, currentValue, asteriskGenerator); - } - - private Object getCurrentValue(Object obj) { - try { - return method.invoke(obj); - } catch (IllegalAccessException | InvocationTargetException e) { - throw new RuntimeException(e); - } - } -} diff --git a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectWriter.java b/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectWriter.java deleted file mode 100644 index 91d6ad710..000000000 --- a/asterisk-java-core/src/main/java/org/asteriskjava/core/databind/writer/AsteriskObjectWriter.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Copyright 2004-2023 Asterisk Java contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.asteriskjava.core.databind.writer; - -import org.asteriskjava.core.databind.annotation.AsteriskName; -import org.asteriskjava.core.databind.annotation.AsteriskSerialize; -import org.asteriskjava.core.databind.serializer.AsteriskSerializer; -import org.asteriskjava.core.databind.serializer.WritableFileName; -import org.asteriskjava.core.databind.serializer.std.ToStringSerializer; - -import java.lang.reflect.Method; -import java.util.Comparator; -import java.util.List; -import java.util.Map.Entry; - -import static org.asteriskjava.core.databind.utils.ReflectionUtils.getGetters; - -/** - * @author Piotr Olaszewski - * @since 4.0.0 - */ -public class AsteriskObjectWriter { - private final Class clazz; - private final Comparator fieldNamesComparator; - - public AsteriskObjectWriter(Class clazz, Comparator fieldNamesComparator) { - this.clazz = clazz; - this.fieldNamesComparator = fieldNamesComparator; - } - - public List getAsteriskObjectMethodWriters() { - return getGetters(clazz, fieldNamesComparator) - .entrySet() - .stream() - .map(this::getAsteriskObjectMethodWriter) - .toList(); - } - - private AsteriskObjectMethodWriter getAsteriskObjectMethodWriter(Entry entry) { - Method method = entry.getValue(); - - String name = getName(method, entry.getKey()); - - AsteriskSerializer asteriskSerializer = getAsteriskSerializer(method); - - boolean serializerWriteFieldName = asteriskSerializer instanceof WritableFileName; - AsteriskObjectMethodWriterContext context = new AsteriskObjectMethodWriterContext(serializerWriteFieldName); - - return new AsteriskObjectMethodWriter(method, name, asteriskSerializer, context); - } - - private static String getName(Method method, String name) { - AsteriskName asteriskName = method.getAnnotation(AsteriskName.class); - return asteriskName == null ? name : asteriskName.value(); - } - - private AsteriskSerializer getAsteriskSerializer(Method method) { - AsteriskSerialize asteriskSerialize = method.getAnnotation(AsteriskSerialize.class); - if (asteriskSerialize == null) { - return new ToStringSerializer(); - } - - Class> asteriskSerializerClass = asteriskSerialize.value(); - try { - //noinspection unchecked - return (AsteriskSerializer) asteriskSerializerClass.getDeclaredConstructor().newInstance(); - } catch (Exception e) { - throw new RuntimeException("Cannot create new instance of serializer %s".formatted(asteriskSerialize), e); - } - } -} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskDecoderTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskDecoderTest.java new file mode 100644 index 000000000..5a21a2124 --- /dev/null +++ b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskDecoderTest.java @@ -0,0 +1,419 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind; + +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.apache.commons.lang3.builder.HashCodeBuilder; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.asteriskjava.core.databind.annotation.AsteriskName; +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.List; +import java.util.Map; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.asteriskjava.core.NewlineDelimiter.LF; +import static org.asteriskjava.core.databind.AsteriskDecoderTest.BaseBean.ResponseType.Goodbye; + +class AsteriskDecoderTest { + private final AsteriskDecoder asteriskDecoder = new AsteriskDecoder(); + + @Test + void shouldDecodeForSimpleBeanWhichExtendsFormBaseBean() { + //given + Instant date = Instant.parse("2023-11-20T20:33:30.002Z"); + + Map content = Map.of( + "ActionID", "id-1", + "DateReceived", date.toString(), + "Challenge", "123456", + "Response", "Goodbye" + ); + + //when + SimpleBean simpleBean = asteriskDecoder.decode(content, SimpleBean.class); + + //then + SimpleBean expected = new SimpleBean(); + expected.setChallenge("123456"); + expected.setActionId("id-1"); + expected.setDateReceived(date); + expected.setResponse(Goodbye); + assertThat(simpleBean).isEqualTo(expected); + } + + @Test + void shouldDecodeListValues() { + //given + AsteriskDecoder asteriskDecoder = new AsteriskDecoder(); + + Map content = Map.of( + "Number", List.of("1", "2", "3") + ); + + //when + ListBean listBean = asteriskDecoder.decode(content, ListBean.class); + + //then + ListBean expected = new ListBean(); + expected.setNumbers(List.of(1, 2, 3)); + assertThat(listBean).isEqualTo(expected); + } + + @Test + void shouldDecodeMap() { + //given + AsteriskDecoder asteriskDecoder = new AsteriskDecoder(); + + Map content = Map.of("Header", "1=name1"); + + //when + MapBean someClass = asteriskDecoder.decode(content, MapBean.class); + + //then + Map map = Map.of(1, "name1"); + MapBean expected = new MapBean(); + expected.setHeaders(map); + assertThat(someClass).isEqualTo(expected); + } + + @Test + void shouldDecodeMapWhenIsListOfEntries() { + //given + AsteriskDecoder asteriskDecoder = new AsteriskDecoder(); + + Map content = Map.of( + "Header", List.of("1=name1", "2=name2", "3=name3") + ); + + //when + MapBean mapBean = asteriskDecoder.decode(content, MapBean.class); + + //then + Map map = Map.of( + 1, "name1", + 2, "name2", + 3, "name3" + ); + MapBean expected = new MapBean(); + expected.setHeaders(map); + assertThat(mapBean).isEqualTo(expected); + } + + @Test + void shouldDecodeSimpleBeanFromString() { + //given + AsteriskDecoder asteriskDecoder = new AsteriskDecoder(); + + Instant date = Instant.parse("2023-11-20T20:33:30.002Z"); + String string = """ + ActionID: id-1 + DateReceived: %s + Challenge: 123456 + Response: Goodbye + """.formatted(date); + String[] content = string.split(LF.getPattern()); + + //when + SimpleBean simpleBean = asteriskDecoder.decode(content, SimpleBean.class); + + //then + SimpleBean expected = new SimpleBean(); + expected.setChallenge("123456"); + expected.setActionId("id-1"); + expected.setDateReceived(date); + expected.setResponse(Goodbye); + assertThat(simpleBean).isEqualTo(expected); + } + + @Test + void shouldDecoderListFromString() { + //given + AsteriskDecoder asteriskDecoder = new AsteriskDecoder(); + + String string = """ + Number: 1 + Number: 2 + Number: 3 + """; + String[] content = string.split(LF.getPattern()); + + //when + ListBean listBean = asteriskDecoder.decode(content, ListBean.class); + + //then + ListBean expected = new ListBean(); + expected.setNumbers(List.of(1, 2, 3)); + assertThat(listBean).isEqualTo(expected); + } + + @Test + void shouldDecodeMapFromString() { + //given + AsteriskDecoder asteriskDecoder = new AsteriskDecoder(); + + String string = """ + Header: 1=name1 + """; + String[] content = string.split(LF.getPattern()); + + //when + MapBean someClass = asteriskDecoder.decode(content, MapBean.class); + + //then + Map map = Map.of(1, "name1"); + MapBean expected = new MapBean(); + expected.setHeaders(map); + assertThat(someClass).isEqualTo(expected); + } + + @Test + void shouldDecodeMapWhenIsListOfEntriesFromString() { + //given + AsteriskDecoder asteriskDecoder = new AsteriskDecoder(); + + String string = """ + Header: 1=name1 + Header: 2=name2 + Header: 3=name3 + """; + String[] content = string.split(LF.getPattern()); + + //when + MapBean mapBean = asteriskDecoder.decode(content, MapBean.class); + + //then + Map map = Map.of( + 1, "name1", + 2, "name2", + 3, "name3" + ); + MapBean expected = new MapBean(); + expected.setHeaders(map); + assertThat(mapBean).isEqualTo(expected); + } + + public static class BaseBean { + public enum ResponseType { + Success, + Error, + Goodbye, + } + + private ResponseType response; + + private Instant dateReceived; + + private String actionId; + + public ResponseType getResponse() { + return response; + } + + public void setResponse(ResponseType response) { + this.response = response; + } + + public Instant getDateReceived() { + return dateReceived; + } + + public void setDateReceived(Instant dateReceived) { + this.dateReceived = dateReceived; + } + + public String getActionId() { + return actionId; + } + + @AsteriskName("ActionID") + public void setActionId(String actionId) { + this.actionId = actionId; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + BaseBean base = (BaseBean) object; + + return new EqualsBuilder() + .append(response, base.response) + .append(dateReceived, base.dateReceived) + .append(actionId, base.actionId) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(response) + .append(dateReceived) + .append(actionId) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("response", response) + .append("dateReceived", dateReceived) + .append("actionId", actionId) + .toString(); + } + } + + public static class SimpleBean extends BaseBean { + private String challenge; + + public String getChallenge() { + return challenge; + } + + public void setChallenge(String challenge) { + this.challenge = challenge; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + SimpleBean someClass = (SimpleBean) object; + + return new EqualsBuilder() + .appendSuper(super.equals(object)) + .append(challenge, someClass.challenge) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .appendSuper(super.hashCode()) + .append(challenge) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .appendSuper(super.toString()) + .append("challenge", challenge) + .toString(); + } + } + + public static class ListBean { + private List numbers; + + public List getNumbers() { + return numbers; + } + + @AsteriskName("Number") + public void setNumbers(List numbers) { + this.numbers = numbers; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + ListBean someClass = (ListBean) object; + + return new EqualsBuilder() + .append(numbers, someClass.numbers) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(numbers) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("numbers", numbers) + .toString(); + } + } + + public static class MapBean { + private Map headers; + + public Map getHeaders() { + return headers; + } + + @AsteriskName("Header") + public void setHeaders(Map headers) { + this.headers = headers; + } + + @Override + public boolean equals(Object object) { + if (this == object) { + return true; + } + + if (object == null || getClass() != object.getClass()) { + return false; + } + + MapBean someClass = (MapBean) object; + + return new EqualsBuilder() + .append(headers, someClass.headers) + .isEquals(); + } + + @Override + public int hashCode() { + return new HashCodeBuilder(17, 37) + .append(headers) + .toHashCode(); + } + + @Override + public String toString() { + return new ToStringBuilder(this) + .append("headers", headers) + .toString(); + } + } +} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskEncoderTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskEncoderTest.java new file mode 100644 index 000000000..73e76fae6 --- /dev/null +++ b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskEncoderTest.java @@ -0,0 +1,158 @@ +/* + * Copyright 2004-2023 Asterisk Java contributors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.asteriskjava.core.databind; + +import org.asteriskjava.core.databind.annotation.AsteriskName; +import org.junit.jupiter.api.Test; + +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import static java.util.Comparator.naturalOrder; +import static org.assertj.core.api.Assertions.assertThat; +import static org.asteriskjava.core.NewlineDelimiter.LF; +import static org.asteriskjava.core.databind.AsteriskEncoderTest.SimpleBean.AuthType.MD5; + +class AsteriskEncoderTest { + @Test + void shouldEncodeWithoutSortingFields() { + //given + AsteriskEncoder asteriskEncoder = new AsteriskEncoder(LF); + + Map source = new LinkedHashMap<>(); + source.put("Z", "value z"); + source.put("A", "value a"); + source.put("M", "value m"); + source.put("F", "value f"); + + //when + String encode = asteriskEncoder.encode(source); + + //then + String expected = "Z: value z" + LF.getPattern(); + expected += "A: value a" + LF.getPattern(); + expected += "M: value m" + LF.getPattern(); + expected += "F: value f" + LF.getPattern() + LF.getPattern(); + assertThat(encode).isEqualTo(expected); + } + + @Test + void shouldEncodeAndSortFields() { + //given + AsteriskEncoder asteriskEncoder = new AsteriskEncoder(LF, naturalOrder()); + + Map source = Map.of( + "Z", "value z", + "A", "value a", + "M", "value m", + "F", "value f" + ); + + //when + String encode = asteriskEncoder.encode(source); + + //then + String expected = "A: value a" + LF.getPattern(); + expected += "F: value f" + LF.getPattern(); + expected += "M: value m" + LF.getPattern(); + expected += "Z: value z" + LF.getPattern() + LF.getPattern(); + assertThat(encode).isEqualTo(expected); + } + + @Test + void shouldEncodeObject() { + //given + AsteriskEncoder asteriskEncoder = new AsteriskEncoder(LF); + + SimpleBean bean = new SimpleBean(); + bean.setActionId("id-1"); + bean.setAuthType(MD5); + bean.setCodecs(List.of("codec1", "codec2")); + Map variable = new LinkedHashMap<>(); + variable.put("key1", "value1"); + variable.put("key2", "value2"); + variable.put("key3", "value3"); + bean.setVariable(variable); + + //when + String string = asteriskEncoder.encode(bean); + + //then + String expected = "Action: SimpleBean" + LF.getPattern(); + expected += "ActionID: id-1" + LF.getPattern(); + expected += "AuthType: MD5" + LF.getPattern(); + expected += "Codecs: codec1,codec2" + LF.getPattern(); + expected += "Variable: key1=value1" + LF.getPattern(); + expected += "Variable: key2=value2" + LF.getPattern(); + expected += "Variable: key3=value3" + LF.getPattern() + LF.getPattern(); + assertThat(string).contains( + "Action: SimpleBean", "ActionID: id-1", "AuthType: MD5", "Codecs: codec1,codec2", + "Variable: key1=value1", "Variable: key2=value2", "Variable: key3=value3" + ); + } + + public static class SimpleBean { + public enum AuthType { + MD5, + } + + private String actionId; + + private AuthType authType; + + private List codecs; + + private Map variable; + + public String getAction() { + return "SimpleBean"; + } + + @AsteriskName("ActionID") + public String getActionId() { + return actionId; + } + + public void setActionId(String actionId) { + this.actionId = actionId; + } + + public AuthType getAuthType() { + return authType; + } + + public void setAuthType(AuthType authType) { + this.authType = authType; + } + + public List getCodecs() { + return codecs; + } + + public void setCodecs(List codecs) { + this.codecs = codecs; + } + + public Map getVariable() { + return variable; + } + + public void setVariable(Map variable) { + this.variable = variable; + } + } +} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskObjectMapperTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskObjectMapperTest.java deleted file mode 100644 index de7a0f54a..000000000 --- a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/AsteriskObjectMapperTest.java +++ /dev/null @@ -1,80 +0,0 @@ -package org.asteriskjava.core.databind; - -import org.asteriskjava.core.databind.annotation.AsteriskName; -import org.asteriskjava.core.databind.annotation.AsteriskSerialize; -import org.asteriskjava.core.databind.serializer.custom.ComaJoiningSerializer; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.asteriskjava.core.NewlineDelimiter.CRLF; -import static org.asteriskjava.core.databind.AsteriskObjectMapper.builder; -import static org.asteriskjava.core.databind.AsteriskObjectMapperTest.SimpleBean.AuthType.MD5; - -class AsteriskObjectMapperTest { - private final AsteriskObjectMapper asteriskObjectMapper = builder() - .crlfNewlineDelimiter() - .build(); - - @Test - void shouldGenerateSimpleBean() { - //given - SimpleBean bean = new SimpleBean(); - bean.setActionId("id-1"); - bean.setAuthType(MD5); - bean.setCodecs(List.of("codec1", "codec2")); - - //when - String string = asteriskObjectMapper.writeValue(bean); - - //then - String expected = "Action: SimpleBean" + CRLF.getPattern(); - expected += "ActionID: id-1" + CRLF.getPattern(); - expected += "AuthType: MD5" + CRLF.getPattern(); - expected += "Codecs: codec1,codec2" + CRLF.getPattern(); - assertThat(string).isEqualTo(expected); - } - - public static class SimpleBean { - public enum AuthType { - MD5, - } - - private String actionId; - - private AuthType authType; - - private List codecs; - - public String getAction() { - return "SimpleBean"; - } - - @AsteriskName("ActionID") - public String getActionId() { - return actionId; - } - - public void setActionId(String actionId) { - this.actionId = actionId; - } - - public AuthType getAuthType() { - return authType; - } - - public void setAuthType(AuthType authType) { - this.authType = authType; - } - - @AsteriskSerialize(ComaJoiningSerializer.class) - public List getCodecs() { - return codecs; - } - - public void setCodecs(List codecs) { - this.codecs = codecs; - } - } -} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializerTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializerTest.java deleted file mode 100644 index b5411890f..000000000 --- a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/ComaJoiningSerializerTest.java +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2004-2023 Asterisk Java contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.asteriskjava.core.databind.serializer.custom; - -import org.asteriskjava.core.databind.AsteriskGenerator; -import org.junit.jupiter.api.Test; - -import java.util.EnumSet; -import java.util.List; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.asteriskjava.core.NewlineDelimiter.CRLF; - -class ComaJoiningSerializerTest { - private final AsteriskGenerator asteriskGenerator = new AsteriskGenerator(CRLF); - - @Test - void shouldSerializeList() { - //given - ComaJoiningSerializer comaJoiningSerializer = new ComaJoiningSerializer(); - - List value = List.of("string1", "string2", "string3"); - - //when - comaJoiningSerializer.serialize("fieldName", value, asteriskGenerator); - - //then - assertThat(asteriskGenerator.generate().trim()).isEqualTo("string1,string2,string3"); - } - - @Test - void shouldSerializeListOfEnums() { - //given - ComaJoiningSerializer comaJoiningSerializer = new ComaJoiningSerializer(); - - EnumSet enums = EnumSet.of(SampleEnum.value1, SampleEnum.value2, SampleEnum.value3); - - //when - comaJoiningSerializer.serialize("fieldName", enums, asteriskGenerator); - - //then - assertThat(asteriskGenerator.generate().trim()).isEqualTo("value1,value2,value3"); - } - - private enum SampleEnum { - value1, - value2, - value3, - } -} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializerTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializerTest.java deleted file mode 100644 index 02c69f9d6..000000000 --- a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/custom/VariableSerializerTest.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2004-2023 Asterisk Java contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.asteriskjava.core.databind.serializer.custom; - -import org.asteriskjava.core.databind.AsteriskGenerator; -import org.junit.jupiter.api.Test; - -import java.util.Map; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.asteriskjava.core.NewlineDelimiter.CRLF; - -class VariableSerializerTest { - private final AsteriskGenerator asteriskGenerator = new AsteriskGenerator(CRLF); - - @Test - void shouldSerializeValues() { - //given - VariableSerializer variableSerializer = new VariableSerializer(); - - Map map = Map.of( - "key1", "value1", - "key2", "value2", - "key3", "value3" - ); - - //when - variableSerializer.serialize("fieldName", map, asteriskGenerator); - - //then - assertThat(asteriskGenerator.generate()) - .contains( - "fieldName: key1=value1", - "fieldName: key2=value2", - "fieldName: key3=value3" - ); - } -} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializerTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializerTest.java deleted file mode 100644 index 0378e957f..000000000 --- a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/serializer/std/ToStringSerializerTest.java +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Copyright 2004-2023 Asterisk Java contributors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.asteriskjava.core.databind.serializer.std; - -import org.asteriskjava.core.databind.AsteriskGenerator; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.asteriskjava.core.NewlineDelimiter.CRLF; -import static org.junit.jupiter.params.provider.Arguments.arguments; - -class ToStringSerializerTest { - private final AsteriskGenerator asteriskGenerator = new AsteriskGenerator(CRLF); - - @ParameterizedTest - @MethodSource("toStringSerializerArguments") - void shouldSerializeUsingToString(Object actual, String expected) { - //given - ToStringSerializer toStringSerializer = new ToStringSerializer(); - - //when - toStringSerializer.serialize("fieldName", actual, asteriskGenerator); - - //then - assertThat(asteriskGenerator.generate().trim()).isEqualTo(expected); - } - - private static Stream toStringSerializerArguments() { - return Stream.of( - arguments("string", "string"), - arguments(true, "true"), - arguments(1.12, "1.12") - ); - } -} diff --git a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/utils/ReflectionUtilsTest.java b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/utils/ReflectionUtilsTest.java index 29f9dccbb..9a42f6f1b 100644 --- a/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/utils/ReflectionUtilsTest.java +++ b/asterisk-java-core/src/test/java/org/asteriskjava/core/databind/utils/ReflectionUtilsTest.java @@ -18,7 +18,6 @@ import org.junit.jupiter.api.Test; import java.lang.reflect.Method; -import java.util.Comparator; import java.util.Map; import java.util.StringJoiner; @@ -138,7 +137,7 @@ public String is() { @Override public String toString() { return new StringJoiner(", ", InvalidClass.class.getSimpleName() + "[", "]") - .toString(); + .toString(); } } @@ -151,15 +150,6 @@ void shouldReturnValidGetters() { assertThat(getters).containsOnlyKeys("Value", "Valid", "Action"); } - @Test - void shouldReturnValidGettersSorted() { - //when - Map getters = getGetters(ValidClass.class, new ActionFieldsComparator()); - - //then - assertThat(getters.keySet()).containsExactly("Action", "Value", "Valid"); - } - static class ValidClass { public String getValue() { @@ -174,14 +164,4 @@ public String getAction() { return "NewAction"; } } - - static class ActionFieldsComparator implements Comparator { - @Override - public int compare(String o1, String o2) { - if (o1.equals(o2)) { - return 0; - } - return o1.equalsIgnoreCase("Action") ? -1 : 1; - } - } } diff --git a/build.gradle b/build.gradle index f7feed549..012774d9c 100644 --- a/build.gradle +++ b/build.gradle @@ -26,6 +26,9 @@ repositories { } dependencies { + implementation project(':asterisk-java-ami') + implementation project(':asterisk-java-core') + implementation 'com.google.guava:guava:32.1.3-jre' implementation 'org.apache.logging.log4j:log4j-core:2.22.0' implementation 'org.reflections:reflections:0.10.2' diff --git a/settings.gradle b/settings.gradle index ccfa47cbf..b974395f0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,3 +1,4 @@ rootProject.name = 'asterisk-java' +include 'asterisk-java-ami' include 'asterisk-java-core' diff --git a/src/main/java/org/asteriskjava/fastagi/internal/AsyncAgiWriter.java b/src/main/java/org/asteriskjava/fastagi/internal/AsyncAgiWriter.java index bfe44849e..68dcfd894 100644 --- a/src/main/java/org/asteriskjava/fastagi/internal/AsyncAgiWriter.java +++ b/src/main/java/org/asteriskjava/fastagi/internal/AsyncAgiWriter.java @@ -1,5 +1,6 @@ package org.asteriskjava.fastagi.internal; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.fastagi.AgiException; import org.asteriskjava.fastagi.AgiWriter; import org.asteriskjava.fastagi.command.AgiCommand; @@ -7,7 +8,6 @@ import org.asteriskjava.manager.TimeoutException; import org.asteriskjava.manager.action.AgiAction; import org.asteriskjava.manager.response.ManagerError; -import org.asteriskjava.manager.response.ManagerResponse; import java.io.IOException; @@ -30,7 +30,7 @@ public AsyncAgiWriter(ManagerConnection connection, String channelName) { public void sendCommand(AgiCommand command) throws AgiException { final AgiAction agiAction; - final ManagerResponse response; + final ManagerActionResponse response; agiAction = new AgiAction(channelName, command.buildCommand()); diff --git a/src/main/java/org/asteriskjava/live/internal/AsteriskChannelImpl.java b/src/main/java/org/asteriskjava/live/internal/AsteriskChannelImpl.java index ff1015708..195d9a094 100644 --- a/src/main/java/org/asteriskjava/live/internal/AsteriskChannelImpl.java +++ b/src/main/java/org/asteriskjava/live/internal/AsteriskChannelImpl.java @@ -16,13 +16,13 @@ */ package org.asteriskjava.live.internal; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.live.*; import org.asteriskjava.lock.LockableList; import org.asteriskjava.lock.LockableMap; import org.asteriskjava.lock.Locker.LockCloser; import org.asteriskjava.manager.action.*; import org.asteriskjava.manager.response.ManagerError; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.util.MixMonitorDirection; import java.util.*; @@ -583,7 +583,7 @@ public void hangup() throws ManagerCommunicationException, NoSuchChannelExceptio public void hangup(HangupCause cause) throws ManagerCommunicationException, NoSuchChannelException { final HangupAction action; - final ManagerResponse response; + final ManagerActionResponse response; if (cause != null) { setVariable(CAUSE_VARIABLE_NAME, Integer.toString(cause.getCode())); @@ -599,7 +599,7 @@ public void hangup(HangupCause cause) throws ManagerCommunicationException, NoSu } public void setAbsoluteTimeout(int seconds) throws ManagerCommunicationException, NoSuchChannelException { - ManagerResponse response; + ManagerActionResponse response; response = server.sendAction(new AbsoluteTimeoutAction(name, seconds)); if (response instanceof ManagerError) { @@ -609,7 +609,7 @@ public void setAbsoluteTimeout(int seconds) throws ManagerCommunicationException public void redirect(String context, String exten, int priority) throws ManagerCommunicationException, NoSuchChannelException { - ManagerResponse response; + ManagerActionResponse response; response = server.sendAction(new RedirectAction(name, context, exten, priority)); if (response instanceof ManagerError) { @@ -619,7 +619,7 @@ public void redirect(String context, String exten, int priority) public void redirectBothLegs(String context, String exten, int priority) throws ManagerCommunicationException, NoSuchChannelException { - ManagerResponse response; + ManagerActionResponse response; try (LockCloser closer = linkedChannels.withLock()) { if (linkedChannels.isEmpty()) { @@ -636,7 +636,7 @@ public void redirectBothLegs(String context, String exten, int priority) } public String getVariable(String variable) throws ManagerCommunicationException, NoSuchChannelException { - ManagerResponse response; + ManagerActionResponse response; String value; try (LockCloser closer = variables.withLock()) { @@ -661,7 +661,7 @@ public String getVariable(String variable) throws ManagerCommunicationException, } public void setVariable(String variable, String value) throws ManagerCommunicationException, NoSuchChannelException { - ManagerResponse response; + ManagerActionResponse response; response = server.sendAction(new SetVarAction(name, variable, value)); if (response instanceof ManagerError) { @@ -673,7 +673,7 @@ public void setVariable(String variable, String value) throws ManagerCommunicati } public void playDtmf(String digit) throws ManagerCommunicationException, NoSuchChannelException, IllegalArgumentException { - ManagerResponse response; + ManagerActionResponse response; if (digit == null) { throw new IllegalArgumentException("DTMF digit to send must not be null"); @@ -695,7 +695,7 @@ public void startMonitoring(String filename, String format) throws ManagerCommun public void startMonitoring(String filename, String format, boolean mix) throws ManagerCommunicationException, NoSuchChannelException { - ManagerResponse response; + ManagerActionResponse response; response = server.sendAction(new MonitorAction(name, filename, format, mix)); if (response instanceof ManagerError) { @@ -705,7 +705,7 @@ public void startMonitoring(String filename, String format, boolean mix) public void changeMonitoring(String filename) throws ManagerCommunicationException, NoSuchChannelException, IllegalArgumentException { - ManagerResponse response; + ManagerActionResponse response; if (filename == null) { throw new IllegalArgumentException("New filename must not be null"); @@ -718,7 +718,7 @@ public void changeMonitoring(String filename) } public void stopMonitoring() throws ManagerCommunicationException, NoSuchChannelException { - ManagerResponse response; + ManagerActionResponse response; response = server.sendAction(new StopMonitorAction(name)); if (response instanceof ManagerError) { @@ -727,7 +727,7 @@ public void stopMonitoring() throws ManagerCommunicationException, NoSuchChannel } public void pauseMonitoring() throws ManagerCommunicationException, NoSuchChannelException { - ManagerResponse response; + ManagerActionResponse response; response = server.sendAction(new PauseMonitorAction(name)); if (response instanceof ManagerError) { @@ -736,7 +736,7 @@ public void pauseMonitoring() throws ManagerCommunicationException, NoSuchChanne } public void unpauseMonitoring() throws ManagerCommunicationException, NoSuchChannelException { - ManagerResponse response; + ManagerActionResponse response; response = server.sendAction(new UnpauseMonitorAction(name)); if (response instanceof ManagerError) { @@ -746,7 +746,7 @@ public void unpauseMonitoring() throws ManagerCommunicationException, NoSuchChan public void pauseMixMonitor(MixMonitorDirection direction) throws ManagerCommunicationException, NoSuchChannelException, RecordingException { - ManagerResponse response; + ManagerActionResponse response; response = server.sendAction(new PauseMixMonitorAction(this.name, 1, direction.getStateName())); if (response instanceof ManagerError) { if (response.getMessage().equals("Cannot set mute flag")) { @@ -758,7 +758,7 @@ public void pauseMixMonitor(MixMonitorDirection direction) public void unPauseMixMonitor(MixMonitorDirection direction) throws ManagerCommunicationException, NoSuchChannelException, RecordingException { - ManagerResponse response; + ManagerActionResponse response; response = server.sendAction(new PauseMixMonitorAction(this.name, 0, direction.getStateName())); if (response instanceof ManagerError) { if (response.getMessage().equals("Cannot set mute flag")) { diff --git a/src/main/java/org/asteriskjava/live/internal/AsteriskQueueMemberImpl.java b/src/main/java/org/asteriskjava/live/internal/AsteriskQueueMemberImpl.java index e37a44f61..1e754594a 100644 --- a/src/main/java/org/asteriskjava/live/internal/AsteriskQueueMemberImpl.java +++ b/src/main/java/org/asteriskjava/live/internal/AsteriskQueueMemberImpl.java @@ -16,12 +16,12 @@ */ package org.asteriskjava.live.internal; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.live.*; import org.asteriskjava.lock.Locker.LockCloser; import org.asteriskjava.manager.action.QueuePauseAction; import org.asteriskjava.manager.action.QueuePenaltyAction; import org.asteriskjava.manager.response.ManagerError; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.util.AstUtil; /** @@ -123,7 +123,7 @@ public void setPausedAll(boolean paused) throws ManagerCommunicationException, N } private void sendPauseAction(QueuePauseAction action) throws ManagerCommunicationException, NoSuchInterfaceException { - final ManagerResponse response = server.sendAction(action); + final ManagerActionResponse response = server.sendAction(action); if (response instanceof ManagerError) { // Message: Interface not found @@ -159,7 +159,7 @@ public void setPenalty(int penalty) throw new IllegalArgumentException("Penalty must not be negative"); } - final ManagerResponse response = server.sendAction(new QueuePenaltyAction(location, penalty, queue.getName())); + final ManagerActionResponse response = server.sendAction(new QueuePenaltyAction(location, penalty, queue.getName())); if (response instanceof ManagerError) { throw new InvalidPenaltyException( "Unable to set penalty for '" + location + "' on '" + queue.getName() + "': " + response.getMessage()); diff --git a/src/main/java/org/asteriskjava/live/internal/AsteriskServerImpl.java b/src/main/java/org/asteriskjava/live/internal/AsteriskServerImpl.java index 7062e5ad7..830e333ad 100644 --- a/src/main/java/org/asteriskjava/live/internal/AsteriskServerImpl.java +++ b/src/main/java/org/asteriskjava/live/internal/AsteriskServerImpl.java @@ -17,6 +17,8 @@ package org.asteriskjava.live.internal; import org.asteriskjava.AsteriskVersion; +import org.asteriskjava.ami.action.ManagerAction; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.config.ConfigFile; import org.asteriskjava.live.*; import org.asteriskjava.lock.Lockable; @@ -431,7 +433,7 @@ public List getQueuesUpdatedAfter(Date date) { public String getVersion() throws ManagerCommunicationException { try (LockCloser closer = this.withLock()) { - final ManagerResponse response; + final ManagerActionResponse response; final String command; initializeIfNeeded(); @@ -467,7 +469,7 @@ public int[] getVersion(String file) throws ManagerCommunicationException { initializeIfNeeded(); if (versions == null) { LockableMap map; - ManagerResponse response; + ManagerActionResponse response; map = new LockableMap<>(new HashMap<>()); try { @@ -530,7 +532,7 @@ public int[] getVersion(String file) throws ManagerCommunicationException { } public String getGlobalVariable(String variable) throws ManagerCommunicationException { - ManagerResponse response; + ManagerActionResponse response; String value; initializeIfNeeded(); @@ -546,7 +548,7 @@ public String getGlobalVariable(String variable) throws ManagerCommunicationExce } public void setGlobalVariable(String variable, String value) throws ManagerCommunicationException { - ManagerResponse response; + ManagerActionResponse response; initializeIfNeeded(); response = sendAction(new SetVarAction(variable, value)); @@ -557,7 +559,7 @@ public void setGlobalVariable(String variable, String value) throws ManagerCommu public Collection getVoicemailboxes() throws ManagerCommunicationException { final Collection voicemailboxes; - ManagerResponse response; + ManagerActionResponse response; final List result; initializeIfNeeded(); @@ -622,7 +624,7 @@ public Collection getVoicemailboxes() throws ManagerCommunicationE } public List executeCliCommand(String command) throws ManagerCommunicationException { - final ManagerResponse response; + final ManagerActionResponse response; initializeIfNeeded(); response = sendAction(new CommandAction(command)); @@ -655,7 +657,7 @@ public void reloadAllModules() throws ManagerCommunicationException { } protected void sendModuleLoadAction(String module, String loadType) throws ManagerCommunicationException { - final ManagerResponse response; + final ManagerActionResponse response; response = sendAction(new ModuleLoadAction(module, loadType)); if (response instanceof ManagerError) { @@ -665,7 +667,7 @@ protected void sendModuleLoadAction(String module, String loadType) throws Manag } public ConfigFile getConfig(String filename) throws ManagerCommunicationException { - final ManagerResponse response; + final ManagerActionResponse response; final GetConfigResponse getConfigResponse; initializeIfNeeded(); @@ -753,7 +755,7 @@ void fireNewMeetMeUser(MeetMeUser user) { } } - ManagerResponse sendActionOnEventConnection(ManagerAction action) throws ManagerCommunicationException { + ManagerActionResponse sendActionOnEventConnection(ManagerAction action) throws ManagerCommunicationException { try { return eventConnection.sendAction(action); } catch (Exception e) { @@ -761,7 +763,7 @@ ManagerResponse sendActionOnEventConnection(ManagerAction action) throws Manager } } - ManagerResponse sendAction(ManagerAction action) throws ManagerCommunicationException { + ManagerActionResponse sendAction(ManagerAction action) throws ManagerCommunicationException { // return connectionPool.sendAction(action); try { return eventConnection.sendAction(action); diff --git a/src/main/java/org/asteriskjava/live/internal/ManagerCommunicationExceptionMapper.java b/src/main/java/org/asteriskjava/live/internal/ManagerCommunicationExceptionMapper.java index 2ed537a59..ff102415c 100644 --- a/src/main/java/org/asteriskjava/live/internal/ManagerCommunicationExceptionMapper.java +++ b/src/main/java/org/asteriskjava/live/internal/ManagerCommunicationExceptionMapper.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.live.internal; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.live.ManagerCommunicationException; import org.asteriskjava.manager.EventTimeoutException; @@ -36,7 +37,7 @@ private ManagerCommunicationExceptionMapper() { /** * Maps exceptions received from * {@link org.asteriskjava.manager.ManagerConnection} when sending a - * {@link org.asteriskjava.manager.action.ManagerAction} to the corresponding + * {@link ManagerAction} to the corresponding * {@link org.asteriskjava.live.ManagerCommunicationException}. * * @param actionName name of the action that has been tried to send diff --git a/src/main/java/org/asteriskjava/live/internal/MeetMeManager.java b/src/main/java/org/asteriskjava/live/internal/MeetMeManager.java index 42f2230cc..983598adb 100644 --- a/src/main/java/org/asteriskjava/live/internal/MeetMeManager.java +++ b/src/main/java/org/asteriskjava/live/internal/MeetMeManager.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.live.internal; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.live.ManagerCommunicationException; import org.asteriskjava.live.MeetMeRoom; import org.asteriskjava.lock.LockableMap; @@ -27,7 +28,6 @@ import org.asteriskjava.manager.event.MeetMeTalkingEvent; import org.asteriskjava.manager.response.CommandResponse; import org.asteriskjava.manager.response.ManagerError; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.util.DateUtil; import org.asteriskjava.util.Log; import org.asteriskjava.util.LogFactory; @@ -156,7 +156,7 @@ void handleMeetMeEvent(AbstractMeetMeEvent event) { private void populateRoom(MeetMeRoomImpl room) { final CommandAction meetMeListAction; - final ManagerResponse response; + final ManagerActionResponse response; final List lines; final Collection userNumbers = new ArrayList<>(); // list // of diff --git a/src/main/java/org/asteriskjava/manager/DefaultManagerConnection.java b/src/main/java/org/asteriskjava/manager/DefaultManagerConnection.java index e7c10501e..244d176ba 100644 --- a/src/main/java/org/asteriskjava/manager/DefaultManagerConnection.java +++ b/src/main/java/org/asteriskjava/manager/DefaultManagerConnection.java @@ -17,11 +17,11 @@ package org.asteriskjava.manager; import org.asteriskjava.AsteriskVersion; +import org.asteriskjava.ami.action.ManagerAction; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.action.EventGeneratingAction; -import org.asteriskjava.manager.action.ManagerAction; import org.asteriskjava.manager.event.ManagerEvent; import org.asteriskjava.manager.internal.ManagerConnectionImpl; -import org.asteriskjava.manager.response.ManagerResponse; import java.io.IOException; import java.net.InetAddress; @@ -288,12 +288,12 @@ public void logoff() throws IllegalStateException { impl.logoff(); } - public ManagerResponse sendAction(ManagerAction action) + public ManagerActionResponse sendAction(ManagerAction action) throws IOException, TimeoutException, IllegalArgumentException, IllegalStateException { return impl.sendAction(action); } - public ManagerResponse sendAction(ManagerAction action, long timeout) + public ManagerActionResponse sendAction(ManagerAction action, long timeout) throws IOException, TimeoutException, IllegalArgumentException, IllegalStateException { return impl.sendAction(action, timeout); } diff --git a/src/main/java/org/asteriskjava/manager/ExpectedResponse.java b/src/main/java/org/asteriskjava/manager/ExpectedResponse.java deleted file mode 100644 index da5c836c6..000000000 --- a/src/main/java/org/asteriskjava/manager/ExpectedResponse.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.asteriskjava.manager; - -import org.asteriskjava.manager.response.ManagerResponse; - -import java.lang.annotation.Retention; -import java.lang.annotation.Target; - -import static java.lang.annotation.ElementType.TYPE; -import static java.lang.annotation.RetentionPolicy.RUNTIME; - -/** - * Indicates that an annotated {@link org.asteriskjava.manager.action.ManagerAction} expects - * a specific subclass of {@link org.asteriskjava.manager.response.ManagerResponse} when executed - * successfully. - * - * @since 1.0.0 - */ -@Target(TYPE) -@Retention(RUNTIME) -public @interface ExpectedResponse { - Class value(); -} diff --git a/src/main/java/org/asteriskjava/manager/ManagerConnection.java b/src/main/java/org/asteriskjava/manager/ManagerConnection.java index 1fdbc8e42..e321f92ad 100644 --- a/src/main/java/org/asteriskjava/manager/ManagerConnection.java +++ b/src/main/java/org/asteriskjava/manager/ManagerConnection.java @@ -17,10 +17,11 @@ package org.asteriskjava.manager; import org.asteriskjava.AsteriskVersion; +import org.asteriskjava.ami.action.ChallengeAction; +import org.asteriskjava.ami.action.ManagerAction; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.action.EventGeneratingAction; -import org.asteriskjava.manager.action.ManagerAction; import org.asteriskjava.manager.event.ManagerEvent; -import org.asteriskjava.manager.response.ManagerResponse; import java.io.IOException; import java.net.InetAddress; @@ -31,8 +32,8 @@ * API. *

* The ManagerConnection repesents a connection to an Asterisk server and is - * capable of sending {@link org.asteriskjava.manager.action.ManagerAction}s and - * receiving {@link org.asteriskjava.manager.response.ManagerResponse}s and + * capable of sending {@link ManagerAction}s and + * receiving {@link ManagerActionResponse}s and * {@link org.asteriskjava.manager.event.ManagerEvent}s. It does not add any * further functionality but rather provides a Java view to Asterisk's Manager * API (freeing you from TCP/IP connection and parsing stuff). @@ -230,7 +231,7 @@ public interface ManagerConnection { * @throws TimeoutException if a timeout occurs while waiting for the * protocol identifier. The connection is closed in this case. * @see org.asteriskjava.manager.action.LoginAction - * @see org.asteriskjava.manager.action.ChallengeAction + * @see ChallengeAction */ void login() throws IllegalStateException, IOException, AuthenticationFailedException, TimeoutException; @@ -250,7 +251,7 @@ public interface ManagerConnection { * @throws TimeoutException if a timeout occurs while waiting for the * protocol identifier. The connection is closed in this case. * @see org.asteriskjava.manager.action.LoginAction - * @see org.asteriskjava.manager.action.ChallengeAction + * @see ChallengeAction * @since 0.3 */ void login(String events) throws IllegalStateException, IOException, AuthenticationFailedException, TimeoutException; @@ -294,12 +295,12 @@ public interface ManagerConnection { * @see #sendAction(ManagerAction, long) * @see #sendAction(ManagerAction, SendActionCallback) */ - ManagerResponse sendAction(ManagerAction action) + ManagerActionResponse sendAction(ManagerAction action) throws IOException, TimeoutException, IllegalArgumentException, IllegalStateException; /** * Sends a ManagerAction to the Asterisk server and waits for the - * corresponding {@link ManagerResponse}. + * corresponding {@link ManagerActionResponse}. * * @param action the action to send to the Asterisk server * @param timeout milliseconds to wait for the response before throwing a @@ -313,12 +314,12 @@ ManagerResponse sendAction(ManagerAction action) * server. * @see #sendAction(ManagerAction, SendActionCallback) */ - ManagerResponse sendAction(ManagerAction action, long timeout) + ManagerActionResponse sendAction(ManagerAction action, long timeout) throws IOException, TimeoutException, IllegalArgumentException, IllegalStateException; /** * Sends a ManagerAction to the Asterisk server and registers a callback - * handler to be called when the corresponding {@link ManagerResponse} is + * handler to be called when the corresponding {@link ManagerActionResponse} is * received. Be very careful that your callbackHandler terminates very * quickly and does not do any fancy processing because it is called from * the reader thread which is blocked for the time it takes to execute your @@ -338,11 +339,11 @@ void sendAction(ManagerAction action, SendActionCallback callback) /** * Sends an {@link EventGeneratingAction} to the Asterisk server and waits - * for the corresponding {@link ManagerResponse} and the + * for the corresponding {@link ManagerActionResponse} and the * {@link org.asteriskjava.manager.event.ResponseEvent}s *

* EventGeneratingActions are {@link ManagerAction}s that don't return their - * response in the corresponding {@link ManagerResponse} but send a series + * response in the corresponding {@link ManagerActionResponse} but send a series * of events that contain the payload. *

* This method will block until the correpsonding action complete event has @@ -375,11 +376,11 @@ ResponseEvents sendEventGeneratingAction(EventGeneratingAction action) /** * Sends an {@link EventGeneratingAction} to the Asterisk server and waits - * for the corresponding {@link ManagerResponse} and the + * for the corresponding {@link ManagerActionResponse} and the * {@link org.asteriskjava.manager.event.ResponseEvent}s *

* EventGeneratingActions are {@link ManagerAction}s that don't return their - * response in the corresponding {@link ManagerResponse} but send a series + * response in the corresponding {@link ManagerActionResponse} but send a series * of events that contain the payload. *

* This method will block until the correpsonding action complete event has diff --git a/src/main/java/org/asteriskjava/manager/ManagerConnectionFactory.java b/src/main/java/org/asteriskjava/manager/ManagerConnectionFactory.java index 9417c96bd..0a4c2963a 100644 --- a/src/main/java/org/asteriskjava/manager/ManagerConnectionFactory.java +++ b/src/main/java/org/asteriskjava/manager/ManagerConnectionFactory.java @@ -16,13 +16,15 @@ */ package org.asteriskjava.manager; +import org.asteriskjava.ami.action.ManagerAction; + /** * This factory is the canonical way to obtain new * {@link org.asteriskjava.manager.ManagerConnection}s.

* It creates new connections in state * {@link org.asteriskjava.manager.ManagerConnectionState#INITIAL}. Before * you can start using such a connection (i.e. sending - * {@link org.asteriskjava.manager.action.ManagerAction}s you must + * {@link ManagerAction}s you must * {@link org.asteriskjava.manager.ManagerConnection#login()} to change its state * to {@link org.asteriskjava.manager.ManagerConnectionState#CONNECTED}.

* Example: diff --git a/src/main/java/org/asteriskjava/manager/PingThread.java b/src/main/java/org/asteriskjava/manager/PingThread.java index 862f882be..b26dd4cf7 100644 --- a/src/main/java/org/asteriskjava/manager/PingThread.java +++ b/src/main/java/org/asteriskjava/manager/PingThread.java @@ -16,10 +16,10 @@ */ package org.asteriskjava.manager; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.lock.LockableSet; import org.asteriskjava.lock.Locker.LockCloser; import org.asteriskjava.manager.action.PingAction; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.util.Log; import org.asteriskjava.util.LogFactory; @@ -170,7 +170,7 @@ protected void ping(ManagerConnection c) { if (timeout <= 0) { c.sendAction(new PingAction(), null); } else { - final ManagerResponse response; + final ManagerActionResponse response; response = c.sendAction(new PingAction(), timeout); logger.debug("Ping response '" + response + "' for " + c.toString()); diff --git a/src/main/java/org/asteriskjava/manager/ResponseEvents.java b/src/main/java/org/asteriskjava/manager/ResponseEvents.java index a271d733f..20b9a53ee 100644 --- a/src/main/java/org/asteriskjava/manager/ResponseEvents.java +++ b/src/main/java/org/asteriskjava/manager/ResponseEvents.java @@ -16,8 +16,8 @@ */ package org.asteriskjava.manager; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.event.ResponseEvent; -import org.asteriskjava.manager.response.ManagerResponse; import java.util.Collection; @@ -25,7 +25,7 @@ /** * Contains the result of executing an * {@link org.asteriskjava.manager.action.EventGeneratingAction}, that is the - * {@link org.asteriskjava.manager.response.ManagerResponse} and any received + * {@link ManagerActionResponse} and any received * {@link org.asteriskjava.manager.event.ManagerEvent}s. * * @author srt @@ -39,7 +39,7 @@ public interface ResponseEvents { * * @return the response received. */ - ManagerResponse getResponse(); + ManagerActionResponse getResponse(); /** * Returns a Collection of ManagerEvents that have been received including diff --git a/src/main/java/org/asteriskjava/manager/SendActionCallback.java b/src/main/java/org/asteriskjava/manager/SendActionCallback.java index 0e315621c..da8ad498a 100644 --- a/src/main/java/org/asteriskjava/manager/SendActionCallback.java +++ b/src/main/java/org/asteriskjava/manager/SendActionCallback.java @@ -16,11 +16,11 @@ */ package org.asteriskjava.manager; -import org.asteriskjava.manager.action.ManagerAction; -import org.asteriskjava.manager.response.ManagerResponse; +import org.asteriskjava.ami.action.ManagerAction; +import org.asteriskjava.ami.action.response.ManagerActionResponse; /** - * Callback interface to send {@link org.asteriskjava.manager.action.ManagerAction}s + * Callback interface to send {@link ManagerAction}s * asynchronously. * * @author srt @@ -33,5 +33,5 @@ public interface SendActionCallback { * * @param response the response that has been received */ - void onResponse(ManagerResponse response); + void onResponse(ManagerActionResponse response); } diff --git a/src/main/java/org/asteriskjava/manager/SendEventGeneratingActionCallback.java b/src/main/java/org/asteriskjava/manager/SendEventGeneratingActionCallback.java index 528e7d643..437c049ed 100644 --- a/src/main/java/org/asteriskjava/manager/SendEventGeneratingActionCallback.java +++ b/src/main/java/org/asteriskjava/manager/SendEventGeneratingActionCallback.java @@ -1,7 +1,7 @@ package org.asteriskjava.manager; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.response.ManagerError; -import org.asteriskjava.manager.response.ManagerResponse; /** * Callback interface to send @@ -9,8 +9,8 @@ * * @see org.asteriskjava.manager.ManagerConnection#sendEventGeneratingAction(org.asteriskjava.manager.action.EventGeneratingAction, SendEventGeneratingActionCallback) *

- * Initial response is passed to one of {@link #onResponse(ManagerResponse)} or - * {@link #onErrorResponse(ManagerResponse)}. but not both. + * Initial response is passed to one of {@link #onResponse(ManagerActionResponse)} or + * {@link #onErrorResponse(ManagerActionResponse)}. but not both. */ public interface SendEventGeneratingActionCallback { /** diff --git a/src/main/java/org/asteriskjava/manager/action/AbsoluteTimeoutAction.java b/src/main/java/org/asteriskjava/manager/action/AbsoluteTimeoutAction.java index eb6ec8830..74abe9faf 100644 --- a/src/main/java/org/asteriskjava/manager/action/AbsoluteTimeoutAction.java +++ b/src/main/java/org/asteriskjava/manager/action/AbsoluteTimeoutAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The AbsoluteTimeoutAction sets the absolute maximum amount of time permitted * for a call on a given channel, it hangs up the channel after a certain time. diff --git a/src/main/java/org/asteriskjava/manager/action/AbstractManagerAction.java b/src/main/java/org/asteriskjava/manager/action/AbstractManagerAction.java deleted file mode 100644 index 7280ecda2..000000000 --- a/src/main/java/org/asteriskjava/manager/action/AbstractManagerAction.java +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2004-2006 Stefan Reuter - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.asteriskjava.manager.action; - -import org.asteriskjava.util.ReflectionUtil; - -import java.lang.reflect.Method; -import java.util.Map; - -/** - * This class implements the ManagerAction interface and can serve as base class - * for your concrete Action implementations. - * - * @author srt - * @version $Id$ - * @since 0.2 - */ -public abstract class AbstractManagerAction implements ManagerAction { - /** - * Serializable version identifier. - */ - static final long serialVersionUID = -7667827187378395689L; - - private String actionId; - - public abstract String getAction(); - - public String getActionId() { - return actionId; - } - - public void setActionId(String actionId) { - this.actionId = actionId; - } - - @Override - public String toString() { - StringBuilder sb; - Map getters; - - sb = new StringBuilder(getClass().getName() + "["); - sb.append("action='").append(getAction()).append("',"); - getters = ReflectionUtil.getGetters(getClass()); - for (Map.Entry entry : getters.entrySet()) { - final String attribute = entry.getKey(); - if ("action".equals(attribute) || "class".equals(attribute)) { - continue; - } - - try { - Object value; - value = entry.getValue().invoke(this); - sb.append(attribute).append("='").append(value).append("',"); - } catch (Exception e) // NOPMD - { - // swallow - } - } - sb.append("systemHashcode=").append(System.identityHashCode(this)); - sb.append("]"); - - return sb.toString(); - } -} diff --git a/src/main/java/org/asteriskjava/manager/action/AbstractMeetMeMuteAction.java b/src/main/java/org/asteriskjava/manager/action/AbstractMeetMeMuteAction.java index 684cc562b..eb4b8e033 100644 --- a/src/main/java/org/asteriskjava/manager/action/AbstractMeetMeMuteAction.java +++ b/src/main/java/org/asteriskjava/manager/action/AbstractMeetMeMuteAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * Abstract base class for mute and unmute actions. * diff --git a/src/main/java/org/asteriskjava/manager/action/AgentCallbackLoginAction.java b/src/main/java/org/asteriskjava/manager/action/AgentCallbackLoginAction.java index 10598f07e..e89f45cf1 100644 --- a/src/main/java/org/asteriskjava/manager/action/AgentCallbackLoginAction.java +++ b/src/main/java/org/asteriskjava/manager/action/AgentCallbackLoginAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The AgentCallbackLoginAction sets an agent as logged in with callback.

* You can pass an extentsion (and optionally a context) to specify the diff --git a/src/main/java/org/asteriskjava/manager/action/AgentLogoffAction.java b/src/main/java/org/asteriskjava/manager/action/AgentLogoffAction.java index 940bbe1c4..b9acc9274 100644 --- a/src/main/java/org/asteriskjava/manager/action/AgentLogoffAction.java +++ b/src/main/java/org/asteriskjava/manager/action/AgentLogoffAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The AgentLogoffAction sets an agent as no longer logged in.

* Available since Asterisk 1.2 diff --git a/src/main/java/org/asteriskjava/manager/action/AgentsAction.java b/src/main/java/org/asteriskjava/manager/action/AgentsAction.java index 2077b93ce..8619fff33 100644 --- a/src/main/java/org/asteriskjava/manager/action/AgentsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/AgentsAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.AgentsCompleteEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/AgiAction.java b/src/main/java/org/asteriskjava/manager/action/AgiAction.java index 014cefd15..326909881 100644 --- a/src/main/java/org/asteriskjava/manager/action/AgiAction.java +++ b/src/main/java/org/asteriskjava/manager/action/AgiAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.AsyncAgiEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/AtxferAction.java b/src/main/java/org/asteriskjava/manager/action/AtxferAction.java index 12b08fc5d..7054962be 100644 --- a/src/main/java/org/asteriskjava/manager/action/AtxferAction.java +++ b/src/main/java/org/asteriskjava/manager/action/AtxferAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * Starts an attended transfer. This action seems to be a bit of a hack. See * http://bugs.digium.com/view.php?id=12158 diff --git a/src/main/java/org/asteriskjava/manager/action/BridgeAction.java b/src/main/java/org/asteriskjava/manager/action/BridgeAction.java index 17121eeda..395bcb3be 100644 --- a/src/main/java/org/asteriskjava/manager/action/BridgeAction.java +++ b/src/main/java/org/asteriskjava/manager/action/BridgeAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The BridgeAction bridges two channels that are currently active on the system.

* It is definied in res/res_features.c.

diff --git a/src/main/java/org/asteriskjava/manager/action/ChallengeAction.java b/src/main/java/org/asteriskjava/manager/action/ChallengeAction.java deleted file mode 100644 index 9b0f0a993..000000000 --- a/src/main/java/org/asteriskjava/manager/action/ChallengeAction.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2004-2006 Stefan Reuter - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.asteriskjava.manager.action; - -import org.asteriskjava.manager.ExpectedResponse; -import org.asteriskjava.manager.response.ChallengeResponse; - -/** - * The ChallengeAction requests a challenge from the server to use when logging - * in using challenge/response. Sending this action to the asterisk server - * results in a ChallengeResponse being received from the server. - * - * @author srt - * @version $Id$ - * @see org.asteriskjava.manager.action.LoginAction - * @see org.asteriskjava.manager.response.ChallengeResponse - */ -@ExpectedResponse(ChallengeResponse.class) -public class ChallengeAction extends AbstractManagerAction { - static final long serialVersionUID = 7240516124871953971L; - private String authType; - - /** - * Creates a new empty ChallengeAction. - */ - public ChallengeAction() { - - } - - /** - * Creates a new ChallengeAction that requests a new login challenge for use - * with the given digest algorithm. - * - * @param authType the digest alogrithm to use. - * @since 0.2 - */ - public ChallengeAction(String authType) { - this.authType = authType; - } - - /** - * Returns Returns the name of this action, i.e. "Challenge". - */ - @Override - public String getAction() { - return "Challenge"; - } - - /** - * Returns the digest alogrithm to use. - */ - public String getAuthType() { - return authType; - } - - /** - * Sets the digest alogrithm to use. Currently asterisk only supports "MD5". - */ - public void setAuthType(String authType) { - this.authType = authType; - } -} diff --git a/src/main/java/org/asteriskjava/manager/action/ChangeMonitorAction.java b/src/main/java/org/asteriskjava/manager/action/ChangeMonitorAction.java index 1947eadcc..c0242123f 100644 --- a/src/main/java/org/asteriskjava/manager/action/ChangeMonitorAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ChangeMonitorAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ChangeMonitorAction changes the monitoring filename of a channel. It has * no effect if the channel is not monitored.

diff --git a/src/main/java/org/asteriskjava/manager/action/CommandAction.java b/src/main/java/org/asteriskjava/manager/action/CommandAction.java index 118d8acbd..9e90ef49f 100644 --- a/src/main/java/org/asteriskjava/manager/action/CommandAction.java +++ b/src/main/java/org/asteriskjava/manager/action/CommandAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.CommandResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/ConfbridgeKickAction.java b/src/main/java/org/asteriskjava/manager/action/ConfbridgeKickAction.java index eb7527d86..0e6a56528 100644 --- a/src/main/java/org/asteriskjava/manager/action/ConfbridgeKickAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ConfbridgeKickAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ConfbridgeKickAction kicks a channel out of a conference. * diff --git a/src/main/java/org/asteriskjava/manager/action/ConfbridgeListAction.java b/src/main/java/org/asteriskjava/manager/action/ConfbridgeListAction.java index e22d06589..9a015b4ca 100644 --- a/src/main/java/org/asteriskjava/manager/action/ConfbridgeListAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ConfbridgeListAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.ConfbridgeListCompleteEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/ConfbridgeListRoomsAction.java b/src/main/java/org/asteriskjava/manager/action/ConfbridgeListRoomsAction.java index 024fb6188..e79d9ee3a 100644 --- a/src/main/java/org/asteriskjava/manager/action/ConfbridgeListRoomsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ConfbridgeListRoomsAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.ConfbridgeListRoomsCompleteEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/ConfbridgeLockAction.java b/src/main/java/org/asteriskjava/manager/action/ConfbridgeLockAction.java index c55b9bfb1..c5e082f7a 100644 --- a/src/main/java/org/asteriskjava/manager/action/ConfbridgeLockAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ConfbridgeLockAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ConfbridgeLockAction lockes a specified conference. * diff --git a/src/main/java/org/asteriskjava/manager/action/ConfbridgeMuteAction.java b/src/main/java/org/asteriskjava/manager/action/ConfbridgeMuteAction.java index 86c688cce..7867d6fae 100644 --- a/src/main/java/org/asteriskjava/manager/action/ConfbridgeMuteAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ConfbridgeMuteAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ConfbridgeMuteAction mutes a channel in a conference. * diff --git a/src/main/java/org/asteriskjava/manager/action/ConfbridgeSetSingleVideoSrcAction.java b/src/main/java/org/asteriskjava/manager/action/ConfbridgeSetSingleVideoSrcAction.java index 7d6f33e29..c99877082 100644 --- a/src/main/java/org/asteriskjava/manager/action/ConfbridgeSetSingleVideoSrcAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ConfbridgeSetSingleVideoSrcAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ConfbridgeSetSingleVideoSrcAction sets a conference user as the single video source distributed to all other video-capable participants. * diff --git a/src/main/java/org/asteriskjava/manager/action/ConfbridgeStartRecordAction.java b/src/main/java/org/asteriskjava/manager/action/ConfbridgeStartRecordAction.java index ff92772c1..93d009a89 100644 --- a/src/main/java/org/asteriskjava/manager/action/ConfbridgeStartRecordAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ConfbridgeStartRecordAction.java @@ -15,6 +15,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ConfbridgeStartAction starts an audio recording of a conference. * diff --git a/src/main/java/org/asteriskjava/manager/action/ConfbridgeStopRecordAction.java b/src/main/java/org/asteriskjava/manager/action/ConfbridgeStopRecordAction.java index 2a5d8c6ec..d7993d36f 100644 --- a/src/main/java/org/asteriskjava/manager/action/ConfbridgeStopRecordAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ConfbridgeStopRecordAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ConfbridgeStopAction stops an audio recording of a conference. * diff --git a/src/main/java/org/asteriskjava/manager/action/ConfbridgeUnlockAction.java b/src/main/java/org/asteriskjava/manager/action/ConfbridgeUnlockAction.java index fb91b522f..8598c76c0 100644 --- a/src/main/java/org/asteriskjava/manager/action/ConfbridgeUnlockAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ConfbridgeUnlockAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ConfbridgeUnlockAction unlocks a specified conference. * diff --git a/src/main/java/org/asteriskjava/manager/action/ConfbridgeUnmuteAction.java b/src/main/java/org/asteriskjava/manager/action/ConfbridgeUnmuteAction.java index f6a4845ff..eb57d8e1b 100644 --- a/src/main/java/org/asteriskjava/manager/action/ConfbridgeUnmuteAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ConfbridgeUnmuteAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ConfbridgeUnmuteAction unmutes a channel in a conference. * diff --git a/src/main/java/org/asteriskjava/manager/action/CoreSettingsAction.java b/src/main/java/org/asteriskjava/manager/action/CoreSettingsAction.java index 0a81b36b2..b16db8980 100644 --- a/src/main/java/org/asteriskjava/manager/action/CoreSettingsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/CoreSettingsAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.CoreSettingsResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/CoreShowChannelsAction.java b/src/main/java/org/asteriskjava/manager/action/CoreShowChannelsAction.java index b6210c2c2..b090d0c33 100644 --- a/src/main/java/org/asteriskjava/manager/action/CoreShowChannelsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/CoreShowChannelsAction.java @@ -17,6 +17,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.CoreShowChannelsCompleteEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/CoreStatusAction.java b/src/main/java/org/asteriskjava/manager/action/CoreStatusAction.java index 7a178d5ed..fc90e99dc 100644 --- a/src/main/java/org/asteriskjava/manager/action/CoreStatusAction.java +++ b/src/main/java/org/asteriskjava/manager/action/CoreStatusAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.CoreStatusResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/DahdiShowChannelsAction.java b/src/main/java/org/asteriskjava/manager/action/DahdiShowChannelsAction.java index a52b21cbd..f08014abc 100644 --- a/src/main/java/org/asteriskjava/manager/action/DahdiShowChannelsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/DahdiShowChannelsAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.DahdiShowChannelsCompleteEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/DbDelAction.java b/src/main/java/org/asteriskjava/manager/action/DbDelAction.java index 528424fe0..f2435efa7 100644 --- a/src/main/java/org/asteriskjava/manager/action/DbDelAction.java +++ b/src/main/java/org/asteriskjava/manager/action/DbDelAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * Deletes an entry in the Asterisk database for a given family and key.

* Available since Asterisk 1.2 with BRIStuff patches and since Asterisk 1.6 diff --git a/src/main/java/org/asteriskjava/manager/action/DbDelTreeAction.java b/src/main/java/org/asteriskjava/manager/action/DbDelTreeAction.java index 6b21c94d4..ae47f72ff 100644 --- a/src/main/java/org/asteriskjava/manager/action/DbDelTreeAction.java +++ b/src/main/java/org/asteriskjava/manager/action/DbDelTreeAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * Recursivly deletes entries in the Asterisk database for a given family and key.

* Available since Asterisk 1.6 diff --git a/src/main/java/org/asteriskjava/manager/action/DbGetAction.java b/src/main/java/org/asteriskjava/manager/action/DbGetAction.java index 591abf8b0..cfa8670f7 100644 --- a/src/main/java/org/asteriskjava/manager/action/DbGetAction.java +++ b/src/main/java/org/asteriskjava/manager/action/DbGetAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.DbGetResponseEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/DbPutAction.java b/src/main/java/org/asteriskjava/manager/action/DbPutAction.java index d91242c50..ccfb9c52c 100644 --- a/src/main/java/org/asteriskjava/manager/action/DbPutAction.java +++ b/src/main/java/org/asteriskjava/manager/action/DbPutAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * Adds or updates an entry in the Asterisk database for a given family, key, * and value.

diff --git a/src/main/java/org/asteriskjava/manager/action/DongleSendSMSAction.java b/src/main/java/org/asteriskjava/manager/action/DongleSendSMSAction.java index a0dde3f58..b8e240351 100644 --- a/src/main/java/org/asteriskjava/manager/action/DongleSendSMSAction.java +++ b/src/main/java/org/asteriskjava/manager/action/DongleSendSMSAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + public class DongleSendSMSAction extends AbstractManagerAction { static final long serialVersionUID = 8194597741743334704L; private String device; diff --git a/src/main/java/org/asteriskjava/manager/action/DongleShowDevicesAction.java b/src/main/java/org/asteriskjava/manager/action/DongleShowDevicesAction.java index c2418b717..2d82a66bf 100644 --- a/src/main/java/org/asteriskjava/manager/action/DongleShowDevicesAction.java +++ b/src/main/java/org/asteriskjava/manager/action/DongleShowDevicesAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.DongleShowDevicesCompleteEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/EventGeneratingAction.java b/src/main/java/org/asteriskjava/manager/action/EventGeneratingAction.java index acd4bf04e..36437a168 100644 --- a/src/main/java/org/asteriskjava/manager/action/EventGeneratingAction.java +++ b/src/main/java/org/asteriskjava/manager/action/EventGeneratingAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.manager.event.ResponseEvent; /** diff --git a/src/main/java/org/asteriskjava/manager/action/EventsAction.java b/src/main/java/org/asteriskjava/manager/action/EventsAction.java index 953a47ce8..86b8ed1c9 100644 --- a/src/main/java/org/asteriskjava/manager/action/EventsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/EventsAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * With the EventsAction you can specify what kind of events should be sent to * this manager connection. diff --git a/src/main/java/org/asteriskjava/manager/action/ExecAction.java b/src/main/java/org/asteriskjava/manager/action/ExecAction.java index 7fa229e13..ae74418e1 100644 --- a/src/main/java/org/asteriskjava/manager/action/ExecAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ExecAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.CommandResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/ExtensionStateAction.java b/src/main/java/org/asteriskjava/manager/action/ExtensionStateAction.java index b3062022a..2524303c4 100644 --- a/src/main/java/org/asteriskjava/manager/action/ExtensionStateAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ExtensionStateAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.ExtensionStateResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/FilterAction.java b/src/main/java/org/asteriskjava/manager/action/FilterAction.java index ec4ddcc32..78aab42e6 100644 --- a/src/main/java/org/asteriskjava/manager/action/FilterAction.java +++ b/src/main/java/org/asteriskjava/manager/action/FilterAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The FilterAction dynamically adds filters for the current user in a manager session. * The filters added are only used for the current session. Once the connection is closed the filters are removed. diff --git a/src/main/java/org/asteriskjava/manager/action/GetConfigAction.java b/src/main/java/org/asteriskjava/manager/action/GetConfigAction.java index e64de5148..36f18b6e8 100644 --- a/src/main/java/org/asteriskjava/manager/action/GetConfigAction.java +++ b/src/main/java/org/asteriskjava/manager/action/GetConfigAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.GetConfigResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/GetVarAction.java b/src/main/java/org/asteriskjava/manager/action/GetVarAction.java index 6d0f14727..aa61c705d 100644 --- a/src/main/java/org/asteriskjava/manager/action/GetVarAction.java +++ b/src/main/java/org/asteriskjava/manager/action/GetVarAction.java @@ -16,7 +16,9 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.response.GetVarResponse; /** @@ -31,8 +33,8 @@ * the Manager API itself.

* GetVarAction returns a {@link org.asteriskjava.manager.response.GetVarResponse}. * To get the actual value from the corresponding - * {@link org.asteriskjava.manager.response.ManagerResponse} call - * {@link org.asteriskjava.manager.response.ManagerResponse#getAttribute(String)} + * {@link ManagerActionResponse} call + * {@link ManagerActionResponse#getAttribute(String)} * with either the variable name as parameter (for Asterisk 1.0.x) or * with "Value" as parameter (for Asterisk since 1.2).

* Example (for Asterisk 1.2):

diff --git a/src/main/java/org/asteriskjava/manager/action/HangupAction.java b/src/main/java/org/asteriskjava/manager/action/HangupAction.java index 2ea1fc3af..30369e5ba 100644 --- a/src/main/java/org/asteriskjava/manager/action/HangupAction.java +++ b/src/main/java/org/asteriskjava/manager/action/HangupAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The HangupAction causes Asterisk to hang up a given channel.

* Hangup with a cause code is only supported by Asterisk versions later than 1.6.2. diff --git a/src/main/java/org/asteriskjava/manager/action/IaxPeerListAction.java b/src/main/java/org/asteriskjava/manager/action/IaxPeerListAction.java index 3463ce453..58425c3a6 100644 --- a/src/main/java/org/asteriskjava/manager/action/IaxPeerListAction.java +++ b/src/main/java/org/asteriskjava/manager/action/IaxPeerListAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.PeerlistCompleteEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/JabberSendAction.java b/src/main/java/org/asteriskjava/manager/action/JabberSendAction.java index 17bb9519f..d09e60ebc 100644 --- a/src/main/java/org/asteriskjava/manager/action/JabberSendAction.java +++ b/src/main/java/org/asteriskjava/manager/action/JabberSendAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The JabberSendAction sends a Jabber (XMPP) message to a recipient.

* Available since Asterisk 1.6.0 diff --git a/src/main/java/org/asteriskjava/manager/action/ListCommandsAction.java b/src/main/java/org/asteriskjava/manager/action/ListCommandsAction.java index 916324ff3..ab9e8676d 100644 --- a/src/main/java/org/asteriskjava/manager/action/ListCommandsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ListCommandsAction.java @@ -16,13 +16,16 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * The ListCommandsAction returns possible commands in the Manager interface. *

* Use the getAttributes method on the ManagerResponse for a map of commands and explanations. * * @author martins - * @see org.asteriskjava.manager.response.ManagerResponse#getAttributes() + * @see ManagerActionResponse#getAttributes() * @since 0.3 */ public class ListCommandsAction extends AbstractManagerAction { diff --git a/src/main/java/org/asteriskjava/manager/action/LocalOptimizeAwayAction.java b/src/main/java/org/asteriskjava/manager/action/LocalOptimizeAwayAction.java index d307c53d5..70087c2bb 100644 --- a/src/main/java/org/asteriskjava/manager/action/LocalOptimizeAwayAction.java +++ b/src/main/java/org/asteriskjava/manager/action/LocalOptimizeAwayAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * LocalOptimizeAway action -- Optimize away a local channel when possible. *

diff --git a/src/main/java/org/asteriskjava/manager/action/LoginAction.java b/src/main/java/org/asteriskjava/manager/action/LoginAction.java index 0a9f61f39..655ff6624 100644 --- a/src/main/java/org/asteriskjava/manager/action/LoginAction.java +++ b/src/main/java/org/asteriskjava/manager/action/LoginAction.java @@ -16,6 +16,9 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.ChallengeAction; + /** * The LoginAction authenticates the connection.

* A successful login is the precondition for sending any other action except @@ -26,7 +29,7 @@ * * @author srt * @version $Id$ - * @see org.asteriskjava.manager.action.ChallengeAction + * @see ChallengeAction * @see org.asteriskjava.manager.response.ManagerError */ public class LoginAction extends AbstractManagerAction { diff --git a/src/main/java/org/asteriskjava/manager/action/LogoffAction.java b/src/main/java/org/asteriskjava/manager/action/LogoffAction.java index a5ef175af..0f218d61f 100644 --- a/src/main/java/org/asteriskjava/manager/action/LogoffAction.java +++ b/src/main/java/org/asteriskjava/manager/action/LogoffAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The LogoffAction causes the server to close the connection. * diff --git a/src/main/java/org/asteriskjava/manager/action/MWIDeleteAction.java b/src/main/java/org/asteriskjava/manager/action/MWIDeleteAction.java index 646f95a4a..5a5d0bfc4 100644 --- a/src/main/java/org/asteriskjava/manager/action/MWIDeleteAction.java +++ b/src/main/java/org/asteriskjava/manager/action/MWIDeleteAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + public class MWIDeleteAction extends AbstractManagerAction { static final long serialVersionUID = 1L; private String mailbox; diff --git a/src/main/java/org/asteriskjava/manager/action/MWIUpdateAction.java b/src/main/java/org/asteriskjava/manager/action/MWIUpdateAction.java index a9adfe154..dccb20e83 100644 --- a/src/main/java/org/asteriskjava/manager/action/MWIUpdateAction.java +++ b/src/main/java/org/asteriskjava/manager/action/MWIUpdateAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + public class MWIUpdateAction extends AbstractManagerAction { static final long serialVersionUID = 1L; private String mailbox; diff --git a/src/main/java/org/asteriskjava/manager/action/MailboxCountAction.java b/src/main/java/org/asteriskjava/manager/action/MailboxCountAction.java index 7a2bb7e84..28c5a6dba 100644 --- a/src/main/java/org/asteriskjava/manager/action/MailboxCountAction.java +++ b/src/main/java/org/asteriskjava/manager/action/MailboxCountAction.java @@ -5,7 +5,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.MailboxCountResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/MailboxStatusAction.java b/src/main/java/org/asteriskjava/manager/action/MailboxStatusAction.java index a9091058d..39a674b47 100644 --- a/src/main/java/org/asteriskjava/manager/action/MailboxStatusAction.java +++ b/src/main/java/org/asteriskjava/manager/action/MailboxStatusAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.MailboxStatusResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/ManagerAction.java b/src/main/java/org/asteriskjava/manager/action/ManagerAction.java deleted file mode 100644 index 93c578dab..000000000 --- a/src/main/java/org/asteriskjava/manager/action/ManagerAction.java +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright 2004-2006 Stefan Reuter - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.asteriskjava.manager.action; - -import java.io.Serializable; - -/** - * Interface that all Actions that can be sent to the Asterisk server must - * impement.

- * Instances of this class represent a command sent to Asterisk via Manager API, - * requesting a particular Action be performed. The number of actions available - * to the client are determined by the modules presently loaded in the Asterisk - * engine.

- * There is one conrete subclass of ManagerAction per each supported Asterisk - * Action. - * - * @author srt - * @version $Id$ - */ -public interface ManagerAction extends Serializable { - /** - * Returns the name of the action for example "Hangup". - */ - String getAction(); - - /** - * Returns the action id. - * - * @return the user provied action id. - */ - String getActionId(); - - /** - * Sets the action id.

- * If the action id is set and sent to the asterisk server any response - * returned by the Asterisk server will include the same id. This way - * the action id can be used to track actions and their corresponding - * responses and response events.

- * Note that Asterisk-Java uses its own internal action id to match - * actions with the corresponding responses and events. Though the internal - * action is never exposed to the application code. So if you want to - * handle reponses or response events on your own your application must - * set a unique action id using this method otherwise the action id of - * the reponse and response event objects passed to your application - * will be null. - * - * @param actionId the user provided action id to set. - * @see org.asteriskjava.manager.response.ManagerResponse#getActionId() - * @see org.asteriskjava.manager.event.ResponseEvent#getActionId() - */ - void setActionId(String actionId); - -} diff --git a/src/main/java/org/asteriskjava/manager/action/MessageSendAction.java b/src/main/java/org/asteriskjava/manager/action/MessageSendAction.java index b13218c65..1dcbc0e0a 100644 --- a/src/main/java/org/asteriskjava/manager/action/MessageSendAction.java +++ b/src/main/java/org/asteriskjava/manager/action/MessageSendAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + import java.util.LinkedHashMap; import java.util.Map; diff --git a/src/main/java/org/asteriskjava/manager/action/MixMonitorAction.java b/src/main/java/org/asteriskjava/manager/action/MixMonitorAction.java index fa7bc94c5..e92a41a94 100644 --- a/src/main/java/org/asteriskjava/manager/action/MixMonitorAction.java +++ b/src/main/java/org/asteriskjava/manager/action/MixMonitorAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.MixMonitorResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/MixMonitorMuteAction.java b/src/main/java/org/asteriskjava/manager/action/MixMonitorMuteAction.java index 1110c239c..7fe589bba 100644 --- a/src/main/java/org/asteriskjava/manager/action/MixMonitorMuteAction.java +++ b/src/main/java/org/asteriskjava/manager/action/MixMonitorMuteAction.java @@ -15,6 +15,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * MixMonitorMute can be used to mute and un-mute an existing recording. * diff --git a/src/main/java/org/asteriskjava/manager/action/ModuleCheckAction.java b/src/main/java/org/asteriskjava/manager/action/ModuleCheckAction.java index 3c368d3aa..4f4890822 100644 --- a/src/main/java/org/asteriskjava/manager/action/ModuleCheckAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ModuleCheckAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.ModuleCheckResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/ModuleLoadAction.java b/src/main/java/org/asteriskjava/manager/action/ModuleLoadAction.java index 632b28bff..e242c6669 100644 --- a/src/main/java/org/asteriskjava/manager/action/ModuleLoadAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ModuleLoadAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ModuleLoadAction loads, unloads or reloads Asterisk modules.

* Available since Asterisk 1.6 diff --git a/src/main/java/org/asteriskjava/manager/action/MonitorAction.java b/src/main/java/org/asteriskjava/manager/action/MonitorAction.java index dfd47f6f2..344ebfd70 100644 --- a/src/main/java/org/asteriskjava/manager/action/MonitorAction.java +++ b/src/main/java/org/asteriskjava/manager/action/MonitorAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The MonitorAction starts monitoring (recording) a channel.

* It is implemented in res/res_monitor.c diff --git a/src/main/java/org/asteriskjava/manager/action/MuteAudioAction.java b/src/main/java/org/asteriskjava/manager/action/MuteAudioAction.java index 0b49c16b0..c037efc9a 100644 --- a/src/main/java/org/asteriskjava/manager/action/MuteAudioAction.java +++ b/src/main/java/org/asteriskjava/manager/action/MuteAudioAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * Action: MuteAudio Synopsis: Mute an audio stream Privilege: system,all * Description: Mute an incoming or outbound audio stream in a channel. diff --git a/src/main/java/org/asteriskjava/manager/action/OriginateAction.java b/src/main/java/org/asteriskjava/manager/action/OriginateAction.java index 964e2b77e..c48e2d26d 100644 --- a/src/main/java/org/asteriskjava/manager/action/OriginateAction.java +++ b/src/main/java/org/asteriskjava/manager/action/OriginateAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.OriginateResponseEvent; import org.asteriskjava.manager.event.ResponseEvent; import org.asteriskjava.util.Log; diff --git a/src/main/java/org/asteriskjava/manager/action/PJSIPNotifyAction.java b/src/main/java/org/asteriskjava/manager/action/PJSIPNotifyAction.java index 40dd386b8..ba356c7e1 100644 --- a/src/main/java/org/asteriskjava/manager/action/PJSIPNotifyAction.java +++ b/src/main/java/org/asteriskjava/manager/action/PJSIPNotifyAction.java @@ -16,6 +16,9 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.ManagerAction; + import java.util.LinkedHashMap; import java.util.Map; diff --git a/src/main/java/org/asteriskjava/manager/action/PJSipShowContactsAction.java b/src/main/java/org/asteriskjava/manager/action/PJSipShowContactsAction.java index bc899dc48..564ce865c 100644 --- a/src/main/java/org/asteriskjava/manager/action/PJSipShowContactsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/PJSipShowContactsAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.ContactListComplete; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/PJSipShowEndpointAction.java b/src/main/java/org/asteriskjava/manager/action/PJSipShowEndpointAction.java index 78c643be3..ea5ded353 100644 --- a/src/main/java/org/asteriskjava/manager/action/PJSipShowEndpointAction.java +++ b/src/main/java/org/asteriskjava/manager/action/PJSipShowEndpointAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.EndpointDetailComplete; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/PJSipShowEndpointsAction.java b/src/main/java/org/asteriskjava/manager/action/PJSipShowEndpointsAction.java index 71c738724..97a1140f5 100644 --- a/src/main/java/org/asteriskjava/manager/action/PJSipShowEndpointsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/PJSipShowEndpointsAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.EndpointListComplete; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/ParkAction.java b/src/main/java/org/asteriskjava/manager/action/ParkAction.java index ed6d30855..1e9533b6b 100644 --- a/src/main/java/org/asteriskjava/manager/action/ParkAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ParkAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ParkAction parks a channel using Asterisk's park feature. *

diff --git a/src/main/java/org/asteriskjava/manager/action/ParkedCallsAction.java b/src/main/java/org/asteriskjava/manager/action/ParkedCallsAction.java index 0f519e0de..ceb34f78c 100644 --- a/src/main/java/org/asteriskjava/manager/action/ParkedCallsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ParkedCallsAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.ParkedCallsCompleteEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/PauseMixMonitorAction.java b/src/main/java/org/asteriskjava/manager/action/PauseMixMonitorAction.java index 31992b30c..940d88ae1 100644 --- a/src/main/java/org/asteriskjava/manager/action/PauseMixMonitorAction.java +++ b/src/main/java/org/asteriskjava/manager/action/PauseMixMonitorAction.java @@ -17,6 +17,8 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The PauseMixMonitorAction temporarily stop/start monitoring (recording) a/both channel(s). *

diff --git a/src/main/java/org/asteriskjava/manager/action/PauseMonitorAction.java b/src/main/java/org/asteriskjava/manager/action/PauseMonitorAction.java index 041ee56e3..72caefefc 100644 --- a/src/main/java/org/asteriskjava/manager/action/PauseMonitorAction.java +++ b/src/main/java/org/asteriskjava/manager/action/PauseMonitorAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The PauseMonitorAction temporarily stop monitoring (recording) a channel. *

diff --git a/src/main/java/org/asteriskjava/manager/action/PingAction.java b/src/main/java/org/asteriskjava/manager/action/PingAction.java index 58be89a06..97fecb8dd 100644 --- a/src/main/java/org/asteriskjava/manager/action/PingAction.java +++ b/src/main/java/org/asteriskjava/manager/action/PingAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.PingResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/PlayDtmfAction.java b/src/main/java/org/asteriskjava/manager/action/PlayDtmfAction.java index c0b71995c..e0a2b7846 100644 --- a/src/main/java/org/asteriskjava/manager/action/PlayDtmfAction.java +++ b/src/main/java/org/asteriskjava/manager/action/PlayDtmfAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The PlayDTMFAction plays a DTMF digit on the specified channel. *

diff --git a/src/main/java/org/asteriskjava/manager/action/QueueAddAction.java b/src/main/java/org/asteriskjava/manager/action/QueueAddAction.java index 61c394ec9..2d22039ef 100644 --- a/src/main/java/org/asteriskjava/manager/action/QueueAddAction.java +++ b/src/main/java/org/asteriskjava/manager/action/QueueAddAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The QueueAddAction adds a new member to a queue.

* It is implemented in apps/app_queue.c

diff --git a/src/main/java/org/asteriskjava/manager/action/QueueChangePriorityCallerAction.java b/src/main/java/org/asteriskjava/manager/action/QueueChangePriorityCallerAction.java index a0f0ac2e9..9e56a4c90 100644 --- a/src/main/java/org/asteriskjava/manager/action/QueueChangePriorityCallerAction.java +++ b/src/main/java/org/asteriskjava/manager/action/QueueChangePriorityCallerAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The QueueChangePriorityCallerAction requests to change priority of caller. * Priority cannot be less than 0. diff --git a/src/main/java/org/asteriskjava/manager/action/QueueLogAction.java b/src/main/java/org/asteriskjava/manager/action/QueueLogAction.java index ce72f7a4f..c9630a978 100644 --- a/src/main/java/org/asteriskjava/manager/action/QueueLogAction.java +++ b/src/main/java/org/asteriskjava/manager/action/QueueLogAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The QueueLogAction adds a custom log entry to the queue_log.

* It is implemented in apps/app_queue.c

diff --git a/src/main/java/org/asteriskjava/manager/action/QueueMemberRingInUseAction.java b/src/main/java/org/asteriskjava/manager/action/QueueMemberRingInUseAction.java index 347ffbb74..1708de96a 100644 --- a/src/main/java/org/asteriskjava/manager/action/QueueMemberRingInUseAction.java +++ b/src/main/java/org/asteriskjava/manager/action/QueueMemberRingInUseAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The QueueMemberRingInUseAction requests to change RingInUse value of queue member. */ diff --git a/src/main/java/org/asteriskjava/manager/action/QueuePauseAction.java b/src/main/java/org/asteriskjava/manager/action/QueuePauseAction.java index 654d5520c..0c8372cfc 100644 --- a/src/main/java/org/asteriskjava/manager/action/QueuePauseAction.java +++ b/src/main/java/org/asteriskjava/manager/action/QueuePauseAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The QueuePauseAction makes a queue member temporarily unavailabe (or * available again).

diff --git a/src/main/java/org/asteriskjava/manager/action/QueuePenaltyAction.java b/src/main/java/org/asteriskjava/manager/action/QueuePenaltyAction.java index 4d3405f16..37a254cdb 100644 --- a/src/main/java/org/asteriskjava/manager/action/QueuePenaltyAction.java +++ b/src/main/java/org/asteriskjava/manager/action/QueuePenaltyAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The QueuePenaltyAction sets the penalty for a queue member.

* It is implemented in apps/app_queue.c

diff --git a/src/main/java/org/asteriskjava/manager/action/QueueRemoveAction.java b/src/main/java/org/asteriskjava/manager/action/QueueRemoveAction.java index b474715f2..7ab93ef01 100644 --- a/src/main/java/org/asteriskjava/manager/action/QueueRemoveAction.java +++ b/src/main/java/org/asteriskjava/manager/action/QueueRemoveAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The QueueRemoveAction removes a member from a queue.

* It is implemented in apps/app_queue.c diff --git a/src/main/java/org/asteriskjava/manager/action/QueueResetAction.java b/src/main/java/org/asteriskjava/manager/action/QueueResetAction.java index c9158d1e3..777584e70 100644 --- a/src/main/java/org/asteriskjava/manager/action/QueueResetAction.java +++ b/src/main/java/org/asteriskjava/manager/action/QueueResetAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The QueueResetAction resets all statistical data of a given queue or all queues.

* It is implemented in apps/app_queue.c

diff --git a/src/main/java/org/asteriskjava/manager/action/QueueStatusAction.java b/src/main/java/org/asteriskjava/manager/action/QueueStatusAction.java index d92958778..7f9bc227a 100644 --- a/src/main/java/org/asteriskjava/manager/action/QueueStatusAction.java +++ b/src/main/java/org/asteriskjava/manager/action/QueueStatusAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.QueueStatusCompleteEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/QueueSummaryAction.java b/src/main/java/org/asteriskjava/manager/action/QueueSummaryAction.java index c1a5520ec..12e13d5bf 100644 --- a/src/main/java/org/asteriskjava/manager/action/QueueSummaryAction.java +++ b/src/main/java/org/asteriskjava/manager/action/QueueSummaryAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.QueueSummaryCompleteEvent; import org.asteriskjava.manager.event.QueueSummaryEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/RedirectAction.java b/src/main/java/org/asteriskjava/manager/action/RedirectAction.java index 0632ed4ec..f787beac1 100644 --- a/src/main/java/org/asteriskjava/manager/action/RedirectAction.java +++ b/src/main/java/org/asteriskjava/manager/action/RedirectAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * Redirects a given channel (and an optional additional channel) to a new * extension. diff --git a/src/main/java/org/asteriskjava/manager/action/SendTextAction.java b/src/main/java/org/asteriskjava/manager/action/SendTextAction.java index 8d9ea69d4..48be8e2f5 100644 --- a/src/main/java/org/asteriskjava/manager/action/SendTextAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SendTextAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * Sends a text message to a given channel while in a call. * An active channel and a text message are required in order to success.

diff --git a/src/main/java/org/asteriskjava/manager/action/SetCdrUserFieldAction.java b/src/main/java/org/asteriskjava/manager/action/SetCdrUserFieldAction.java index d9ffcb5e3..3f37408ee 100644 --- a/src/main/java/org/asteriskjava/manager/action/SetCdrUserFieldAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SetCdrUserFieldAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The SetCDRUserFieldAction causes the user field of the call detail record for * the given channel to be changed.

diff --git a/src/main/java/org/asteriskjava/manager/action/SetVarAction.java b/src/main/java/org/asteriskjava/manager/action/SetVarAction.java index 120e996ac..e3fde9d88 100644 --- a/src/main/java/org/asteriskjava/manager/action/SetVarAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SetVarAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The SetVarAction sets the value of a global or local channel variable.

* Setting global variables is supported since Asterisk 1.2. diff --git a/src/main/java/org/asteriskjava/manager/action/ShowDialplanAction.java b/src/main/java/org/asteriskjava/manager/action/ShowDialplanAction.java index d7b96f85b..a9c30f381 100644 --- a/src/main/java/org/asteriskjava/manager/action/ShowDialplanAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ShowDialplanAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.ResponseEvent; import org.asteriskjava.manager.event.ShowDialplanCompleteEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/SipNotifyAction.java b/src/main/java/org/asteriskjava/manager/action/SipNotifyAction.java index 13cef02dc..b5a4097ef 100644 --- a/src/main/java/org/asteriskjava/manager/action/SipNotifyAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SipNotifyAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + import java.util.LinkedHashMap; import java.util.Map; diff --git a/src/main/java/org/asteriskjava/manager/action/SipPeersAction.java b/src/main/java/org/asteriskjava/manager/action/SipPeersAction.java index 212ea7e89..1f2dcae26 100644 --- a/src/main/java/org/asteriskjava/manager/action/SipPeersAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SipPeersAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.PeerlistCompleteEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/SipShowPeerAction.java b/src/main/java/org/asteriskjava/manager/action/SipShowPeerAction.java index 6c6428c3f..2b463c272 100644 --- a/src/main/java/org/asteriskjava/manager/action/SipShowPeerAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SipShowPeerAction.java @@ -16,13 +16,15 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.response.SipShowPeerResponse; /** * Retrieves a the details about a given SIP peer.

* The result is returned in the response received in reply to this action.

- * Use {@link org.asteriskjava.manager.response.ManagerResponse#getAttribute(String)} + * Use {@link ManagerActionResponse#getAttribute(String)} * to retrieve the properties. Consider using {@link org.asteriskjava.manager.action.SipPeersAction} * instead.

* Available since Asterisk 1.2 diff --git a/src/main/java/org/asteriskjava/manager/action/SipShowRegistryAction.java b/src/main/java/org/asteriskjava/manager/action/SipShowRegistryAction.java index 2809e83d4..af96baaec 100644 --- a/src/main/java/org/asteriskjava/manager/action/SipShowRegistryAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SipShowRegistryAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.RegistrationsCompleteEvent; import org.asteriskjava.manager.event.RegistryEntryEvent; import org.asteriskjava.manager.event.ResponseEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/SkypeAccountPropertyAction.java b/src/main/java/org/asteriskjava/manager/action/SkypeAccountPropertyAction.java index e3367485e..5d30c33a3 100644 --- a/src/main/java/org/asteriskjava/manager/action/SkypeAccountPropertyAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SkypeAccountPropertyAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/org/asteriskjava/manager/action/SkypeAddBuddyAction.java b/src/main/java/org/asteriskjava/manager/action/SkypeAddBuddyAction.java index dacea3a9f..90cb1330b 100644 --- a/src/main/java/org/asteriskjava/manager/action/SkypeAddBuddyAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SkypeAddBuddyAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The SkypeAddBuddyAction adds a buddy to the buddy list of a Skype for Asterisk user.

* Available with Skype for Asterisk. diff --git a/src/main/java/org/asteriskjava/manager/action/SkypeBuddiesAction.java b/src/main/java/org/asteriskjava/manager/action/SkypeBuddiesAction.java index f7610ba07..292a39ae0 100644 --- a/src/main/java/org/asteriskjava/manager/action/SkypeBuddiesAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SkypeBuddiesAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.ResponseEvent; import org.asteriskjava.manager.event.SkypeBuddyListCompleteEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/SkypeBuddyAction.java b/src/main/java/org/asteriskjava/manager/action/SkypeBuddyAction.java index a45acc1f2..b0a9ffdb5 100644 --- a/src/main/java/org/asteriskjava/manager/action/SkypeBuddyAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SkypeBuddyAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.SkypeBuddyResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/SkypeChatSendAction.java b/src/main/java/org/asteriskjava/manager/action/SkypeChatSendAction.java index 76a961bd0..5454308bb 100644 --- a/src/main/java/org/asteriskjava/manager/action/SkypeChatSendAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SkypeChatSendAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The SkypeChatSendAction sends a Skype Chat message to a buddy of a Skype for Asterisk user.

* Available with Skype for Asterisk. diff --git a/src/main/java/org/asteriskjava/manager/action/SkypeLicenseListAction.java b/src/main/java/org/asteriskjava/manager/action/SkypeLicenseListAction.java index c8787a7e3..490c7c5ea 100644 --- a/src/main/java/org/asteriskjava/manager/action/SkypeLicenseListAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SkypeLicenseListAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.ResponseEvent; import org.asteriskjava.manager.event.SkypeBuddyListCompleteEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/SkypeLicenseStatusAction.java b/src/main/java/org/asteriskjava/manager/action/SkypeLicenseStatusAction.java index 87aff086c..0516c56df 100644 --- a/src/main/java/org/asteriskjava/manager/action/SkypeLicenseStatusAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SkypeLicenseStatusAction.java @@ -16,7 +16,8 @@ */ package org.asteriskjava.manager.action; -import org.asteriskjava.manager.ExpectedResponse; +import org.asteriskjava.ami.action.AbstractManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; import org.asteriskjava.manager.response.SkypeLicenseStatusResponse; /** diff --git a/src/main/java/org/asteriskjava/manager/action/SkypeRemoveBuddyAction.java b/src/main/java/org/asteriskjava/manager/action/SkypeRemoveBuddyAction.java index 80fce7212..4cdfb517c 100644 --- a/src/main/java/org/asteriskjava/manager/action/SkypeRemoveBuddyAction.java +++ b/src/main/java/org/asteriskjava/manager/action/SkypeRemoveBuddyAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The SkypeRemoveBuddyAction removes a buddy from the buddy list of a Skype for Asterisk user.

* Available with Skype for Asterisk. diff --git a/src/main/java/org/asteriskjava/manager/action/StatusAction.java b/src/main/java/org/asteriskjava/manager/action/StatusAction.java index a3e728d58..f353165db 100644 --- a/src/main/java/org/asteriskjava/manager/action/StatusAction.java +++ b/src/main/java/org/asteriskjava/manager/action/StatusAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.ResponseEvent; import org.asteriskjava.manager.event.StatusCompleteEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/StopMixMonitorAction.java b/src/main/java/org/asteriskjava/manager/action/StopMixMonitorAction.java index 5e5047f19..12b1cb188 100644 --- a/src/main/java/org/asteriskjava/manager/action/StopMixMonitorAction.java +++ b/src/main/java/org/asteriskjava/manager/action/StopMixMonitorAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * Stop the specified MixMonitor on the specified channel. *

diff --git a/src/main/java/org/asteriskjava/manager/action/StopMonitorAction.java b/src/main/java/org/asteriskjava/manager/action/StopMonitorAction.java index 5e5ca8c37..a8e718a4d 100644 --- a/src/main/java/org/asteriskjava/manager/action/StopMonitorAction.java +++ b/src/main/java/org/asteriskjava/manager/action/StopMonitorAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The StopMonitorAction ends monitoring (recording) a channel.

* It is implemented in res/res_monitor.c diff --git a/src/main/java/org/asteriskjava/manager/action/UnpauseMonitorAction.java b/src/main/java/org/asteriskjava/manager/action/UnpauseMonitorAction.java index 62821291d..304b0013e 100644 --- a/src/main/java/org/asteriskjava/manager/action/UnpauseMonitorAction.java +++ b/src/main/java/org/asteriskjava/manager/action/UnpauseMonitorAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The PauseMonitorAction re-enables monitoring (recording) of a channel after * calling PauseMonitor. diff --git a/src/main/java/org/asteriskjava/manager/action/UpdateConfigAction.java b/src/main/java/org/asteriskjava/manager/action/UpdateConfigAction.java index f3fd0ad43..637b3e237 100644 --- a/src/main/java/org/asteriskjava/manager/action/UpdateConfigAction.java +++ b/src/main/java/org/asteriskjava/manager/action/UpdateConfigAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + import java.util.HashMap; import java.util.Map; diff --git a/src/main/java/org/asteriskjava/manager/action/UserEventAction.java b/src/main/java/org/asteriskjava/manager/action/UserEventAction.java index f9131a5f7..f145380d7 100644 --- a/src/main/java/org/asteriskjava/manager/action/UserEventAction.java +++ b/src/main/java/org/asteriskjava/manager/action/UserEventAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.UserEvent; /** diff --git a/src/main/java/org/asteriskjava/manager/action/VoicemailUsersListAction.java b/src/main/java/org/asteriskjava/manager/action/VoicemailUsersListAction.java index 247c97350..8a5bd4d68 100644 --- a/src/main/java/org/asteriskjava/manager/action/VoicemailUsersListAction.java +++ b/src/main/java/org/asteriskjava/manager/action/VoicemailUsersListAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.ResponseEvent; import org.asteriskjava.manager.event.VoicemailUserEntryCompleteEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/ZapDialOffhookAction.java b/src/main/java/org/asteriskjava/manager/action/ZapDialOffhookAction.java index d561a0a3c..93b4aad2f 100644 --- a/src/main/java/org/asteriskjava/manager/action/ZapDialOffhookAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ZapDialOffhookAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ZapDialOffhookAction dials a number on a zap channel while offhook. * diff --git a/src/main/java/org/asteriskjava/manager/action/ZapDndOffAction.java b/src/main/java/org/asteriskjava/manager/action/ZapDndOffAction.java index 4cfb4f386..47be0d29f 100644 --- a/src/main/java/org/asteriskjava/manager/action/ZapDndOffAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ZapDndOffAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ZapDNDOnAction switches a zap channel "Do Not Disturb" status off. * diff --git a/src/main/java/org/asteriskjava/manager/action/ZapDndOnAction.java b/src/main/java/org/asteriskjava/manager/action/ZapDndOnAction.java index f1c87c33b..0f55cc778 100644 --- a/src/main/java/org/asteriskjava/manager/action/ZapDndOnAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ZapDndOnAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ZapDNDOnAction switches a zap channel "Do Not Disturb" status on. * diff --git a/src/main/java/org/asteriskjava/manager/action/ZapHangupAction.java b/src/main/java/org/asteriskjava/manager/action/ZapHangupAction.java index ee1a4a15f..29d4e3ed6 100644 --- a/src/main/java/org/asteriskjava/manager/action/ZapHangupAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ZapHangupAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ZapHangupAction hangs up a zap channel. * diff --git a/src/main/java/org/asteriskjava/manager/action/ZapRestartAction.java b/src/main/java/org/asteriskjava/manager/action/ZapRestartAction.java index 30c3ee96a..fead5d77f 100644 --- a/src/main/java/org/asteriskjava/manager/action/ZapRestartAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ZapRestartAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * Fully restarts all zaptel channels and terminates any calls on Zap * interfaces. diff --git a/src/main/java/org/asteriskjava/manager/action/ZapShowChannelsAction.java b/src/main/java/org/asteriskjava/manager/action/ZapShowChannelsAction.java index 9ba259fc9..1f04e1913 100644 --- a/src/main/java/org/asteriskjava/manager/action/ZapShowChannelsAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ZapShowChannelsAction.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.event.ResponseEvent; import org.asteriskjava.manager.event.ZapShowChannelsCompleteEvent; diff --git a/src/main/java/org/asteriskjava/manager/action/ZapTransferAction.java b/src/main/java/org/asteriskjava/manager/action/ZapTransferAction.java index e34ceb3dd..d8b693a7e 100644 --- a/src/main/java/org/asteriskjava/manager/action/ZapTransferAction.java +++ b/src/main/java/org/asteriskjava/manager/action/ZapTransferAction.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.action; +import org.asteriskjava.ami.action.AbstractManagerAction; + /** * The ZapTransferAction transfers a zap channel. * diff --git a/src/main/java/org/asteriskjava/manager/event/ResponseEvent.java b/src/main/java/org/asteriskjava/manager/event/ResponseEvent.java index cd7f1312a..30e0c4fd4 100644 --- a/src/main/java/org/asteriskjava/manager/event/ResponseEvent.java +++ b/src/main/java/org/asteriskjava/manager/event/ResponseEvent.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.event; +import org.asteriskjava.ami.action.ManagerAction; + /** * Abstract base class for events triggered in response to a ManagerAction. *

@@ -24,7 +26,7 @@ * * @author srt * @version $Id$ - * @see org.asteriskjava.manager.action.ManagerAction + * @see ManagerAction */ public abstract class ResponseEvent extends ManagerEvent { private static final long serialVersionUID = 1L; @@ -42,7 +44,7 @@ public ResponseEvent(Object source) { * * @return the action id of the ManagerAction that caused this event or * null if none was set. - * @see org.asteriskjava.manager.action.ManagerAction#setActionId(String) + * @see ManagerAction#setActionId(String) */ public final String getActionId() { return actionId; diff --git a/src/main/java/org/asteriskjava/manager/internal/ActionBuilder.java b/src/main/java/org/asteriskjava/manager/internal/ActionBuilder.java index eb7bd69ba..252bb6a20 100644 --- a/src/main/java/org/asteriskjava/manager/internal/ActionBuilder.java +++ b/src/main/java/org/asteriskjava/manager/internal/ActionBuilder.java @@ -17,7 +17,7 @@ package org.asteriskjava.manager.internal; import org.asteriskjava.AsteriskVersion; -import org.asteriskjava.manager.action.ManagerAction; +import org.asteriskjava.ami.action.ManagerAction; /** diff --git a/src/main/java/org/asteriskjava/manager/internal/ActionBuilderImpl.java b/src/main/java/org/asteriskjava/manager/internal/ActionBuilderImpl.java index 77f05d445..2917eaa59 100644 --- a/src/main/java/org/asteriskjava/manager/internal/ActionBuilderImpl.java +++ b/src/main/java/org/asteriskjava/manager/internal/ActionBuilderImpl.java @@ -17,8 +17,10 @@ package org.asteriskjava.manager.internal; import org.asteriskjava.AsteriskVersion; +import org.asteriskjava.ami.ActionFieldsComparator; +import org.asteriskjava.ami.action.ManagerAction; +import org.asteriskjava.core.databind.AsteriskEncoder; import org.asteriskjava.manager.AsteriskMapping; -import org.asteriskjava.manager.action.ManagerAction; import org.asteriskjava.manager.action.UserEventAction; import org.asteriskjava.manager.event.UserEvent; import org.asteriskjava.util.Log; @@ -47,6 +49,11 @@ class ActionBuilderImpl implements ActionBuilder { private AsteriskVersion targetVersion; private final Set membersToIgnore = new HashSet<>(); + private final AsteriskEncoder asteriskEncoder = AsteriskEncoder.builder() + .crlfNewlineDelimiter() + .fieldNamesComparator(new ActionFieldsComparator()) + .build(); + /** * Creates a new ActionBuilder for Asterisk 1.0. */ @@ -73,20 +80,21 @@ public String buildAction(final ManagerAction action) { @SuppressWarnings("unchecked") public String buildAction(final ManagerAction action, final String internalActionId) { + String actionId = internalActionId != null ? ManagerUtil.addInternalActionId(action.getActionId(), internalActionId) : action.getActionId(); + + if (action.getClass().getPackageName().contains("org.asteriskjava.ami.action")) { + action.setActionId(actionId); + return asteriskEncoder.encode(action); + } + StringBuilder sb = new StringBuilder(); sb.append("action: "); sb.append(action.getAction()); sb.append(LINE_SEPARATOR); - if (internalActionId != null) { - sb.append("actionid: "); - sb.append(ManagerUtil.addInternalActionId(action.getActionId(), internalActionId)); - sb.append(LINE_SEPARATOR); - } else if (action.getActionId() != null) { - sb.append("actionid: "); - sb.append(action.getActionId()); - sb.append(LINE_SEPARATOR); - } + sb.append("actionid: "); + sb.append(actionId); + sb.append(LINE_SEPARATOR); Map getters; @@ -122,7 +130,7 @@ public String buildAction(final ManagerAction action, final String internalActio Map attributes = (Map) value; for (Map.Entry entry : attributes.entrySet()) { appendString(sb, entry.getKey() == null ? "null" : entry.getKey().toString(), - entry.getValue() == null ? "null" : entry.getValue().toString()); + entry.getValue() == null ? "null" : entry.getValue().toString()); } } } @@ -259,7 +267,7 @@ private static String mapToAsterisk(Method getter) { // check annotation of getter method annotation - = getter.getAnnotation(AsteriskMapping.class + = getter.getAnnotation(AsteriskMapping.class ); if (annotation != null) { return annotation.value(); @@ -270,7 +278,7 @@ private static String mapToAsterisk(Method getter) { try { Method setter = getter.getDeclaringClass().getDeclaredMethod(setterName, getter.getReturnType()); annotation - = setter.getAnnotation(AsteriskMapping.class + = setter.getAnnotation(AsteriskMapping.class ); if (annotation != null) { return annotation.value(); @@ -284,7 +292,7 @@ private static String mapToAsterisk(Method getter) { try { Field field = getter.getDeclaringClass().getDeclaredField(fieldName); annotation - = field.getAnnotation(AsteriskMapping.class + = field.getAnnotation(AsteriskMapping.class ); if (annotation != null) { return annotation.value(); @@ -330,6 +338,6 @@ private static String lcFirst(String s) { } return Character.toLowerCase(s.charAt(0)) - + (s.length() == 1 ? "" : s.substring(1)); + + (s.length() == 1 ? "" : s.substring(1)); } } diff --git a/src/main/java/org/asteriskjava/manager/internal/AsyncEventPump.java b/src/main/java/org/asteriskjava/manager/internal/AsyncEventPump.java index d17b6ae4c..c1a843750 100644 --- a/src/main/java/org/asteriskjava/manager/internal/AsyncEventPump.java +++ b/src/main/java/org/asteriskjava/manager/internal/AsyncEventPump.java @@ -1,9 +1,9 @@ package org.asteriskjava.manager.internal; import com.google.common.util.concurrent.RateLimiter; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.lock.Locker; import org.asteriskjava.manager.event.ManagerEvent; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.pbx.util.LogTime; import org.asteriskjava.util.Log; import org.asteriskjava.util.LogFactory; @@ -143,7 +143,7 @@ public void stop() { * add a ManagerResponse to the queue, only if the queue is not full */ @Override - public void dispatchResponse(ManagerResponse response, Integer requiredHandlingTime) { + public void dispatchResponse(ManagerActionResponse response, Integer requiredHandlingTime) { if (!queue.offer(new EventWrapper(response))) { logger.error(name + " Event queue is full, not processing ManagerResponse " + response); } @@ -161,7 +161,7 @@ public void dispatchEvent(ManagerEvent event, Integer requiredHandlingTime) { private static class EventWrapper { LogTime timer = new LogTime(); - ManagerResponse response; + ManagerActionResponse response; ManagerEvent event; CountDownLatch poison; @@ -180,7 +180,7 @@ public String getPayloadAsString() { } - EventWrapper(ManagerResponse response) { + EventWrapper(ManagerActionResponse response) { this.response = response; } diff --git a/src/main/java/org/asteriskjava/manager/internal/Dispatcher.java b/src/main/java/org/asteriskjava/manager/internal/Dispatcher.java index 95f36efe0..95ac2b417 100644 --- a/src/main/java/org/asteriskjava/manager/internal/Dispatcher.java +++ b/src/main/java/org/asteriskjava/manager/internal/Dispatcher.java @@ -16,8 +16,8 @@ */ package org.asteriskjava.manager.internal; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.event.ManagerEvent; -import org.asteriskjava.manager.response.ManagerResponse; /** * The Dispatcher defines the interface used for communication between @@ -31,7 +31,7 @@ */ interface Dispatcher { /** - * This method is called by the reader whenever a {@link ManagerResponse} is + * This method is called by the reader whenever a {@link ManagerActionResponse} is * received. The response is dispatched to the associated * {@link org.asteriskjava.manager.SendActionCallback}. * @@ -40,7 +40,7 @@ interface Dispatcher { * within to not cause a back log of events * @see ManagerReader */ - void dispatchResponse(ManagerResponse response, Integer requiredHandlingTime); + void dispatchResponse(ManagerActionResponse response, Integer requiredHandlingTime); /** * This method is called by the reader whenever a ManagerEvent is received. diff --git a/src/main/java/org/asteriskjava/manager/internal/ManagerConnectionImpl.java b/src/main/java/org/asteriskjava/manager/internal/ManagerConnectionImpl.java index 7cfbb8a7c..c6c72f2aa 100644 --- a/src/main/java/org/asteriskjava/manager/internal/ManagerConnectionImpl.java +++ b/src/main/java/org/asteriskjava/manager/internal/ManagerConnectionImpl.java @@ -16,6 +16,12 @@ package org.asteriskjava.manager.internal; import org.asteriskjava.AsteriskVersion; +import org.asteriskjava.ami.action.AuthType; +import org.asteriskjava.ami.action.ChallengeAction; +import org.asteriskjava.ami.action.ManagerAction; +import org.asteriskjava.ami.action.annotation.ExpectedResponse; +import org.asteriskjava.ami.action.response.ChallengeManagerActionResponse; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.lock.Lockable; import org.asteriskjava.lock.LockableList; import org.asteriskjava.lock.LockableMap; @@ -23,7 +29,9 @@ import org.asteriskjava.manager.*; import org.asteriskjava.manager.action.*; import org.asteriskjava.manager.event.*; -import org.asteriskjava.manager.response.*; +import org.asteriskjava.manager.response.CommandResponse; +import org.asteriskjava.manager.response.CoreSettingsResponse; +import org.asteriskjava.manager.response.ManagerError; import org.asteriskjava.pbx.util.LogTime; import org.asteriskjava.util.DateUtil; import org.asteriskjava.util.Log; @@ -442,7 +450,7 @@ public void login(String eventMask) throws IOException, AuthenticationFailedExce * timeout ms. *

  • Sends a {@link ChallengeAction} requesting a challenge for authType * MD5. - *
  • When the {@link ChallengeResponse} is received a {@link LoginAction} + *
  • When the {@link ChallengeManagerActionResponse} is received a {@link LoginAction} * is sent using the calculated key (MD5 hash of the password appended to * the received challenge). * @@ -465,11 +473,11 @@ protected void doLogin(long timeout, String eventMask) throws IOException, AuthenticationFailedException, TimeoutException { try (LockCloser closer = this.withLock()) { ChallengeAction challengeAction; - ManagerResponse challengeResponse; + ManagerActionResponse challengeResponse; String challenge; String key; LoginAction loginAction; - ManagerResponse loginResponse; + ManagerActionResponse loginResponse; if (socket == null) { connect(); @@ -492,7 +500,8 @@ protected void doLogin(long timeout, String eventMask) throw new TimeoutException("Timeout waiting for protocol identifier"); } - challengeAction = new ChallengeAction("MD5"); + challengeAction = new ChallengeAction(); + challengeAction.setAuthType(AuthType.MD5); try { challengeResponse = sendAction(challengeAction); } catch (Exception e) { @@ -500,8 +509,8 @@ protected void doLogin(long timeout, String eventMask) throw new AuthenticationFailedException("Unable to send challenge action", e); } - if (challengeResponse instanceof ChallengeResponse) { - challenge = ((ChallengeResponse) challengeResponse).getChallenge(); + if (challengeResponse instanceof ChallengeManagerActionResponse) { + challenge = ((ChallengeManagerActionResponse) challengeResponse).getChallenge(); } else { disconnect(); throw new AuthenticationFailedException("Unable to get challenge from Asterisk. ChallengeAction returned: " @@ -597,7 +606,7 @@ protected AsteriskVersion determineVersion() throws IOException, TimeoutExceptio */ protected AsteriskVersion determineVersionByCoreSettings() throws Exception { - ManagerResponse response = sendAction(new CoreSettingsAction()); + ManagerActionResponse response = sendAction(new CoreSettingsAction()); if (!(response instanceof CoreSettingsResponse)) { // NOTE: you need system or reporting permissions logger.info("Could not get core settings, do we have the necessary permissions?"); @@ -616,7 +625,7 @@ protected AsteriskVersion determineVersionByCoreSettings() throws Exception { * @throws Exception */ protected AsteriskVersion determineVersionByCoreShowVersion() throws Exception { - final ManagerResponse coreShowVersionResponse = sendAction(new CommandAction(CMD_SHOW_VERSION)); + final ManagerActionResponse coreShowVersionResponse = sendAction(new CommandAction(CMD_SHOW_VERSION)); if (coreShowVersionResponse == null || !(coreShowVersionResponse instanceof CommandResponse)) { // this needs 'command' permissions @@ -711,7 +720,7 @@ protected void disconnect() { } } - public ManagerResponse sendAction(ManagerAction action) + public ManagerActionResponse sendAction(ManagerAction action) throws IOException, TimeoutException, IllegalArgumentException, IllegalStateException { return sendAction(action, defaultResponseTimeout); } @@ -721,11 +730,11 @@ public ManagerResponse sendAction(ManagerAction action) * * @param timeout - in milliseconds */ - public ManagerResponse sendAction(ManagerAction action, long timeout) + public ManagerActionResponse sendAction(ManagerAction action, long timeout) throws IOException, TimeoutException, IllegalArgumentException, IllegalStateException { DefaultSendActionCallback callbackHandler = new DefaultSendActionCallback(); - ManagerResponse response = null; + ManagerActionResponse response = null; try { sendAction(action, callbackHandler); @@ -790,7 +799,7 @@ else if (state != CONNECTED) { } } - Class responseClass = getExpectedResponseClass(action.getClass()); + Class responseClass = getExpectedResponseClass(action.getClass()); if (responseClass != null) { reader.expectResponseClass(internalActionId, responseClass); } @@ -810,7 +819,7 @@ boolean isShowVersionCommandAction(ManagerAction action) { return false; } - private Class getExpectedResponseClass(Class actionClass) { + private Class getExpectedResponseClass(Class actionClass) { final ExpectedResponse annotation = actionClass.getAnnotation(ExpectedResponse.class); if (annotation == null) { return null; @@ -988,14 +997,14 @@ public ManagerConnectionState getState() { /* Implementation of Dispatcher: callbacks for ManagerReader */ /** - * This method is called by the reader whenever a {@link ManagerResponse} is + * This method is called by the reader whenever a {@link ManagerActionResponse} is * received. The response is dispatched to the associated * {@link SendActionCallback}. * * @param response the response received by the reader * @see ManagerReader */ - public void dispatchResponse(ManagerResponse response, Integer requiredHandlingTime) { + public void dispatchResponse(ManagerActionResponse response, Integer requiredHandlingTime) { final String actionId; String internalActionId; SendActionCallback listener; @@ -1389,18 +1398,18 @@ public String toString() { */ private static class DefaultSendActionCallback implements SendActionCallback, Serializable { private static final long serialVersionUID = 7831097958568769220L; - private ManagerResponse response; + private ManagerActionResponse response; private final CountDownLatch latch = new CountDownLatch(1); private volatile boolean disposed = false; private LogTime timer = new LogTime(); - private ManagerResponse waitForResponse(long timeout) throws InterruptedException { + private ManagerActionResponse waitForResponse(long timeout) throws InterruptedException { latch.await(timeout, TimeUnit.MILLISECONDS); return this.response; } @Override - public void onResponse(ManagerResponse response) { + public void onResponse(ManagerActionResponse response) { this.response = response; if (disposed) { logger.error("Response arrived after Disposal and assumably Timeout " + response + " elapsed: " @@ -1466,7 +1475,7 @@ public void onManagerEvent(ManagerEvent event) { } } - public void onResponse(ManagerResponse response) { + public void onResponse(ManagerActionResponse response) { // If disconnected if (response == null) { // Set flag that must not wait for the response @@ -1532,7 +1541,7 @@ public void onManagerEvent(ManagerEvent event) { } @Override - public void onResponse(ManagerResponse response) { + public void onResponse(ManagerActionResponse response) { // If disconnected if (response == null) { callback.onResponse(events); diff --git a/src/main/java/org/asteriskjava/manager/internal/ManagerReader.java b/src/main/java/org/asteriskjava/manager/internal/ManagerReader.java index 2b3e46c19..9510bcb3f 100644 --- a/src/main/java/org/asteriskjava/manager/internal/ManagerReader.java +++ b/src/main/java/org/asteriskjava/manager/internal/ManagerReader.java @@ -16,8 +16,8 @@ */ package org.asteriskjava.manager.internal; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.event.ManagerEvent; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.util.SocketConnectionFacade; import java.io.IOException; @@ -57,7 +57,7 @@ public interface ManagerReader extends Runnable { */ void registerEventClass(Class event); - void expectResponseClass(String internalActionId, Class responseClass); + void expectResponseClass(String internalActionId, Class responseClass); /** * Terminates this reader. diff --git a/src/main/java/org/asteriskjava/manager/internal/ManagerReaderImpl.java b/src/main/java/org/asteriskjava/manager/internal/ManagerReaderImpl.java index efbc00535..c9d06e832 100644 --- a/src/main/java/org/asteriskjava/manager/internal/ManagerReaderImpl.java +++ b/src/main/java/org/asteriskjava/manager/internal/ManagerReaderImpl.java @@ -17,11 +17,11 @@ package org.asteriskjava.manager.internal; import com.google.common.util.concurrent.RateLimiter; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.event.DisconnectEvent; import org.asteriskjava.manager.event.ManagerEvent; import org.asteriskjava.manager.event.ProtocolIdentifierReceivedEvent; import org.asteriskjava.manager.internal.backwardsCompatibility.BackwardsCompatibilityForManagerEvents; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.pbx.util.LogTime; import org.asteriskjava.util.DateUtil; import org.asteriskjava.util.Log; @@ -56,7 +56,7 @@ public class ManagerReaderImpl implements ManagerReader { */ private final ResponseBuilder responseBuilder; - private final Map> expectedResponseClasses; + private final Map> expectedResponseClasses; /** * The source to use when creating {@link ManagerEvent}s. @@ -120,7 +120,7 @@ public void registerEventClass(Class eventClass) { eventBuilder.registerEventClass(eventClass); } - public void expectResponseClass(String internalActionId, Class responseClass) { + public void expectResponseClass(String internalActionId, Class responseClass) { expectedResponseClasses.put(internalActionId, responseClass); } @@ -132,7 +132,7 @@ public void expectResponseClass(String internalActionId, Class buffer) { - Class responseClass = null; + private ManagerActionResponse buildResponse(Map buffer) { + Class responseClass = null; final String actionId = (String) buffer.get("actionid"); final String internalActionId = ManagerUtil.getInternalActionId(actionId); if (internalActionId != null) { responseClass = expectedResponseClasses.remove(internalActionId); } - final ManagerResponse response = responseBuilder.buildResponse(responseClass, buffer); + final ManagerActionResponse response = responseBuilder.buildResponse(responseClass, buffer); if (response != null) { - response.setDateReceived(DateUtil.getDate()); + response.setDateReceived(DateUtil.getDate().toInstant()); } return response; diff --git a/src/main/java/org/asteriskjava/manager/internal/ManagerWriter.java b/src/main/java/org/asteriskjava/manager/internal/ManagerWriter.java index 94d844870..f7c04dccf 100644 --- a/src/main/java/org/asteriskjava/manager/internal/ManagerWriter.java +++ b/src/main/java/org/asteriskjava/manager/internal/ManagerWriter.java @@ -17,7 +17,7 @@ package org.asteriskjava.manager.internal; import org.asteriskjava.AsteriskVersion; -import org.asteriskjava.manager.action.ManagerAction; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.util.SocketConnectionFacade; import java.io.IOException; diff --git a/src/main/java/org/asteriskjava/manager/internal/ManagerWriterImpl.java b/src/main/java/org/asteriskjava/manager/internal/ManagerWriterImpl.java index d1d8f01e1..d94776dc5 100644 --- a/src/main/java/org/asteriskjava/manager/internal/ManagerWriterImpl.java +++ b/src/main/java/org/asteriskjava/manager/internal/ManagerWriterImpl.java @@ -17,9 +17,9 @@ package org.asteriskjava.manager.internal; import org.asteriskjava.AsteriskVersion; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.lock.Lockable; import org.asteriskjava.lock.Locker.LockCloser; -import org.asteriskjava.manager.action.ManagerAction; import org.asteriskjava.util.SocketConnectionFacade; import java.io.IOException; diff --git a/src/main/java/org/asteriskjava/manager/internal/ResponseBuilder.java b/src/main/java/org/asteriskjava/manager/internal/ResponseBuilder.java index b3c940409..144ba2723 100644 --- a/src/main/java/org/asteriskjava/manager/internal/ResponseBuilder.java +++ b/src/main/java/org/asteriskjava/manager/internal/ResponseBuilder.java @@ -16,7 +16,7 @@ */ package org.asteriskjava.manager.internal; -import org.asteriskjava.manager.response.ManagerResponse; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import java.util.Map; @@ -26,7 +26,7 @@ * * @author srt * @version $Id$ - * @see org.asteriskjava.manager.response.ManagerResponse + * @see ManagerActionResponse */ interface ResponseBuilder { /** @@ -37,5 +37,5 @@ interface ResponseBuilder { * case. * @return the response with the given attributes. */ - ManagerResponse buildResponse(Class responseClass, Map attributes); + ManagerActionResponse buildResponse(Class responseClass, Map attributes); } diff --git a/src/main/java/org/asteriskjava/manager/internal/ResponseBuilderImpl.java b/src/main/java/org/asteriskjava/manager/internal/ResponseBuilderImpl.java index 577843f08..eb183fb86 100644 --- a/src/main/java/org/asteriskjava/manager/internal/ResponseBuilderImpl.java +++ b/src/main/java/org/asteriskjava/manager/internal/ResponseBuilderImpl.java @@ -15,9 +15,11 @@ */ package org.asteriskjava.manager.internal; +import org.asteriskjava.ami.action.response.ManagerActionResponse; +import org.asteriskjava.ami.action.response.ResponseType; +import org.asteriskjava.core.databind.AsteriskDecoder; import org.asteriskjava.manager.response.CommandResponse; import org.asteriskjava.manager.response.ManagerError; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.manager.util.EventAttributesHelper; import org.slf4j.Logger; @@ -30,28 +32,37 @@ * * @author srt * @version $Id$ - * @see org.asteriskjava.manager.response.ManagerResponse + * @see ManagerActionResponse */ class ResponseBuilderImpl implements ResponseBuilder { private static final Logger logger = getLogger(ResponseBuilderImpl.class); private static final Set ignoredAttributes = new HashSet<>(Arrays.asList( - "attributes", "proxyresponse", ManagerReader.COMMAND_RESULT_RESPONSE_KEY)); + "attributes", "proxyresponse", ManagerReader.COMMAND_RESULT_RESPONSE_KEY)); private static final String RESPONSE_KEY = "response"; private static final String PROXY_RESPONSE_KEY = "proxyresponse"; private static final String RESPONSE_TYPE_ERROR = "error"; private static final String OUTPUT_RESPONSE_KEY = "output"; //Asterisk 14.3.0 + private final AsteriskDecoder asteriskDecoder = new AsteriskDecoder(false); + @SuppressWarnings("unchecked") - public ManagerResponse buildResponse(Class responseClass, Map attributes) { - final ManagerResponse response; + public ManagerActionResponse buildResponse(Class responseClass, Map attributes) { + responseClass = responseClass == null ? ManagerActionResponse.class : responseClass; + if (responseClass.getPackageName().contains("org.asteriskjava.ami.action.response")) { + ManagerActionResponse response = asteriskDecoder.decode(attributes, responseClass); + response.setAttributes(new HashMap<>(attributes)); + return response; + } + + final ManagerActionResponse response; final String responseType = (String) attributes.get(RESPONSE_KEY); if (RESPONSE_TYPE_ERROR.equalsIgnoreCase(responseType)) { response = new ManagerError(); } else if (responseClass == null) { - response = new ManagerResponse(); + response = new ManagerActionResponse(); } else { try { response = responseClass.getDeclaredConstructor().newInstance(); @@ -90,7 +101,7 @@ public ManagerResponse buildResponse(Class responseCl } if (response.getResponse() != null && attributes.get(PROXY_RESPONSE_KEY) != null) { - response.setResponse((String) attributes.get(PROXY_RESPONSE_KEY)); + response.setResponse(ResponseType.valueOf((String) attributes.get(PROXY_RESPONSE_KEY))); } // make the map of all attributes available to the response diff --git a/src/main/java/org/asteriskjava/manager/internal/ResponseEventsImpl.java b/src/main/java/org/asteriskjava/manager/internal/ResponseEventsImpl.java index 7507f7598..0698893b1 100644 --- a/src/main/java/org/asteriskjava/manager/internal/ResponseEventsImpl.java +++ b/src/main/java/org/asteriskjava/manager/internal/ResponseEventsImpl.java @@ -16,11 +16,11 @@ */ package org.asteriskjava.manager.internal; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.lock.LockableList; import org.asteriskjava.lock.Locker.LockCloser; import org.asteriskjava.manager.ResponseEvents; import org.asteriskjava.manager.event.ResponseEvent; -import org.asteriskjava.manager.response.ManagerResponse; import java.util.ArrayList; import java.util.Collection; @@ -35,7 +35,7 @@ * @since 0.2 */ public class ResponseEventsImpl implements ResponseEvents { - private ManagerResponse response; + private ManagerActionResponse response; private final LockableList events; private boolean complete; private final CountDownLatch latch = new CountDownLatch(1); @@ -50,7 +50,7 @@ public ResponseEventsImpl() { // implementation of the ResponseEvents interface - public ManagerResponse getResponse() { + public ManagerActionResponse getResponse() { return response; } @@ -69,7 +69,7 @@ public boolean isComplete() { * * @param response the ManagerResponse received. */ - public void setRepsonse(ManagerResponse response) { + public void setRepsonse(ManagerActionResponse response) { this.response = response; } diff --git a/src/main/java/org/asteriskjava/manager/response/ChallengeResponse.java b/src/main/java/org/asteriskjava/manager/response/ChallengeResponse.java deleted file mode 100644 index 662668784..000000000 --- a/src/main/java/org/asteriskjava/manager/response/ChallengeResponse.java +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2004-2006 Stefan Reuter - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package org.asteriskjava.manager.response; - -/** - * Corresponds to a ChallengeAction and contains the challenge needed to log in using - * challenge/response. - * - * @author srt - * @version $Id$ - * @see org.asteriskjava.manager.action.ChallengeAction - * @see org.asteriskjava.manager.action.LoginAction - */ -public class ChallengeResponse extends ManagerResponse { - private static final long serialVersionUID = -7253724086340850957L; - - private String challenge; - - /** - * Returns the challenge to use when creating the key for log in. - * - * @return the challenge to use when creating the key for log in. - * @see org.asteriskjava.manager.action.LoginAction#setKey(String) - */ - public String getChallenge() { - return challenge; - } - - public void setChallenge(String challenge) { - this.challenge = challenge; - } -} diff --git a/src/main/java/org/asteriskjava/manager/response/CommandResponse.java b/src/main/java/org/asteriskjava/manager/response/CommandResponse.java index f1e640dde..1bdd8ac87 100644 --- a/src/main/java/org/asteriskjava/manager/response/CommandResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/CommandResponse.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + import java.util.List; /** @@ -29,7 +31,7 @@ * @version $Id$ * @see org.asteriskjava.manager.action.CommandAction */ -public class CommandResponse extends ManagerResponse { +public class CommandResponse extends ManagerActionResponse { private static final long serialVersionUID = 1L; private String privilege; diff --git a/src/main/java/org/asteriskjava/manager/response/CoreSettingsResponse.java b/src/main/java/org/asteriskjava/manager/response/CoreSettingsResponse.java index e7e233922..45107dc37 100644 --- a/src/main/java/org/asteriskjava/manager/response/CoreSettingsResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/CoreSettingsResponse.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * Corresponds to a CoreSettingsAction and contains the current settings summary of the * Asterisk server. @@ -25,7 +27,7 @@ * @see org.asteriskjava.manager.action.CoreSettingsAction * @since 1.0.0 */ -public class CoreSettingsResponse extends ManagerResponse { +public class CoreSettingsResponse extends ManagerActionResponse { private static final long serialVersionUID = 1L; private String amiVersion; diff --git a/src/main/java/org/asteriskjava/manager/response/CoreStatusResponse.java b/src/main/java/org/asteriskjava/manager/response/CoreStatusResponse.java index cc2f8237a..2c9f3e65c 100644 --- a/src/main/java/org/asteriskjava/manager/response/CoreStatusResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/CoreStatusResponse.java @@ -16,6 +16,7 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.util.DateUtil; import java.util.Date; @@ -30,7 +31,7 @@ * @see org.asteriskjava.manager.action.CoreStatusAction * @since 1.0.0 */ -public class CoreStatusResponse extends ManagerResponse { +public class CoreStatusResponse extends ManagerActionResponse { private static final long serialVersionUID = 1L; private String coreStartupTime; diff --git a/src/main/java/org/asteriskjava/manager/response/ExtensionStateResponse.java b/src/main/java/org/asteriskjava/manager/response/ExtensionStateResponse.java index e9afcb964..168ca239f 100644 --- a/src/main/java/org/asteriskjava/manager/response/ExtensionStateResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/ExtensionStateResponse.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * Response to an {@link org.asteriskjava.manager.action.ExtensionStateAction}. * @@ -23,7 +25,7 @@ * @version $Id$ * @see org.asteriskjava.manager.action.ExtensionStateAction */ -public class ExtensionStateResponse extends ManagerResponse { +public class ExtensionStateResponse extends ManagerActionResponse { private static final long serialVersionUID = -2044248427247227390L; private String exten; private String context; diff --git a/src/main/java/org/asteriskjava/manager/response/GetConfigResponse.java b/src/main/java/org/asteriskjava/manager/response/GetConfigResponse.java index da4ec4a02..7268f3e12 100644 --- a/src/main/java/org/asteriskjava/manager/response/GetConfigResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/GetConfigResponse.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + import java.util.Locale; import java.util.Map; import java.util.Map.Entry; @@ -34,7 +36,7 @@ * @see org.asteriskjava.manager.action.GetConfigAction * @since 0.3 */ -public class GetConfigResponse extends ManagerResponse { +public class GetConfigResponse extends ManagerActionResponse { private static final long serialVersionUID = -2044248427247227390L; private Map categories; diff --git a/src/main/java/org/asteriskjava/manager/response/GetVarResponse.java b/src/main/java/org/asteriskjava/manager/response/GetVarResponse.java index 8758a6da7..2ae370f36 100644 --- a/src/main/java/org/asteriskjava/manager/response/GetVarResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/GetVarResponse.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * Corresponds to a GetVarAction and contains the value of the requested variable. * @@ -24,7 +26,7 @@ * @see org.asteriskjava.manager.action.GetVarAction * @since 1.0.0 */ -public class GetVarResponse extends ManagerResponse { +public class GetVarResponse extends ManagerActionResponse { private static final long serialVersionUID = 1L; private String variable; diff --git a/src/main/java/org/asteriskjava/manager/response/MailboxCountResponse.java b/src/main/java/org/asteriskjava/manager/response/MailboxCountResponse.java index 66bdae04e..497cc6224 100644 --- a/src/main/java/org/asteriskjava/manager/response/MailboxCountResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/MailboxCountResponse.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * A MailboxCountResponse is sent in response to a MailboxCountAction and contains the number of old * and new messages in a mailbox. @@ -24,7 +26,7 @@ * @version $Id$ * @see org.asteriskjava.manager.action.MailboxCountAction */ -public class MailboxCountResponse extends ManagerResponse { +public class MailboxCountResponse extends ManagerActionResponse { /** * Serial version identifier */ diff --git a/src/main/java/org/asteriskjava/manager/response/MailboxStatusResponse.java b/src/main/java/org/asteriskjava/manager/response/MailboxStatusResponse.java index c0e245d0d..aeffd5224 100644 --- a/src/main/java/org/asteriskjava/manager/response/MailboxStatusResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/MailboxStatusResponse.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * A MailboxStatusResponse is sent in response to a MailboxStatusAction and indicates if a set * of mailboxes contains waiting messages. @@ -24,7 +26,7 @@ * @version $Id$ * @see org.asteriskjava.manager.action.MailboxStatusAction */ -public class MailboxStatusResponse extends ManagerResponse { +public class MailboxStatusResponse extends ManagerActionResponse { /** * Serial version identifier */ diff --git a/src/main/java/org/asteriskjava/manager/response/ManagerError.java b/src/main/java/org/asteriskjava/manager/response/ManagerError.java index 2b208f72b..85052ef64 100644 --- a/src/main/java/org/asteriskjava/manager/response/ManagerError.java +++ b/src/main/java/org/asteriskjava/manager/response/ManagerError.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * Represents an "Response: Error" response received from the asterisk server. * The cause for the error is given in the message attribute. @@ -23,7 +25,7 @@ * @author srt * @version $Id$ */ -public class ManagerError extends ManagerResponse { +public class ManagerError extends ManagerActionResponse { /** * Serial version identifier */ diff --git a/src/main/java/org/asteriskjava/manager/response/MixMonitorResponse.java b/src/main/java/org/asteriskjava/manager/response/MixMonitorResponse.java index ab8d3997f..ffc747b1c 100644 --- a/src/main/java/org/asteriskjava/manager/response/MixMonitorResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/MixMonitorResponse.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * A MixMonitorResponse is sent in response to a MixMonitorAction and provides * the ID of the MixMonitor that was started on the referenced channel. @@ -24,7 +26,7 @@ * * @see org.asteriskjava.manager.action.MixMonitorAction; */ -public class MixMonitorResponse extends ManagerResponse { +public class MixMonitorResponse extends ManagerActionResponse { private static final long serialVersionUID = 1L; private String mixMonitorId; diff --git a/src/main/java/org/asteriskjava/manager/response/ModuleCheckResponse.java b/src/main/java/org/asteriskjava/manager/response/ModuleCheckResponse.java index f131a5865..a73519ad2 100644 --- a/src/main/java/org/asteriskjava/manager/response/ModuleCheckResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/ModuleCheckResponse.java @@ -16,6 +16,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * Corresponds to a ModuleCheckAction and contains the version of the module. * @@ -24,7 +26,7 @@ * @see org.asteriskjava.manager.action.ModuleCheckAction * @since 1.0.0 */ -public class ModuleCheckResponse extends ManagerResponse { +public class ModuleCheckResponse extends ManagerActionResponse { private static final long serialVersionUID = -7253724086340850957L; private String version; diff --git a/src/main/java/org/asteriskjava/manager/response/PingResponse.java b/src/main/java/org/asteriskjava/manager/response/PingResponse.java index aee7a35b0..c6550545d 100644 --- a/src/main/java/org/asteriskjava/manager/response/PingResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/PingResponse.java @@ -16,13 +16,15 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * Corresponds to a PingAction and contains an additional (yet useless) ping property. * * @author srt * @see org.asteriskjava.manager.action.PingAction */ -public class PingResponse extends ManagerResponse { +public class PingResponse extends ManagerActionResponse { private static final long serialVersionUID = 0L; private String ping; diff --git a/src/main/java/org/asteriskjava/manager/response/SipShowPeerResponse.java b/src/main/java/org/asteriskjava/manager/response/SipShowPeerResponse.java index 1ea7987b7..b8babdf7f 100644 --- a/src/main/java/org/asteriskjava/manager/response/SipShowPeerResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/SipShowPeerResponse.java @@ -15,6 +15,8 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + import java.util.Map; /** @@ -25,7 +27,7 @@ * @see org.asteriskjava.manager.action.SipShowPeerAction * @since 1.0.0 */ -public class SipShowPeerResponse extends ManagerResponse { +public class SipShowPeerResponse extends ManagerActionResponse { private static final long serialVersionUID = 1L; private String channelType; diff --git a/src/main/java/org/asteriskjava/manager/response/SkypeBuddyResponse.java b/src/main/java/org/asteriskjava/manager/response/SkypeBuddyResponse.java index 87f40ff72..21cbe2e97 100644 --- a/src/main/java/org/asteriskjava/manager/response/SkypeBuddyResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/SkypeBuddyResponse.java @@ -16,13 +16,15 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * Corresponds to a SkypeBuddyAction and contains the details of a Skype buddy. * * @see org.asteriskjava.manager.action.SkypeBuddyAction * @since 1.0.0 */ -public class SkypeBuddyResponse extends ManagerResponse { +public class SkypeBuddyResponse extends ManagerActionResponse { private static final long serialVersionUID = 0L; private String skypename; diff --git a/src/main/java/org/asteriskjava/manager/response/SkypeLicenseStatusResponse.java b/src/main/java/org/asteriskjava/manager/response/SkypeLicenseStatusResponse.java index 7bdaaab88..493162c57 100644 --- a/src/main/java/org/asteriskjava/manager/response/SkypeLicenseStatusResponse.java +++ b/src/main/java/org/asteriskjava/manager/response/SkypeLicenseStatusResponse.java @@ -16,13 +16,15 @@ */ package org.asteriskjava.manager.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + /** * Corresponds to a SkypeLicenseStatusAction and contains the number of licensed Skype calls. * * @see org.asteriskjava.manager.action.SkypeLicenseStatusAction * @since 1.0.0 */ -public class SkypeLicenseStatusResponse extends ManagerResponse { +public class SkypeLicenseStatusResponse extends ManagerActionResponse { private static final long serialVersionUID = 0L; private Integer callsLicensed; diff --git a/src/main/java/org/asteriskjava/manager/util/EventAttributesHelper.java b/src/main/java/org/asteriskjava/manager/util/EventAttributesHelper.java index e78a4a628..afc58e4a7 100644 --- a/src/main/java/org/asteriskjava/manager/util/EventAttributesHelper.java +++ b/src/main/java/org/asteriskjava/manager/util/EventAttributesHelper.java @@ -15,9 +15,9 @@ */ package org.asteriskjava.manager.util; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.event.CdrEvent; import org.asteriskjava.manager.event.UserEvent; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.util.AstUtil; import org.asteriskjava.util.ReflectionUtil; import org.slf4j.Logger; @@ -29,6 +29,7 @@ import java.util.Map; import java.util.Set; +import static java.util.Arrays.stream; import static org.slf4j.LoggerFactory.getLogger; /** @@ -81,7 +82,7 @@ public static void setAttributes(Object target, Map attributes, // it seems silly to warn if it's a user event -- maybe it was // intentional - if (setter == null && !(target instanceof UserEvent) && !target.getClass().equals(ManagerResponse.class)) { + if (setter == null && !(target instanceof UserEvent) && !target.getClass().equals(ManagerActionResponse.class)) { //CDR has dynamic properties if (target instanceof CdrEvent) { @@ -123,6 +124,11 @@ public static void setAttributes(Object target, Map attributes, value = parseLong(entry); } else if (dataType.isAssignableFrom(int.class) || dataType.isAssignableFrom(Integer.class)) { value = parseInteger(entry); + } else if (dataType.isEnum()) { + value = stream(dataType.getEnumConstants()) + .filter(t -> t.toString().equals(entry.getValue())) + .findFirst() + .orElse(null); } else { try { Constructor constructor = dataType.getConstructor(String.class); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/BridgeAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/BridgeAction.java index b0bd14a69..650fc4ce5 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/BridgeAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/BridgeAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.pbx.Channel; public class BridgeAction extends AbstractManagerAction { @@ -13,7 +14,7 @@ public BridgeAction(final Channel lhs, final Channel rhs) { } @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.BridgeAction action = new org.asteriskjava.manager.action.BridgeAction(); action.setActionId(this.getActionId()); action.setChannel1(this._lhs.getChannelName()); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/CommandAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/CommandAction.java index 128fa5b44..2b6acb163 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/CommandAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/CommandAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; + public class CommandAction extends AbstractManagerAction { private String _command; @@ -12,7 +14,7 @@ public CommandAction() { } @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.CommandAction action = new org.asteriskjava.manager.action.CommandAction(); action.setCommand(this._command); action.setActionId(this.getActionId()); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ConfbridgeKickAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ConfbridgeKickAction.java index 21d813898..b5ef3409d 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ConfbridgeKickAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ConfbridgeKickAction.java @@ -1,5 +1,7 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; + public class ConfbridgeKickAction extends AbstractManagerAction { private final String channel; @@ -11,7 +13,7 @@ public ConfbridgeKickAction(final String room, String channel) { } @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.ConfbridgeKickAction action = new org.asteriskjava.manager.action.ConfbridgeKickAction(); action.setActionId(this.getActionId()); action.setChannel(channel); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ConfbridgeListAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ConfbridgeListAction.java index 7be64573b..4f6ec7938 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ConfbridgeListAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ConfbridgeListAction.java @@ -1,6 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; -import org.asteriskjava.manager.action.ManagerAction; +import org.asteriskjava.ami.action.ManagerAction; public class ConfbridgeListAction extends AbstractManagerAction implements EventGeneratingAction { diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/DbGetAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/DbGetAction.java index 549cc25d6..06e6648be 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/DbGetAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/DbGetAction.java @@ -1,6 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; -import org.asteriskjava.manager.action.ManagerAction; +import org.asteriskjava.ami.action.ManagerAction; public class DbGetAction extends AbstractManagerAction { diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/GetVarAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/GetVarAction.java index 1680df51f..e899b0f39 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/GetVarAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/GetVarAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.pbx.Channel; public class GetVarAction extends AbstractManagerAction { @@ -13,7 +14,7 @@ public GetVarAction(final Channel channel, String variableName) { } @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.GetVarAction action = new org.asteriskjava.manager.action.GetVarAction(); action.setActionId(this.getActionId()); action.setChannel(this._channel.getChannelName()); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/HangupAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/HangupAction.java index bbbcfdc73..3ad525359 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/HangupAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/HangupAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.pbx.Channel; public class HangupAction extends AbstractManagerAction { @@ -12,7 +13,7 @@ public HangupAction(final Channel channel) { } @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.HangupAction action = new org.asteriskjava.manager.action.HangupAction(); action.setActionId(this.getActionId()); action.setCause(this._cause); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ListCommandsAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ListCommandsAction.java index df9d7f195..7b0bc324a 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ListCommandsAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ListCommandsAction.java @@ -1,9 +1,11 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; + public class ListCommandsAction extends AbstractManagerAction { @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.ListCommandsAction action = new org.asteriskjava.manager.action.ListCommandsAction(); action.setActionId(this.getActionId()); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ManagerAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ManagerAction.java index 66dd34fbf..6ddf3ecce 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ManagerAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/ManagerAction.java @@ -3,6 +3,6 @@ public interface ManagerAction { // Converts an iManagerAction into an asterisk-java ManagerAction. - org.asteriskjava.manager.action.ManagerAction getAJAction(); + org.asteriskjava.ami.action.ManagerAction getAJAction(); } diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/MonitorAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/MonitorAction.java index 521a68804..2cb16f786 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/MonitorAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/MonitorAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.pbx.Channel; public class MonitorAction extends AbstractManagerAction { @@ -17,7 +18,7 @@ public MonitorAction(final Channel channel, final String file, final String form } @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.MonitorAction action = new org.asteriskjava.manager.action.MonitorAction( this._channel.getChannelName(), this._file, this._format, this._mix); action.setActionId(this.getActionId()); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/OriginateAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/OriginateAction.java index 75184235c..a822beb16 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/OriginateAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/OriginateAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.pbx.CallerID; import org.asteriskjava.pbx.Channel; import org.asteriskjava.pbx.EndPoint; @@ -47,7 +48,7 @@ public String toString() { } @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.OriginateAction action = new org.asteriskjava.manager.action.OriginateAction(); action.setActionId(getActionId()); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/PingAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/PingAction.java index 9c2e46b02..835001b1d 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/PingAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/PingAction.java @@ -1,9 +1,11 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; + public class PingAction extends AbstractManagerAction { @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.PingAction action = new org.asteriskjava.manager.action.PingAction(); action.setActionId(this.getActionId()); return action; diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/PlayDtmfAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/PlayDtmfAction.java index 7c0082fc5..b6059bf0f 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/PlayDtmfAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/PlayDtmfAction.java @@ -1,6 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; -import org.asteriskjava.manager.action.ManagerAction; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.pbx.Channel; import org.asteriskjava.pbx.DTMFTone; diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/RedirectAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/RedirectAction.java index d2dd82d2f..f9991121d 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/RedirectAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/RedirectAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.pbx.Channel; import org.asteriskjava.pbx.EndPoint; @@ -41,7 +42,7 @@ public String toString() { } @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.RedirectAction action = new org.asteriskjava.manager.action.RedirectAction(); action.setActionId(this.getActionId()); action.setChannel(this._channel.getChannelName().toLowerCase()); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SetVarAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SetVarAction.java index 9657f32aa..2928c6c01 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SetVarAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SetVarAction.java @@ -1,5 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.pbx.Channel; public class SetVarAction extends AbstractManagerAction { @@ -15,7 +16,7 @@ public SetVarAction(final Channel channel, final String variableName, final Stri } @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.SetVarAction action = new org.asteriskjava.manager.action.SetVarAction(); action.setActionId(this.getActionId()); action.setChannel(this._channel.getChannelName()); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SipPeersAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SipPeersAction.java index 4ff04c8f0..aeb50d200 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SipPeersAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SipPeersAction.java @@ -1,12 +1,14 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; +import org.asteriskjava.ami.action.ManagerAction; + public class SipPeersAction extends AbstractManagerAction implements EventGeneratingAction { public SipPeersAction() { } @Override - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { return new org.asteriskjava.manager.action.SipPeersAction(); } diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SipShowPeerAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SipShowPeerAction.java index 1b524b8e1..84a6646f8 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SipShowPeerAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/SipShowPeerAction.java @@ -1,6 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; -import org.asteriskjava.manager.action.ManagerAction; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.pbx.EndPoint; public class SipShowPeerAction extends AbstractManagerAction { diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/StatusAction.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/StatusAction.java index be7ab9b1f..db257fef8 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/StatusAction.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/actions/StatusAction.java @@ -1,6 +1,7 @@ package org.asteriskjava.pbx.asterisk.wrap.actions; import org.asteriskjava.AsteriskVersion; +import org.asteriskjava.ami.action.ManagerAction; import org.asteriskjava.pbx.Channel; import org.asteriskjava.pbx.PBXFactory; import org.asteriskjava.pbx.internal.core.AsteriskPBX; @@ -23,7 +24,7 @@ public StatusAction(Channel channel) { this._channel = null; } - public org.asteriskjava.manager.action.ManagerAction getAJAction() { + public ManagerAction getAJAction() { final org.asteriskjava.manager.action.StatusAction action; if (_channel == null) action = new org.asteriskjava.manager.action.StatusAction(); diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/CommandResponse.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/CommandResponse.java index 135e76138..af55ba501 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/CommandResponse.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/CommandResponse.java @@ -1,5 +1,7 @@ package org.asteriskjava.pbx.asterisk.wrap.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + import java.util.Collections; import java.util.List; @@ -8,7 +10,7 @@ public class CommandResponse extends ManagerResponse { private List result; private boolean error; - public CommandResponse(org.asteriskjava.manager.response.ManagerResponse response) { + public CommandResponse(ManagerActionResponse response) { super(response); if (response instanceof org.asteriskjava.manager.response.CommandResponse) { diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/ManagerError.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/ManagerError.java index 68479b634..2b2562edf 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/ManagerError.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/ManagerError.java @@ -1,8 +1,10 @@ package org.asteriskjava.pbx.asterisk.wrap.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; + public class ManagerError extends CommandResponse { - public ManagerError(org.asteriskjava.manager.response.ManagerResponse error) { + public ManagerError(ManagerActionResponse error) { super(error); } diff --git a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/ManagerResponse.java b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/ManagerResponse.java index bd5715997..62b1f9dc1 100644 --- a/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/ManagerResponse.java +++ b/src/main/java/org/asteriskjava/pbx/asterisk/wrap/response/ManagerResponse.java @@ -1,5 +1,6 @@ package org.asteriskjava.pbx.asterisk.wrap.response; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.response.ManagerError; import java.util.Date; @@ -22,11 +23,11 @@ public class ManagerResponse { final private String _uniqueId; final private Map _attributes; - public ManagerResponse(org.asteriskjava.manager.response.ManagerResponse response) { - this._dateReceived = response.getDateReceived(); + public ManagerResponse(ManagerActionResponse response) { + this._dateReceived = Date.from(response.getDateReceived()); this._actionId = response.getActionId(); this._server = response.getServer(); - this._response = response.getResponse(); + this._response = response.getResponse().toString(); this._eventList = response.getEventList(); this._uniqueId = response.getUniqueId(); this._attributes = response.getAttributes(); diff --git a/src/main/java/org/asteriskjava/pbx/internal/core/CoherentEventFactory.java b/src/main/java/org/asteriskjava/pbx/internal/core/CoherentEventFactory.java index 22941bf19..971501570 100644 --- a/src/main/java/org/asteriskjava/pbx/internal/core/CoherentEventFactory.java +++ b/src/main/java/org/asteriskjava/pbx/internal/core/CoherentEventFactory.java @@ -1,5 +1,6 @@ package org.asteriskjava.pbx.internal.core; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.pbx.asterisk.wrap.actions.ManagerAction; import org.asteriskjava.pbx.asterisk.wrap.events.ManagerEvent; import org.asteriskjava.pbx.asterisk.wrap.events.ResponseEvent; @@ -136,7 +137,7 @@ public static ResponseEvent build(org.asteriskjava.manager.event.ResponseEvent e return response; } - public static ManagerResponse build(org.asteriskjava.manager.response.ManagerResponse response) { + public static ManagerResponse build(ManagerActionResponse response) { ManagerResponse result; if (response instanceof org.asteriskjava.manager.response.CommandResponse) { result = new CommandResponse(response); @@ -149,8 +150,8 @@ public static ManagerResponse build(org.asteriskjava.manager.response.ManagerRes } - public static org.asteriskjava.manager.action.ManagerAction build(ManagerAction action) { - org.asteriskjava.manager.action.ManagerAction result = null; + public static org.asteriskjava.ami.action.ManagerAction build(ManagerAction action) { + org.asteriskjava.ami.action.ManagerAction result = null; // final Class // target = CoherentEventFactory.mapActions.get(action.getClass()); diff --git a/src/main/java/org/asteriskjava/pbx/internal/core/CoherentManagerConnection.java b/src/main/java/org/asteriskjava/pbx/internal/core/CoherentManagerConnection.java index fd73f2fd8..f02b7dfe6 100644 --- a/src/main/java/org/asteriskjava/pbx/internal/core/CoherentManagerConnection.java +++ b/src/main/java/org/asteriskjava/pbx/internal/core/CoherentManagerConnection.java @@ -1,6 +1,7 @@ package org.asteriskjava.pbx.internal.core; import org.asteriskjava.AsteriskVersion; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.*; import org.asteriskjava.pbx.*; import org.asteriskjava.pbx.asterisk.wrap.actions.*; @@ -282,9 +283,9 @@ public static ManagerResponse sendAction(final ManagerAction action, final int t CoherentManagerConnection.getInstance(); if ((CoherentManagerConnection.managerConnection != null) && (CoherentManagerConnection.managerConnection.getState() == ManagerConnectionState.CONNECTED)) { - final org.asteriskjava.manager.action.ManagerAction ajAction = action.getAJAction(); + final org.asteriskjava.ami.action.ManagerAction ajAction = action.getAJAction(); - org.asteriskjava.manager.response.ManagerResponse response = CoherentManagerConnection.managerConnection + ManagerActionResponse response = CoherentManagerConnection.managerConnection .sendAction(ajAction, timeout); ManagerResponse convertedResponse = null; diff --git a/src/test/java/org/asteriskjava/manager/internal/ActionBuilderImplTest.java b/src/test/java/org/asteriskjava/manager/internal/ActionBuilderImplTest.java index a36214a46..c15b17772 100644 --- a/src/test/java/org/asteriskjava/manager/internal/ActionBuilderImplTest.java +++ b/src/test/java/org/asteriskjava/manager/internal/ActionBuilderImplTest.java @@ -17,6 +17,7 @@ package org.asteriskjava.manager.internal; import org.asteriskjava.AsteriskVersion; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.action.*; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -40,6 +41,7 @@ void testBuildAction() { String actual; myAction = new MyAction(); + myAction.setActionId("id-1"); myAction.setFirstProperty("first value"); myAction.setSecondProperty(2); myAction.setNonPublicProperty("private"); @@ -50,7 +52,7 @@ void testBuildAction() { assertTrue(actual.indexOf("firstproperty: first value\r\n") >= 0, "First property missing"); assertTrue(actual.indexOf("secondproperty: 2\r\n") >= 0, "Second property missing"); assertTrue(actual.endsWith("\r\n\r\n"), "Missing trailing CRNL CRNL"); - assertEquals(61, actual.length(), "Incorrect length"); + assertEquals(77, actual.length(), "Incorrect length"); } @Test @@ -59,6 +61,7 @@ void testBuildActionWithNullValue() { String actual; myAction = new MyAction(); + myAction.setActionId("id-1"); myAction.setFirstProperty("first value"); actual = actionBuilder.buildAction(myAction); @@ -66,7 +69,7 @@ void testBuildActionWithNullValue() { assertTrue(actual.indexOf("action: My\r\n") >= 0, "Action name missing"); assertTrue(actual.indexOf("firstproperty: first value\r\n") >= 0, "First property missing"); assertTrue(actual.endsWith("\r\n\r\n"), "Missing trailing CRNL CRNL"); - assertEquals(42, actual.length(), "Incorrect length"); + assertEquals(58, actual.length(), "Incorrect length"); } @Test diff --git a/src/test/java/org/asteriskjava/manager/internal/AnnotatedAction.java b/src/test/java/org/asteriskjava/manager/internal/AnnotatedAction.java index c68cd2e9c..3c4720a36 100644 --- a/src/test/java/org/asteriskjava/manager/internal/AnnotatedAction.java +++ b/src/test/java/org/asteriskjava/manager/internal/AnnotatedAction.java @@ -16,8 +16,8 @@ */ package org.asteriskjava.manager.internal; +import org.asteriskjava.ami.action.AbstractManagerAction; import org.asteriskjava.manager.AsteriskMapping; -import org.asteriskjava.manager.action.AbstractManagerAction; public class AnnotatedAction extends AbstractManagerAction { private static final long serialVersionUID = 1L; diff --git a/src/test/java/org/asteriskjava/manager/internal/ManagerConnectionImplTest.java b/src/test/java/org/asteriskjava/manager/internal/ManagerConnectionImplTest.java index 2565925a9..8f4306c95 100644 --- a/src/test/java/org/asteriskjava/manager/internal/ManagerConnectionImplTest.java +++ b/src/test/java/org/asteriskjava/manager/internal/ManagerConnectionImplTest.java @@ -17,6 +17,8 @@ package org.asteriskjava.manager.internal; import org.asteriskjava.AsteriskVersion; +import org.asteriskjava.ami.action.response.ManagerActionResponse; +import org.asteriskjava.ami.action.response.ResponseType; import org.asteriskjava.manager.AuthenticationFailedException; import org.asteriskjava.manager.ManagerConnectionState; import org.asteriskjava.manager.ManagerEventListener; @@ -29,7 +31,6 @@ import org.asteriskjava.manager.event.DisconnectEvent; import org.asteriskjava.manager.event.ManagerEvent; import org.asteriskjava.manager.event.NewChannelEvent; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.util.SocketConnectionFacade; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -321,7 +322,7 @@ void testSendActionWhenNotConnected() throws Exception { @Test void testSendAction() throws Exception { StatusAction statusAction; - ManagerResponse response; + ManagerActionResponse response; statusAction = new StatusAction(); statusAction.setActionId("123"); @@ -333,7 +334,7 @@ void testSendAction() throws Exception { assertEquals("123", statusAction.getActionId(), "incorrect actionId in action"); assertEquals("123", response.getActionId(), "incorrect actionId in response"); - assertEquals("Success", response.getResponse(), "incorrect response"); + assertEquals(ResponseType.Success, response.getResponse(), "incorrect response"); assertEquals(1, mockWriter.otherActionsSent, "other actions not sent 1 time"); } @@ -363,12 +364,12 @@ void testSendActionTimeout() throws Exception { @Test void testDispatchResponseUnexpectedResponse() { - ManagerResponse response; + ManagerActionResponse response; - response = new ManagerResponse(); + response = new ManagerActionResponse(); // internalActionId: 123_0 response.setActionId("123_0-abc"); - response.setResponse("Success"); + response.setResponse(ResponseType.Success); // expected result is ignoring the response and logging mc.dispatchResponse(response, null); @@ -376,11 +377,11 @@ void testDispatchResponseUnexpectedResponse() { @Test void testDispatchResponseMissingInternalActionId() { - ManagerResponse response; + ManagerActionResponse response; - response = new ManagerResponse(); + response = new ManagerActionResponse(); response.setActionId("abc"); - response.setResponse("Success"); + response.setResponse(ResponseType.Success); // expected result is ignoring the response and logging mc.dispatchResponse(response, null); @@ -388,11 +389,11 @@ void testDispatchResponseMissingInternalActionId() { @Test void testDispatchResponseNullActionId() { - ManagerResponse response; + ManagerActionResponse response; - response = new ManagerResponse(); + response = new ManagerActionResponse(); response.setActionId(null); - response.setResponse("Success"); + response.setResponse(ResponseType.Success); // expected result is ignoring the response and logging mc.dispatchResponse(response, null); diff --git a/src/test/java/org/asteriskjava/manager/internal/ManagerReaderImplTest.java b/src/test/java/org/asteriskjava/manager/internal/ManagerReaderImplTest.java index eabb78d67..bc0e1b9fa 100644 --- a/src/test/java/org/asteriskjava/manager/internal/ManagerReaderImplTest.java +++ b/src/test/java/org/asteriskjava/manager/internal/ManagerReaderImplTest.java @@ -16,9 +16,10 @@ */ package org.asteriskjava.manager.internal; +import org.asteriskjava.ami.action.response.ManagerActionResponse; +import org.asteriskjava.ami.action.response.ResponseType; import org.asteriskjava.manager.event.*; import org.asteriskjava.manager.response.CommandResponse; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.util.DateUtil; import org.asteriskjava.util.SocketConnectionFacade; import org.junit.jupiter.api.AfterEach; @@ -207,14 +208,14 @@ void testRunReceivingResponse() throws Exception { assertEquals(1, dispatcher.dispatchedResponses.size(), "not exactly one response dispatched"); - assertEquals(ManagerResponse.class, dispatcher.dispatchedResponses.get(0).getClass(), "first response must be a ManagerResponse"); - assertEquals("Success", dispatcher.dispatchedResponses.get(0).getResponse(), "ManagerResponse contains incorrect response"); + assertEquals(ManagerActionResponse.class, dispatcher.dispatchedResponses.get(0).getClass(), "first response must be a ManagerResponse"); + assertEquals(ResponseType.Success, dispatcher.dispatchedResponses.get(0).getResponse(), "ManagerResponse contains incorrect response"); assertEquals("Authentication accepted", dispatcher.dispatchedResponses.get(0).getMessage(), "ManagerResponse contains incorrect message"); assertEquals("Authentication accepted", dispatcher.dispatchedResponses.get(0).getAttribute("MESSAGE"), "ManagerResponse contains incorrect message (via getAttribute)"); - assertEquals(now, dispatcher.dispatchedResponses.get(0).getDateReceived(), "ManagerResponse contains incorrect dateReceived"); + assertEquals(now, Date.from(dispatcher.dispatchedResponses.get(0).getDateReceived()), "ManagerResponse contains incorrect dateReceived"); assertEquals(1, dispatcher.dispatchedEvents.size(), "not exactly one events dispatched"); @@ -243,7 +244,7 @@ void testRunReceivingCommandResponse() throws Exception { assertEquals(CommandResponse.class, dispatcher.dispatchedResponses.get(0).getClass(), "first response must be a CommandResponse"); - assertEquals("Follows", dispatcher.dispatchedResponses.get(0).getResponse(), "CommandResponse contains incorrect response"); + assertEquals(ResponseType.Follows, dispatcher.dispatchedResponses.get(0).getResponse(), "CommandResponse contains incorrect response"); assertEquals("678#12345", dispatcher.dispatchedResponses.get(0).getActionId(), "CommandResponse contains incorrect actionId"); @@ -251,7 +252,7 @@ void testRunReceivingCommandResponse() throws Exception { assertEquals(result, ((CommandResponse) dispatcher.dispatchedResponses.get(0)).getResult(), "CommandResponse contains incorrect result"); - assertEquals(now, dispatcher.dispatchedResponses.get(0).getDateReceived(), "CommandResponse contains incorrect dateReceived"); + assertEquals(now, Date.from(dispatcher.dispatchedResponses.get(0).getDateReceived()), "CommandResponse contains incorrect dateReceived"); } @Test @@ -270,15 +271,15 @@ void testRunCatchingIOException() throws Exception { private class MockedDispatcher implements Dispatcher { List dispatchedEvents; - List dispatchedResponses; + List dispatchedResponses; public MockedDispatcher() { this.dispatchedEvents = new ArrayList(); - this.dispatchedResponses = new ArrayList(); + this.dispatchedResponses = new ArrayList(); } @Override - public void dispatchResponse(ManagerResponse response, Integer requiredHandlingTime) { + public void dispatchResponse(ManagerActionResponse response, Integer requiredHandlingTime) { dispatchedResponses.add(response); } diff --git a/src/test/java/org/asteriskjava/manager/internal/ManagerReaderMock.java b/src/test/java/org/asteriskjava/manager/internal/ManagerReaderMock.java index e4bc3332b..7bbdee101 100644 --- a/src/test/java/org/asteriskjava/manager/internal/ManagerReaderMock.java +++ b/src/test/java/org/asteriskjava/manager/internal/ManagerReaderMock.java @@ -16,8 +16,8 @@ */ package org.asteriskjava.manager.internal; +import org.asteriskjava.ami.action.response.ManagerActionResponse; import org.asteriskjava.manager.event.ManagerEvent; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.util.SocketConnectionFacade; import java.io.IOException; @@ -39,7 +39,7 @@ public void setSocket(SocketConnectionFacade socket) { setSocketCalls++; } - public void expectResponseClass(String actionId, Class responseClass) { + public void expectResponseClass(String actionId, Class responseClass) { } diff --git a/src/test/java/org/asteriskjava/manager/internal/ManagerWriterMock.java b/src/test/java/org/asteriskjava/manager/internal/ManagerWriterMock.java index 6dedf480c..b6efdfc56 100644 --- a/src/test/java/org/asteriskjava/manager/internal/ManagerWriterMock.java +++ b/src/test/java/org/asteriskjava/manager/internal/ManagerWriterMock.java @@ -17,14 +17,16 @@ package org.asteriskjava.manager.internal; import org.asteriskjava.AsteriskVersion; -import org.asteriskjava.manager.action.ChallengeAction; +import org.asteriskjava.ami.action.AuthType; +import org.asteriskjava.ami.action.ChallengeAction; +import org.asteriskjava.ami.action.ManagerAction; +import org.asteriskjava.ami.action.response.ChallengeManagerActionResponse; +import org.asteriskjava.ami.action.response.ManagerActionResponse; +import org.asteriskjava.ami.action.response.ResponseType; import org.asteriskjava.manager.action.LoginAction; import org.asteriskjava.manager.action.LogoffAction; -import org.asteriskjava.manager.action.ManagerAction; import org.asteriskjava.manager.event.ProtocolIdentifierReceivedEvent; -import org.asteriskjava.manager.response.ChallengeResponse; import org.asteriskjava.manager.response.ManagerError; -import org.asteriskjava.manager.response.ManagerResponse; import org.asteriskjava.util.DateUtil; import org.asteriskjava.util.SocketConnectionFacade; @@ -96,21 +98,21 @@ public void run() { public void sendAction(ManagerAction action, String internalActionId) throws IOException { if (action instanceof ChallengeAction) { ChallengeAction challengeAction = (ChallengeAction) action; - String authType = challengeAction.getAuthType(); + AuthType authType = challengeAction.getAuthType(); - if (!authType.equals("MD5")) { + if (authType != AuthType.MD5) { throw new RuntimeException("Expected authType 'MD5' got '" + authType + "'"); } challengeActionsSent++; if (sendResponse) { - ChallengeResponse challengeResponse; + ChallengeManagerActionResponse challengeManagerActionResponse; - challengeResponse = new ChallengeResponse(); - challengeResponse.setActionId(ManagerUtil.addInternalActionId(action.getActionId(), internalActionId)); - challengeResponse.setChallenge(CHALLENGE); - dispatchLater(challengeResponse); + challengeManagerActionResponse = new ChallengeManagerActionResponse(); + challengeManagerActionResponse.setActionId(ManagerUtil.addInternalActionId(action.getActionId(), internalActionId)); + challengeManagerActionResponse.setChallenge(CHALLENGE); + dispatchLater(challengeManagerActionResponse); } } else if (action instanceof LoginAction) { @@ -130,17 +132,17 @@ public void sendAction(ManagerAction action, String internalActionId) throws IOE loginActionsSent++; if (sendResponse) { - ManagerResponse loginResponse; + ManagerActionResponse loginResponse; // let testReconnectWithKeepAliveAfterAuthenticationFailure // succeed after // 3 unsuccessful attempts if (key.equals(expectedKey) || loginActionsSent > 2) { - loginResponse = new ManagerResponse(); - loginResponse.setResponse("Success"); + loginResponse = new ManagerActionResponse(); + loginResponse.setResponse(ResponseType.Success); } else { loginResponse = new ManagerError(); - loginResponse.setResponse("Error"); + loginResponse.setResponse(ResponseType.Error); loginResponse.setMessage("Authentication failed"); } loginResponse.setActionId(ManagerUtil.addInternalActionId(action.getActionId(), internalActionId)); @@ -150,28 +152,28 @@ public void sendAction(ManagerAction action, String internalActionId) throws IOE logoffActionsSent++; if (sendResponse) { - ManagerResponse response; + ManagerActionResponse response; - response = new ManagerResponse(); + response = new ManagerActionResponse(); response.setActionId(ManagerUtil.addInternalActionId(action.getActionId(), internalActionId)); - response.setResponse("Success"); + response.setResponse(ResponseType.Success); dispatchLater(response); } } else { otherActionsSent++; if (sendResponse) { - ManagerResponse response; + ManagerActionResponse response; - response = new ManagerResponse(); + response = new ManagerActionResponse(); response.setActionId(ManagerUtil.addInternalActionId(action.getActionId(), internalActionId)); - response.setResponse("Success"); + response.setResponse(ResponseType.Success); dispatchLater(response); } } } - private void dispatchLater(final ManagerResponse response) { + private void dispatchLater(final ManagerActionResponse response) { Thread future = new Thread(new Runnable() { public void run() { try { diff --git a/src/test/java/org/asteriskjava/manager/internal/ResponseBuilderImplTest.java b/src/test/java/org/asteriskjava/manager/internal/ResponseBuilderImplTest.java index 65cd4d8d1..8bbf69e73 100644 --- a/src/test/java/org/asteriskjava/manager/internal/ResponseBuilderImplTest.java +++ b/src/test/java/org/asteriskjava/manager/internal/ResponseBuilderImplTest.java @@ -16,7 +16,12 @@ */ package org.asteriskjava.manager.internal; -import org.asteriskjava.manager.response.*; +import org.asteriskjava.ami.action.response.ChallengeManagerActionResponse; +import org.asteriskjava.ami.action.response.ManagerActionResponse; +import org.asteriskjava.ami.action.response.ResponseType; +import org.asteriskjava.manager.response.ExtensionStateResponse; +import org.asteriskjava.manager.response.MailboxCountResponse; +import org.asteriskjava.manager.response.MailboxStatusResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -37,65 +42,64 @@ void setUp() { @Test void testBuildResponse() { - ManagerResponse response; + ManagerActionResponse response; attributes.put("response", "Success"); - response = responseBuilder.buildResponse(ManagerResponse.class, attributes); - assertEquals(ManagerResponse.class, response.getClass(), "Response of wrong type"); - assertEquals("Success", response.getResponse(), "Response not set correctly"); + response = responseBuilder.buildResponse(ManagerActionResponse.class, attributes); + assertEquals(ManagerActionResponse.class, response.getClass(), "Response of wrong type"); + assertEquals(ResponseType.Success, response.getResponse(), "Response not set correctly"); } @Test void testBuildResponseWithoutResponseClass() { - ManagerResponse response; + ManagerActionResponse response; attributes.put("response", "Success"); response = responseBuilder.buildResponse(null, attributes); - assertEquals(ManagerResponse.class, response.getClass(), "Response of wrong type"); - assertEquals("Success", response.getResponse(), "Response not set correctly"); + assertEquals(ManagerActionResponse.class, response.getClass(), "Response of wrong type"); + assertEquals(ResponseType.Success, response.getResponse(), "Response not set correctly"); } @Test void testBuildError() { - ManagerResponse response; + ManagerActionResponse response; attributes.put("response", "Error"); attributes.put("message", "Missing action in request"); - response = responseBuilder.buildResponse(ManagerResponse.class, attributes); - assertEquals(ManagerError.class, response.getClass(), "Response of wrong type"); + response = responseBuilder.buildResponse(ManagerActionResponse.class, attributes); assertEquals("Missing action in request", response.getMessage(), "Message not set correctly"); } @Test void testBuildErrorWithActionId() { - ManagerResponse response; + ManagerActionResponse response; attributes.put("response", "Error"); attributes.put("actionid", "1234"); attributes.put("message", "Missing action in request"); - response = responseBuilder.buildResponse(ManagerResponse.class, attributes); + response = responseBuilder.buildResponse(ManagerActionResponse.class, attributes); assertEquals("1234", response.getActionId(), "ActionId not set correctly"); } @Test void testBuildChallengeResponse() { - ManagerResponse response; + ManagerActionResponse response; attributes.put("response", "Success"); attributes.put("challenge", "131494410"); - response = responseBuilder.buildResponse(ChallengeResponse.class, attributes); - assertEquals(ChallengeResponse.class, response.getClass(), "Response of wrong type"); - assertEquals("131494410", ((ChallengeResponse) response).getChallenge(), "Challenge not set correctly"); + response = responseBuilder.buildResponse(ChallengeManagerActionResponse.class, attributes); + assertEquals(ChallengeManagerActionResponse.class, response.getClass(), "Response of wrong type"); + assertEquals("131494410", ((ChallengeManagerActionResponse) response).getChallenge(), "Challenge not set correctly"); } @Test void testBuildMailboxStatusResponse() { - ManagerResponse response; + ManagerActionResponse response; attributes.put("response", "Success"); attributes.put("message", "Mailbox Status"); @@ -112,7 +116,7 @@ void testBuildMailboxStatusResponse() { @Test void testBuildMailboxStatusResponseWithNoWaiting() { - ManagerResponse response; + ManagerActionResponse response; attributes.put("response", "Success"); attributes.put("message", "Mailbox Status"); @@ -129,7 +133,7 @@ void testBuildMailboxStatusResponseWithNoWaiting() { @Test void testBuildMailboxCountResponse() { - ManagerResponse response; + ManagerActionResponse response; attributes.put("response", "Success"); attributes.put("message", "Mailbox Message Count"); @@ -148,7 +152,7 @@ void testBuildMailboxCountResponse() { @Test void testBuildExtensionStateResponse() { - ManagerResponse response; + ManagerActionResponse response; attributes.put("response", "Success"); attributes.put("message", "Extension Status");