├── .classpath ├── .github └── workflows │ └── maven.yml ├── .gitignore ├── .project ├── .settings ├── org.eclipse.jdt.core.prefs └── org.eclipse.m2e.core.prefs ├── LICENSE ├── Readme.md ├── logs ├── debug.log └── error.log ├── pom.xml └── src └── main ├── java └── io │ └── freeswitch │ ├── ChannelVariable.java │ ├── ConnectionFailure.java │ ├── ExecutionHandler.java │ ├── IEndPoint.java │ ├── IProtocolListener.java │ ├── SayGender.java │ ├── SayMethod.java │ ├── SayType.java │ ├── codec │ ├── FreeSwitchDecoder.java │ ├── FreeSwitchMessageHeaders.java │ └── HeaderParser.java │ ├── command │ ├── ApiCommand.java │ ├── AuthCommand.java │ ├── BaseCommand.java │ ├── BgApiCommand.java │ ├── ConnectCommand.java │ ├── DivertEventsCommand.java │ ├── EventCommand.java │ ├── EventsCommand.java │ ├── ExitCommand.java │ ├── FilterCommand.java │ ├── GetVarCommand.java │ ├── HangupCommand.java │ ├── LogCommand.java │ ├── MyEventsCommand.java │ ├── NixEventCommand.java │ ├── NoEventsCommand.java │ ├── NologCommand.java │ ├── OriginateCommand.java │ ├── PlayAndGetDigitsCommand.java │ ├── PlaybackCommand.java │ ├── RecordCommand.java │ ├── ResumeCommand.java │ ├── SayCommand.java │ ├── SchedApiCommand.java │ ├── SendMsgCommand.java │ ├── SetVarCommand.java │ ├── SleepCommand.java │ └── SpeakCommand.java │ ├── common │ ├── HangupCauses.java │ ├── LogLevels.java │ └── UuidFactory.java │ ├── event │ ├── AbstractEvent.java │ ├── BackgroundJob.java │ ├── ChannelBridge.java │ ├── ChannelExecute.java │ ├── ChannelExecuteComplete.java │ ├── ChannelHangup.java │ ├── ChannelHangupComplete.java │ ├── ChannelProgress.java │ ├── ChannelProgressMedia.java │ ├── ChannelUnbridge.java │ ├── Dtmf.java │ ├── EslEvent.java │ ├── EventHeaders.java │ ├── IEventsListener.java │ ├── RecordStop.java │ └── SessionHeartbeat.java │ ├── inbound │ ├── DefaultFreeSwitchHandler.java │ ├── DefaultInboundPipelineFactory.java │ ├── FreeSwitchHandler.java │ ├── FreeSwitchPipelineFactory.java │ ├── FreeSwitchServer.java │ └── FreeSwitchSession.java │ ├── message │ ├── CommandReply.java │ └── FreeSwitchMessage.java │ └── outbound │ ├── DefaultFreeSwitchClientHandler.java │ ├── DefaultFreeSwitchClientPipelineFactory.java │ ├── FreeSwitchClient.java │ └── FreeSwitchClientHandler.java └── resources └── logback.xml /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /.github/workflows/maven.yml: -------------------------------------------------------------------------------- 1 | # This workflow will build a Java project with Maven 2 | # For more information see: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven 3 | 4 | name: ci 5 | 6 | on: 7 | push: 8 | branches: [ master ] 9 | pull_request: 10 | branches: [ master ] 11 | 12 | jobs: 13 | build: 14 | 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - uses: actions/checkout@v2 19 | - name: Set up JDK 1.8 20 | uses: actions/setup-java@v1 21 | with: 22 | java-version: 1.8 23 | - name: Build with Maven 24 | run: mvn compile 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | *.jar 8 | *.war 9 | *.ear 10 | 11 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 12 | hs_err_pid* 13 | /target/ 14 | .idea 15 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | io.freeswitch 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.m2e.core.maven2Builder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.m2e.core.maven2Nature 22 | 23 | 24 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5 3 | org.eclipse.jdt.core.compiler.compliance=1.5 4 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 5 | org.eclipse.jdt.core.compiler.source=1.5 6 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Java FreeSwitch library 2 | ======================================== 3 | 4 | ## **Overview** 5 | This library helps interact with the FreeSwitch via its mod_event_socket. For more information about the mod_event_socket refer to [FreeSwitch web site](https://freeswitch.org/confluence/display/FREESWITCH/mod_event_socket). Also it offers more flexibility for extension by any other developer who picks the source code. 6 | 7 | In its current state it can help build IVR applications more quickly. 8 | 9 | ## **Features** 10 | The framework in its current state can be used to interact with FreeSwitch easily in: 11 | * Inbound mode [Event Socket Inbound mode](https://freeswitch.org/confluence/display/FREESWITCH/mod_event_socket#mod_event_socket-Inbound) 12 | * Outbound mode [Event Socket Outbound mode](https://wiki.freeswitch.org/wiki/Event_Socket_Outbound) 13 | * One good thing it has is that you can implement your own FreeSwitch message decoder if you do not want to use the built-in ones 14 | 15 | ## **License** 16 | This software is licensed under the Apache 2 license, quoted below. 17 | 18 | Copyright © 2015 19 | 20 | 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 21 | 22 | [http://www.apache.org/licenses/LICENSE-2.0] 23 | 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. 24 | 25 | -------------------------------------------------------------------------------- /logs/debug.log: -------------------------------------------------------------------------------- 1 | 2015-11-19 11:05:08 - Client connecting .. 2 | 2015-11-19 11:05:08 - read header line [Content-Type: auth/request] 3 | 2015-11-19 11:05:08 - adding header [CONTENT_TYPE] [auth/request] 4 | 2015-11-19 11:05:08 - read header line [] 5 | 2015-11-19 11:05:08 - Received message: [FreeSwitchMessage: contentType=[auth/request] headers=1, body=0 lines.] 6 | 2015-11-19 11:05:08 - Auth request received [FreeSwitchMessage: contentType=[auth/request] headers=1, body=0 lines.] 7 | 2015-11-19 11:05:08 - Auth requested, sending [auth *****] 8 | 2015-11-19 11:05:08 - Command sent to freeSwitch [auth ClueCon 9 | 10 | ] 11 | 2015-11-19 11:05:08 - read header line [Content-Type: command/reply] 12 | 2015-11-19 11:05:08 - adding header [CONTENT_TYPE] [command/reply] 13 | 2015-11-19 11:05:08 - read header line [Reply-Text: +OK accepted] 14 | 2015-11-19 11:05:08 - adding header [REPLY_TEXT] [+OK accepted] 15 | 2015-11-19 11:05:08 - read header line [] 16 | 2015-11-19 11:05:08 - Received message: [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 17 | 2015-11-19 11:05:08 - Command reply received [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 18 | 2015-11-19 11:05:08 - Auth response [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 19 | 2015-11-19 11:05:08 - Auth response success=true, message=[+OK accepted] 20 | 2015-11-19 11:05:08 - Client connected .. 21 | 2015-11-19 11:05:08 - Command sent to freeSwitch [event plain all 22 | 23 | ] 24 | 2015-11-19 11:05:08 - read header line [Content-Type: command/reply] 25 | 2015-11-19 11:05:08 - adding header [CONTENT_TYPE] [command/reply] 26 | 2015-11-19 11:05:08 - read header line [Reply-Text: +OK event listener enabled plain] 27 | 2015-11-19 11:05:08 - adding header [REPLY_TEXT] [+OK event listener enabled plain] 28 | 2015-11-19 11:05:08 - read header line [] 29 | 2015-11-19 11:05:08 - Received message: [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 30 | 2015-11-19 11:05:08 - Command reply received [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 31 | 2015-11-19 11:05:08 - Command sent to freeSwitch [filter Event-HeaderName heartbeat 32 | 33 | ] 34 | 2015-11-19 11:05:08 - read header line [Content-Type: command/reply] 35 | 2015-11-19 11:05:08 - adding header [CONTENT_TYPE] [command/reply] 36 | 2015-11-19 11:05:08 - read header line [Reply-Text: +OK filter added. [Event-HeaderName]=[heartbeat]] 37 | 2015-11-19 11:05:08 - adding header [REPLY_TEXT] [+OK filter added. [Event-HeaderName]=[heartbeat]] 38 | 2015-11-19 11:05:08 - read header line [] 39 | 2015-11-19 11:05:08 - Received message: [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 40 | 2015-11-19 11:05:08 - Command reply received [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 41 | 2015-11-19 11:05:08 - Command sent to freeSwitch [noevents 42 | 43 | ] 44 | 2015-11-19 11:05:08 - read header line [Content-Type: command/reply] 45 | 2015-11-19 11:05:08 - adding header [CONTENT_TYPE] [command/reply] 46 | 2015-11-19 11:05:08 - read header line [Reply-Text: +OK no longer listening for events] 47 | 2015-11-19 11:05:08 - adding header [REPLY_TEXT] [+OK no longer listening for events] 48 | 2015-11-19 11:05:08 - read header line [] 49 | 2015-11-19 11:05:08 - Received message: [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 50 | 2015-11-19 11:05:08 - Command reply received [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 51 | 2015-11-19 11:05:08 - Command sent to freeSwitch [event plain all 52 | 53 | ] 54 | 2015-11-19 11:05:08 - read header line [Content-Type: command/reply] 55 | 2015-11-19 11:05:08 - adding header [CONTENT_TYPE] [command/reply] 56 | 2015-11-19 11:05:08 - read header line [Reply-Text: +OK event listener enabled plain] 57 | 2015-11-19 11:05:08 - adding header [REPLY_TEXT] [+OK event listener enabled plain] 58 | 2015-11-19 11:05:08 - read header line [] 59 | 2015-11-19 11:05:08 - Received message: [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 60 | 2015-11-19 11:05:08 - Command reply received [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 61 | 2015-11-19 11:05:08 - Command sent to freeSwitch [filter Event-HeaderName heartbeat 62 | 63 | ] 64 | 2015-11-19 11:05:08 - read header line [Content-Type: command/reply] 65 | 2015-11-19 11:05:08 - adding header [CONTENT_TYPE] [command/reply] 66 | 2015-11-19 11:05:08 - read header line [Reply-Text: +OK filter added. [Event-HeaderName]=[heartbeat]] 67 | 2015-11-19 11:05:08 - adding header [REPLY_TEXT] [+OK filter added. [Event-HeaderName]=[heartbeat]] 68 | 2015-11-19 11:05:08 - read header line [] 69 | 2015-11-19 11:05:08 - Received message: [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 70 | 2015-11-19 11:05:08 - Command reply received [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 71 | 2015-11-19 11:05:08 - Command sent to freeSwitch [filter Event-HeaderName channel_create 72 | 73 | ] 74 | 2015-11-19 11:05:08 - read header line [Content-Type: command/reply] 75 | 2015-11-19 11:05:08 - adding header [CONTENT_TYPE] [command/reply] 76 | 2015-11-19 11:05:08 - read header line [Reply-Text: +OK filter added. [Event-HeaderName]=[channel_create]] 77 | 2015-11-19 11:05:08 - adding header [REPLY_TEXT] [+OK filter added. [Event-HeaderName]=[channel_create]] 78 | 2015-11-19 11:05:08 - read header line [] 79 | 2015-11-19 11:05:08 - Received message: [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 80 | 2015-11-19 11:05:08 - Command reply received [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 81 | 2015-11-19 11:05:08 - Command sent to freeSwitch [filter Event-HeaderName background_job 82 | 83 | ] 84 | 2015-11-19 11:05:08 - read header line [Content-Type: command/reply] 85 | 2015-11-19 11:05:08 - adding header [CONTENT_TYPE] [command/reply] 86 | 2015-11-19 11:05:08 - read header line [Reply-Text: +OK filter added. [Event-HeaderName]=[background_job]] 87 | 2015-11-19 11:05:08 - adding header [REPLY_TEXT] [+OK filter added. [Event-HeaderName]=[background_job]] 88 | 2015-11-19 11:05:08 - read header line [] 89 | 2015-11-19 11:05:08 - Received message: [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 90 | 2015-11-19 11:05:08 - Command reply received [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 91 | 2015-11-19 11:05:08 - Command sent to freeSwitch [api sofia status 92 | 93 | ] 94 | 2015-11-19 11:05:08 - read header line [Content-Type: api/response] 95 | 2015-11-19 11:05:08 - adding header [CONTENT_TYPE] [api/response] 96 | 2015-11-19 11:05:08 - read header line [Content-Length: 729] 97 | 2015-11-19 11:05:08 - adding header [CONTENT_LENGTH] [729] 98 | 2015-11-19 11:05:08 - read header line [] 99 | 2015-11-19 11:05:08 - have content-length, decoding body .. 100 | 2015-11-19 11:05:08 - read [729] body bytes 101 | 2015-11-19 11:05:08 - read body line [ Name Type Data State] 102 | 2015-11-19 11:05:08 - read body line [=================================================================================================] 103 | 2015-11-19 11:05:08 - read body line [ 192.168.254.246 alias internal ALIASED] 104 | 2015-11-19 11:05:08 - read body line [ external profile sip:mod_sofia@192.168.254.246:5080 RUNNING (0)] 105 | 2015-11-19 11:05:08 - read body line [ external::example.com gateway sip:joeuser@example.com NOREG] 106 | 2015-11-19 11:05:08 - read body line [external::expresso-gh-voice gateway sip:FreeSWITCH@192.168.254.220 NOREG] 107 | 2015-11-19 11:05:08 - read body line [ internal profile sip:mod_sofia@192.168.254.246:5060 RUNNING (0)] 108 | 2015-11-19 11:05:08 - read body line [=================================================================================================] 109 | 2015-11-19 11:05:08 - read body line [2 profiles 1 alias] 110 | 2015-11-19 11:05:08 - Received message: [FreeSwitchMessage: contentType=[api/response] headers=2, body=9 lines.] 111 | 2015-11-19 11:05:08 - Api response received [FreeSwitchMessage: contentType=[api/response] headers=2, body=9 lines.] 112 | 2015-11-19 11:05:08 - sofia status = [ external profile sip:mod_sofia@192.168.254.246:5080 RUNNING (0)] 113 | 2015-11-19 11:05:33 - Command sent to freeSwitch [exit 114 | 115 | ] 116 | 2015-11-19 11:05:33 - read header line [Content-Type: command/reply] 117 | 2015-11-19 11:05:33 - adding header [CONTENT_TYPE] [command/reply] 118 | 2015-11-19 11:05:33 - read header line [Reply-Text: +OK bye] 119 | 2015-11-19 11:05:33 - adding header [REPLY_TEXT] [+OK bye] 120 | 2015-11-19 11:05:33 - read header line [] 121 | 2015-11-19 11:05:33 - read header line [Content-Type: text/disconnect-notice] 122 | 2015-11-19 11:05:33 - Received message: [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 123 | 2015-11-19 11:05:33 - adding header [CONTENT_TYPE] [text/disconnect-notice] 124 | 2015-11-19 11:05:33 - Command reply received [FreeSwitchMessage: contentType=[command/reply] headers=2, body=0 lines.] 125 | 2015-11-19 11:05:33 - read header line [Content-Length: 67] 126 | 2015-11-19 11:05:33 - adding header [CONTENT_LENGTH] [67] 127 | 2015-11-19 11:05:33 - read header line [] 128 | 2015-11-19 11:05:33 - have content-length, decoding body .. 129 | 2015-11-19 11:05:33 - read [67] body bytes 130 | 2015-11-19 11:05:33 - read body line [Disconnected, goodbye.] 131 | 2015-11-19 11:05:33 - read body line [See you at ClueCon! http://www.cluecon.com/] 132 | 2015-11-19 11:05:33 - Received message: [FreeSwitchMessage: contentType=[text/disconnect-notice] headers=2, body=2 lines.] 133 | 2015-11-19 11:05:33 - Disconnect notice received [FreeSwitchMessage: contentType=[text/disconnect-notice] headers=2, body=2 lines.] 134 | 2015-11-19 11:05:33 - Received disconnection notice 135 | 2015-11-19 11:05:33 - Disconnected .. 136 | -------------------------------------------------------------------------------- /logs/error.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Tochemey/FreeSwitch4j/e07ebb41605adcdb5695211d660881fa55ff49ca/logs/error.log -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | io.freeswitch 5 | io.freeswitch 6 | 0.0.1-SNAPSHOT 7 | Java ESL Library 8 | 9 | 10 | io.netty 11 | netty 12 | 3.10.5.Final 13 | 14 | 15 | org.slf4j 16 | slf4j-api 17 | 1.7.12 18 | 19 | 20 | ch.qos.logback 21 | logback-classic 22 | 1.1.3 23 | 24 | 25 | ch.qos.logback 26 | logback-core 27 | 1.1.3 28 | 29 | 30 | org.apache.commons 31 | commons-lang3 32 | 3.4 33 | 34 | 35 | joda-time 36 | joda-time 37 | 2.8.1 38 | 39 | 40 | junit 41 | junit 42 | 4.13.1 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/ChannelVariable.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch; 17 | 18 | /** 19 | * ChannelVariable. It helps set channel variables. 20 | * 21 | * @author Arsene Tochemey GANDOTE 22 | */ 23 | public class ChannelVariable { 24 | 25 | private String _name; 26 | private String _value; 27 | 28 | public ChannelVariable(String name, String value) { 29 | _name = name; 30 | _value = value; 31 | } 32 | 33 | @Override 34 | public String toString() { 35 | return _name + "=" + _value; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/ConnectionFailure.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public class ConnectionFailure extends Exception { 22 | 23 | /** 24 | * 25 | */ 26 | private static final long serialVersionUID = 1L; 27 | 28 | /** 29 | * 30 | */ 31 | public ConnectionFailure() { 32 | } 33 | 34 | /** 35 | * @param message 36 | */ 37 | public ConnectionFailure(String message) { 38 | super(message); 39 | } 40 | 41 | /** 42 | * @param cause 43 | */ 44 | public ConnectionFailure(Throwable cause) { 45 | super(cause); 46 | } 47 | 48 | /** 49 | * @param message 50 | * @param cause 51 | */ 52 | public ConnectionFailure(String message, Throwable cause) { 53 | super(message, cause); 54 | } 55 | 56 | /** 57 | * @param message 58 | * @param cause 59 | * @param enableSuppression 60 | * @param writableStackTrace 61 | */ 62 | public ConnectionFailure(String message, Throwable cause, 63 | boolean enableSuppression, boolean writableStackTrace) { 64 | super(message, cause, enableSuppression, writableStackTrace); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/ExecutionHandler.java: -------------------------------------------------------------------------------- 1 | package io.freeswitch; 2 | 3 | /* 4 | * Copyright 2009 Red Hat, Inc. 5 | * 6 | * Red Hat licenses this file to you under the Apache License, version 2.0 7 | * (the "License"); you may not use this file except in compliance with the 8 | * License. You may obtain a copy of the License at: 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | * License for the specific language governing permissions and limitations 16 | * under the License. 17 | */ 18 | 19 | import org.jboss.netty.channel.*; 20 | import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor; 21 | import org.jboss.netty.util.ExternalResourceReleasable; 22 | import org.jboss.netty.util.internal.ExecutorUtil; 23 | 24 | import java.util.concurrent.Executor; 25 | 26 | /** 27 | * Forwards an upstream {@link ChannelEvent} to an {@link Executor}. 28 | *

29 | * You can implement various thread model by adding this handler to a 30 | * {@link ChannelPipeline}. The most common use case of this handler is to add a 31 | * {@link ExecutionHandler} which was specified with 32 | * {@link OrderedMemoryAwareThreadPoolExecutor}: 33 | *

 34 |  * ChannelPipeline pipeline = ...;
 35 |  * pipeline.addLast("decoder", new MyProtocolDecoder());
 36 |  * pipeline.addLast("encoder", new MyProtocolEncoder());
 37 |  *
 38 |  * // HERE
 39 |  * pipeline.addLast("executor", new {@link ExecutionHandler}(new {@link OrderedMemoryAwareThreadPoolExecutor}(16, 1048576, 1048576)));
 40 |  *
 41 |  * pipeline.addLast("handler", new MyBusinessLogicHandler());
 42 |  * 
to utilize more processors to handle {@link ChannelEvent}s. You can 43 | * also use other {@link Executor} implementation than the recommended 44 | * {@link OrderedMemoryAwareThreadPoolExecutor}. 45 | * 46 | * @author The Netty Project (netty-dev@lists.jboss.org) 47 | * @author Trustin Lee (tlee@redhat.com) 48 | * @version $Rev: 1685 $, $Date: 2009-08-28 16:15:49 +0900 (금, 28 8 2009) $ 49 | * @apiviz.landmark 50 | * @apiviz.has java.util.concurrent.ThreadPoolExecutor 51 | */ 52 | public class ExecutionHandler implements ChannelUpstreamHandler, ChannelDownstreamHandler, ExternalResourceReleasable { 53 | 54 | private final Executor executor; 55 | 56 | /** 57 | * Creates a new instance with the specified {@link Executor}. Specify an 58 | * {@link OrderedMemoryAwareThreadPoolExecutor} if unsure. 59 | */ 60 | public ExecutionHandler(Executor executor) { 61 | if (executor == null) { 62 | throw new NullPointerException("executor"); 63 | } 64 | this.executor = executor; 65 | } 66 | 67 | /** 68 | * Returns the {@link Executor} which was specified with the constructor. 69 | */ 70 | public Executor getExecutor() { 71 | return executor; 72 | } 73 | 74 | /** 75 | * Shuts down the {@link Executor} which was specified with the constructor 76 | * and wait for its termination. 77 | */ 78 | public void releaseExternalResources() { 79 | ExecutorUtil.terminate(getExecutor()); 80 | } 81 | 82 | public void handleUpstream( 83 | ChannelHandlerContext context, ChannelEvent e) throws Exception { 84 | executor.execute(new ChannelEventRunnable(context, e)); 85 | } 86 | 87 | public void handleDownstream( 88 | ChannelHandlerContext ctx, ChannelEvent e) throws Exception { 89 | if (e instanceof ChannelStateEvent) { 90 | ChannelStateEvent cse = (ChannelStateEvent) e; 91 | if (cse.getState() == ChannelState.INTEREST_OPS 92 | && (((Integer) cse.getValue()).intValue() & Channel.OP_READ) != 0) { 93 | 94 | // setReadable(true) requested 95 | boolean readSuspended = ctx.getAttachment() != null; 96 | if (readSuspended) { 97 | // Drop the request silently if MemoryAwareThreadPool has 98 | // set the flag. 99 | e.getFuture().setSuccess(); 100 | return; 101 | } 102 | } 103 | } 104 | 105 | ctx.sendDownstream(e); 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/IEndPoint.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch; 17 | 18 | /** 19 | * An address for an FreeSWITCH end point. Can be a SIP address, an sofia 20 | * address or event an application 21 | * 22 | * @author Arsene Tochemey GANDOTE 23 | */ 24 | public interface IEndPoint { 25 | 26 | /** 27 | * Format the address as a string which could be dialed using the 28 | * "originate" or "bridge" command 29 | * 30 | * @return Properly formatted string 31 | */ 32 | public String toDialString(); 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/IProtocolListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch; 17 | 18 | import io.freeswitch.event.EslEvent; 19 | import io.freeswitch.message.CommandReply; 20 | 21 | /** 22 | * End users of the {@link io.freeswitch.outbound.FreeSwitchClient} should not need to use this class. 23 | * 24 | * @author Arsene Tochemey GANDOTE 25 | */ 26 | public interface IProtocolListener { 27 | 28 | /** 29 | * Raised when authentication response is received 30 | * 31 | * @param response 32 | */ 33 | void authResponseReceived(CommandReply response); 34 | 35 | /** 36 | * Raised whenever an event is received from FreeSwitch 37 | * 38 | * @param event FreeSwitch event 39 | */ 40 | void eventReceived(EslEvent event); 41 | 42 | /** 43 | * Raised when the client is disconnected. 44 | */ 45 | void disconnected(); 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/SayGender.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public enum SayGender { 22 | FEMININE, MASCULINE, NEUTER 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/SayMethod.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public enum SayMethod { 22 | N_A, PRONOUNCED, ITERATED, COUNTED 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/SayType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public enum SayType { 22 | NUMBER, ITEMS, PERSONS, MESSAGES, CURRENCY, TIME_MEASUREMENT, CURRENT_DATE, CURRENT_TIME, CURRENT_DATE_TIME, TELEPHONE_NUMBER, TELEPHONE_EXTENSION, URL, IP_ADDRESS, EMAIL_ADDRESS, POSTAL_ADDRESS, ACCOUNT_NUMBER, NAME_SPELLED, NAME_PHONETIC, SHORT_DATE_TIME 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/codec/FreeSwitchDecoder.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.codec; 17 | 18 | import io.freeswitch.codec.FreeSwitchMessageHeaders.HeaderName; 19 | import io.freeswitch.message.FreeSwitchMessage; 20 | import org.jboss.netty.buffer.ChannelBuffer; 21 | import org.jboss.netty.channel.Channel; 22 | import org.jboss.netty.channel.ChannelHandlerContext; 23 | import org.jboss.netty.handler.codec.frame.TooLongFrameException; 24 | import org.jboss.netty.handler.codec.replay.ReplayingDecoder; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | /** 29 | * @author Arsene Tochemey GANDOTE 30 | * @author david varnes 31 | */ 32 | public class FreeSwitchDecoder extends 33 | ReplayingDecoder { 34 | 35 | /** 36 | * Line feed character 37 | */ 38 | static final byte LF = 10; 39 | private final Logger log = LoggerFactory.getLogger(this.getClass()); 40 | private final int maxHeaderSize; 41 | private FreeSwitchMessage currentMessage; 42 | private boolean treatUnknownHeadersAsBody = false; 43 | /** 44 | * @param maxHeaderSize 45 | */ 46 | public FreeSwitchDecoder(int maxHeaderSize) { 47 | super(State.READ_HEADER); 48 | if (maxHeaderSize <= 0) { 49 | throw new IllegalArgumentException( 50 | "maxHeaderSize must be a positive integer: " 51 | + maxHeaderSize); 52 | } 53 | this.maxHeaderSize = maxHeaderSize; 54 | } 55 | 56 | public FreeSwitchDecoder(int maxHeaderSize, 57 | boolean treatUnknownHeadersAsBody) { 58 | this(maxHeaderSize); 59 | this.treatUnknownHeadersAsBody = treatUnknownHeadersAsBody; 60 | } 61 | 62 | /* 63 | * (non-Javadoc) 64 | * 65 | * @see io.netty.handler.codec.ByteToMessageDecoder#decode(io.netty.channel. 66 | * ChannelHandlerContext, io.netty.buffer.ByteBuf, java.util.List) 67 | */ 68 | protected Object decode(ChannelHandlerContext ctx, Channel channel, ChannelBuffer buffer, 69 | State state) throws Exception { 70 | log.trace("decode() : state [{}]", state); 71 | switch (state) { 72 | case READ_HEADER: 73 | if (currentMessage == null) { 74 | currentMessage = new FreeSwitchMessage(); 75 | } 76 | /* 77 | * read '\n' terminated lines until reach a single '\n' 78 | */ 79 | boolean reachedDoubleLF = false; 80 | while (!reachedDoubleLF) { 81 | // this will read or fail 82 | String headerLine = readToLineFeedOrFail(buffer, maxHeaderSize); 83 | log.debug("read header line [{}]", headerLine); 84 | if (!headerLine.isEmpty()) { 85 | // split the header line 86 | String[] headerParts = HeaderParser.splitHeader(headerLine); 87 | HeaderName headerName = FreeSwitchMessageHeaders.HeaderName.fromLiteral(headerParts[0]); 88 | if (headerName == null) { 89 | if (treatUnknownHeadersAsBody) { 90 | // cache this 'header' as a body line <-- useful for Outbound client mode 91 | currentMessage.addBodyLine(headerLine); 92 | } else { 93 | throw new IllegalStateException("Unhandled ESL header [" + headerParts[0] + ']'); 94 | } 95 | } 96 | currentMessage.addHeader(headerName, headerParts[1]); 97 | } else { 98 | reachedDoubleLF = true; 99 | } 100 | // do not read in this line again 101 | checkpoint(); 102 | } 103 | // have read all headers - check for content-length 104 | if (currentMessage.hasContentLength()) { 105 | checkpoint(State.READ_BODY); 106 | log.debug("have content-length, decoding body .."); 107 | // force the next section 108 | 109 | return null; 110 | } else { 111 | // end of message 112 | checkpoint(State.READ_HEADER); 113 | // send message upstream 114 | FreeSwitchMessage decodedMessage = currentMessage; 115 | currentMessage = null; 116 | 117 | return decodedMessage; 118 | } 119 | 120 | case READ_BODY: 121 | /* 122 | * read the content-length specified 123 | */ 124 | int contentLength = currentMessage.contentLength(); 125 | ChannelBuffer bodyBytes = buffer.readBytes(contentLength); 126 | log.debug("read [{}] body bytes", bodyBytes.writerIndex()); 127 | // most bodies are line based, so split on LF 128 | while (bodyBytes.readable()) { 129 | String bodyLine = readLine(bodyBytes, contentLength); 130 | log.debug("read body line [{}]", bodyLine); 131 | currentMessage.addBodyLine(bodyLine); 132 | } 133 | 134 | // end of message 135 | checkpoint(State.READ_HEADER); 136 | // send message upstream 137 | FreeSwitchMessage decodedMessage = currentMessage; 138 | currentMessage = null; 139 | 140 | return decodedMessage; 141 | 142 | default: 143 | throw new Error("Illegal state: [" + state + ']'); 144 | } 145 | } 146 | 147 | private String readToLineFeedOrFail(ChannelBuffer buffer, int maxLineLength) 148 | throws TooLongFrameException { 149 | StringBuilder sb = new StringBuilder(64); 150 | while (true) { 151 | // this read might fail 152 | byte nextByte = buffer.readByte(); 153 | if (nextByte == LF) { 154 | return sb.toString(); 155 | } else { 156 | // Abort decoding if the decoded line is too large. 157 | if (sb.length() >= maxLineLength) { 158 | throw new TooLongFrameException( 159 | "ESL header line is longer than " + maxLineLength 160 | + " bytes."); 161 | } 162 | sb.append((char) nextByte); 163 | } 164 | } 165 | } 166 | 167 | private String readLine(ChannelBuffer buffer, int maxLineLength) 168 | throws TooLongFrameException { 169 | StringBuilder sb = new StringBuilder(64); 170 | while (buffer.readable()) { 171 | // this read should always succeed 172 | byte nextByte = buffer.readByte(); 173 | if (nextByte == LF) { 174 | return sb.toString(); 175 | } else { 176 | // Abort decoding if the decoded line is too large. 177 | if (sb.length() >= maxLineLength) { 178 | throw new TooLongFrameException( 179 | "ESL message line is longer than " + maxLineLength 180 | + " bytes."); 181 | } 182 | sb.append((char) nextByte); 183 | } 184 | } 185 | 186 | return sb.toString(); 187 | } 188 | 189 | protected static enum State { 190 | 191 | READ_HEADER, READ_BODY, 192 | } 193 | 194 | } 195 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/codec/FreeSwitchMessageHeaders.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.codec; 17 | 18 | /** 19 | * @author david varnes 20 | * @author Arsene Tochemey GANDOTE 21 | */ 22 | public class FreeSwitchMessageHeaders { 23 | /** 24 | * Standard ESL header names. 25 | *

26 | * Note this enum will need to be kept in synch with any new headers 27 | * introduced on the server side. 28 | */ 29 | public enum HeaderName { 30 | /* 31 | * Minor optimization - put most often used headers at the top for 32 | * fastest resolution in static fromLiteral(). 33 | */ 34 | 35 | /** 36 | * {@code "Content-Type"} 37 | */ 38 | CONTENT_TYPE("Content-Type"), 39 | /** 40 | * {@code "Content-Length"} 41 | */ 42 | CONTENT_LENGTH("Content-Length"), 43 | /** 44 | * {@code "Reply-Text"} 45 | */ 46 | REPLY_TEXT("Reply-Text"), 47 | /** 48 | * {@code "Job-UUID"} 49 | */ 50 | JOB_UUID("Job-UUID"), 51 | /** 52 | * {@code "Socket-Mode"} 53 | */ 54 | SOCKET_MODE("Socket-Mode"), 55 | /** 56 | * {@code "Control"} 57 | */ 58 | Control("Control"),; 59 | 60 | private final String literal; 61 | 62 | private HeaderName(String literal) { 63 | this.literal = literal; 64 | } 65 | 66 | public static HeaderName fromLiteral(String literal) { 67 | for (HeaderName name : values()) { 68 | if (name.literal.equalsIgnoreCase(literal)) { 69 | return name; 70 | } 71 | } 72 | 73 | return null; 74 | } 75 | 76 | public String literal() { 77 | return literal; 78 | } 79 | } 80 | 81 | /** 82 | * Some common ESL header values. These are provided as a convenience for 83 | * commonly used values. 84 | *

85 | * This values are not coded as an enum to allow for the very large range of 86 | * possible values, since they are just Strings. 87 | */ 88 | public static final class HeaderValue { 89 | /** 90 | * {@code "+OK"} 91 | */ 92 | public static final String OK = "+OK"; 93 | /** 94 | * {@code "auth/request"} 95 | */ 96 | public static final String AUTH_REQUEST = "auth/request"; 97 | /** 98 | * {@code "api/response"} 99 | */ 100 | public static final String API_RESPONSE = "api/response"; 101 | /** 102 | * {@code "command/reply"} 103 | */ 104 | public static final String COMMAND_REPLY = "command/reply"; 105 | /** 106 | * {@code "text/event-plain"} 107 | */ 108 | public static final String TEXT_EVENT_PLAIN = "text/event-plain"; 109 | /** 110 | * {@code "text/event-xml"} 111 | */ 112 | public static final String TEXT_EVENT_XML = "text/event-xml"; 113 | /** 114 | * {@code "text/disconnect-notice"} 115 | */ 116 | public static final String TEXT_DISCONNECT_NOTICE = "text/disconnect-notice"; 117 | /** 118 | * {@code "-ERR invalid"} 119 | */ 120 | public static final String ERR_INVALID = "-ERR invalid"; 121 | /** 122 | * {@code "text/rude-rejection"}} 123 | */ 124 | public static final String TEXT_RUDE_REJECTION = "text/rude-rejection"; 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/codec/HeaderParser.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.codec; 17 | 18 | /** 19 | * @author david varnes 20 | * @author Arsene Tochemey GANDOTE 21 | * @author Trustin Lee 22 | */ 23 | public class HeaderParser { 24 | /** 25 | * Split a header in the form 26 | *

27 |      *   Header-HeaderName: Some_header-value
28 |      * 
29 | * into a String array. 30 | * 31 | * @param sb the string header to parse 32 | * @return a String[] array with header name at 0 and header value at 1 33 | */ 34 | public static String[] splitHeader(String sb) { 35 | final int length = sb.length(); 36 | int nameStart; 37 | int nameEnd; 38 | int colonEnd; 39 | int valueStart; 40 | int valueEnd; 41 | 42 | nameStart = findNonWhitespace(sb, 0); 43 | for (nameEnd = nameStart; nameEnd < length; nameEnd++) { 44 | char ch = sb.charAt(nameEnd); 45 | if (ch == ':' || Character.isWhitespace(ch)) { 46 | break; 47 | } 48 | } 49 | 50 | for (colonEnd = nameEnd; colonEnd < length; colonEnd++) { 51 | if (sb.charAt(colonEnd) == ':') { 52 | colonEnd++; 53 | break; 54 | } 55 | } 56 | 57 | valueStart = findNonWhitespace(sb, colonEnd); 58 | if (valueStart == length) { 59 | return new String[]{ 60 | sb.substring(nameStart, nameEnd), 61 | "" 62 | }; 63 | } 64 | 65 | valueEnd = findEndOfString(sb); 66 | return new String[]{ 67 | sb.substring(nameStart, nameEnd), 68 | sb.substring(valueStart, valueEnd) 69 | }; 70 | } 71 | 72 | private static int findNonWhitespace(String sb, int offset) { 73 | int result; 74 | for (result = offset; result < sb.length(); result++) { 75 | if (!Character.isWhitespace(sb.charAt(result))) { 76 | break; 77 | } 78 | } 79 | return result; 80 | } 81 | 82 | private static int findEndOfString(String sb) { 83 | int result; 84 | for (result = sb.length(); result > 0; result--) { 85 | if (!Character.isWhitespace(sb.charAt(result - 1))) { 86 | break; 87 | } 88 | } 89 | return result; 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/ApiCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * ApiCommand. Send an api command (blocking mode) 20 | * 21 | * @author Arsene Tochemey GANDOTE 22 | */ 23 | public class ApiCommand extends BaseCommand { 24 | 25 | public ApiCommand(String command) { 26 | this._command = command; 27 | } 28 | 29 | @Override 30 | public String command() { 31 | return "api"; 32 | } 33 | 34 | @Override 35 | public String argument() { 36 | return this._command; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/AuthCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * AuthCommand. It is used to send an authentication credentials to freeSwitch 20 | * while connected to freeSwitch. 21 | * 22 | * @author Arsene Tochemey GANDOTE 23 | */ 24 | public class AuthCommand extends BaseCommand { 25 | 26 | public AuthCommand(String password) { 27 | this._command = password; 28 | } 29 | 30 | @Override 31 | public String argument() { 32 | return this._command; 33 | } 34 | 35 | @Override 36 | public String command() { 37 | return "auth"; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/BaseCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import io.freeswitch.common.UuidFactory; 19 | 20 | import java.util.Objects; 21 | import java.util.UUID; 22 | 23 | /** 24 | * @author Arsene Tochemey GANDOTE 25 | */ 26 | public abstract class BaseCommand { 27 | 28 | /** 29 | * Command sequence number. It helps identify each command that is sent to 30 | * the FreeSwitch server 31 | */ 32 | private final UUID sequence; 33 | /** 34 | * Additional data to add to the command 35 | */ 36 | public Object optional; 37 | /** 38 | * Property that helps set the command argument 39 | */ 40 | protected String _command; 41 | 42 | 43 | public BaseCommand() { 44 | this.sequence = UuidFactory.create(); 45 | } 46 | 47 | public UUID getSequence() { 48 | return this.sequence; 49 | } 50 | 51 | /** 52 | * The command argument 53 | */ 54 | public abstract String argument(); 55 | 56 | /** 57 | * The command name 58 | */ 59 | public abstract String command(); 60 | 61 | @Override 62 | public boolean equals(Object obj) { 63 | if (obj == null || obj.getClass() != this.getClass()) { 64 | return false; 65 | } 66 | if (obj == this) { 67 | return true; 68 | } 69 | 70 | BaseCommand cmd = (BaseCommand) obj; 71 | return cmd.toString().equals(toString()) && this.sequence.equals(cmd.getSequence()); 72 | } 73 | 74 | @Override 75 | public int hashCode() { 76 | int hash = 7; 77 | hash = 31 * hash + Objects.hashCode(this.sequence); 78 | hash = 31 * hash + Objects.hashCode(this.command()); 79 | hash = 31 * hash + Objects.hashCode(this.argument()); 80 | return hash; 81 | } 82 | 83 | @Override 84 | public String toString() { 85 | return String.format("%s %s", command(), argument()); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/BgApiCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import java.util.UUID; 19 | 20 | /** 21 | * BgApiCommand. 22 | * It is used to Execute an API command in a thread different from 23 | * the main thread running freeSwitch. In other words it sends an api command 24 | * (non-blocking mode) this will let you execute a job in the background and the 25 | * result will be sent as an event with an indicated uuid to match the reply to 26 | * the command) 27 | * 28 | * @author Arsene Tochemey GANDOTE 29 | */ 30 | public class BgApiCommand extends BaseCommand { 31 | 32 | /** 33 | * The command Id. Each bgapi command can explicitly have an Id since bgapi 34 | * generates a UUID for each command executed on FreeSwitch 35 | */ 36 | public UUID CommandId; 37 | 38 | public BgApiCommand(String command, String argument) { 39 | this._command = String.format("%s %s", command, argument); 40 | } 41 | 42 | @Override 43 | public String argument() { 44 | return this._command; 45 | } 46 | 47 | @Override 48 | public String command() { 49 | return "bgapi"; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/ConnectCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * ConnectCommand. 20 | * This command is used to connect to FreeSwitch to retrieve the 21 | * details of an inbound call. This function will be used in an application 22 | * server to which FreeSwitch will connect to via its mode outbound event 23 | * socket. 24 | * 25 | * @author Arsene Tochemey GANDOTE 26 | */ 27 | public class ConnectCommand extends BaseCommand { 28 | 29 | @Override 30 | public String argument() { 31 | return ""; 32 | } 33 | 34 | @Override 35 | public String command() { 36 | return "connect"; 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/DivertEventsCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * DivertEventsCommand. The divert_events switch is available to allow event 20 | * that an embedded script would expect to get in the inputcallback to be 21 | * diverted to the event socket. 22 | * 23 | * @author Arsene Tochemey GANDOTE 24 | */ 25 | public class DivertEventsCommand extends BaseCommand { 26 | 27 | public DivertEventsCommand(boolean on) { 28 | if (on) 29 | this._command = "on"; 30 | else 31 | this._command = "off"; 32 | } 33 | 34 | @Override 35 | public String argument() { 36 | return this._command; 37 | } 38 | 39 | @Override 40 | public String command() { 41 | return "divert_events"; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/EventCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * EventCommand. Enable or disable event by class or all (plain or xml or json 20 | * output format) 21 | * 22 | * @author Arsene Tochemey GANDOTE 23 | */ 24 | public class EventCommand extends BaseCommand { 25 | 26 | public EventCommand(String eventlist) { 27 | this._command = eventlist; 28 | } 29 | 30 | @Override 31 | public String argument() { 32 | return this._command; 33 | } 34 | 35 | @Override 36 | public String command() { 37 | return "event"; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/EventsCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * EventsCommand. Enable or disable event by class or all (plain or xml or json 20 | * output format) 21 | * 22 | * @author Arsene Tochemey GANDOTE 23 | */ 24 | public class EventsCommand extends BaseCommand { 25 | 26 | public EventsCommand(String eventlist) { 27 | this._command = eventlist; 28 | } 29 | 30 | @Override 31 | public String argument() { 32 | return this._command; 33 | } 34 | 35 | @Override 36 | public String command() { 37 | return "event"; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/ExitCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * ExitCommand. 20 | * It is used to disconnect from FreeSwitch 21 | * 22 | * @author Arsene Tochemey GANDOTE 23 | */ 24 | public class ExitCommand extends BaseCommand { 25 | 26 | @Override 27 | public String argument() { 28 | return ""; 29 | } 30 | 31 | @Override 32 | public String command() { 33 | return "exit"; 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/FilterCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * Specify event types to listen for. Note, this is not a filter out but rather 20 | * a "filter in," that is, when a filter is applied only the filtered values are 21 | * received. Multiple filters on a socket connection are allowed. 22 | * 23 | * @author Arsene Tochemey GANDOTE 24 | */ 25 | public class FilterCommand extends BaseCommand { 26 | 27 | /** 28 | * 29 | */ 30 | public FilterCommand(String filter) { 31 | this._command = filter; 32 | } 33 | 34 | @Override 35 | public String argument() { 36 | return this._command; 37 | } 38 | 39 | @Override 40 | public String command() { 41 | return "filter"; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/GetVarCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import java.util.UUID; 19 | 20 | /** 21 | * GetVarCommand. 22 | * It is used to retrieve a channel variable based upon the channel id 23 | * 24 | * @author Arsene Tochemey GANDOTE 25 | */ 26 | public class GetVarCommand extends BaseCommand { 27 | 28 | public GetVarCommand(UUID channelId, String variable) { 29 | this._command = String.format("%s %s", channelId, variable); 30 | } 31 | 32 | @Override 33 | public String argument() { 34 | return this._command; 35 | } 36 | 37 | @Override 38 | public String command() { 39 | return "uuid_getvar"; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/HangupCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import java.util.UUID; 19 | 20 | /** 21 | * HangupCommand. It is used to hangup a live call with a specific reason. 22 | * 23 | * @author Arsene Tochemey GANDOTE 24 | */ 25 | public class HangupCommand extends BaseCommand { 26 | 27 | public HangupCommand(UUID channelId, String reason) { 28 | this._command = String.format( 29 | "sendmsg %1$s\ncall-command: %2$s\nhangup-cause: %3$s", channelId, 30 | "hangup", reason); 31 | } 32 | 33 | @Override 34 | public String argument() { 35 | return ""; 36 | } 37 | 38 | @Override 39 | public String command() { 40 | return this._command; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/LogCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import io.freeswitch.common.LogLevels; 19 | 20 | /** 21 | * @author Arsene Tochemey GANDOTE 22 | */ 23 | public class LogCommand extends BaseCommand { 24 | 25 | /** 26 | * 27 | */ 28 | public LogCommand(LogLevels level) { 29 | this._command = level.toString().toLowerCase(); 30 | } 31 | 32 | 33 | @Override 34 | public String argument() { 35 | return this._command; 36 | } 37 | 38 | @Override 39 | public String command() { 40 | return "log"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/MyEventsCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import java.util.UUID; 19 | 20 | /** 21 | * MyEvents. The 'myevents' subscription allows your inbound socket connection 22 | * to behave like an outbound socket connect. It will "lock on" to the event 23 | * for a particular uuid and will ignore all other event, closing the socket 24 | * when the channel goes away or closing the channel when the socket disconnects 25 | * and all applications have finished executing 26 | * 27 | * @author Arsene Tochemey GANDOTE 28 | */ 29 | public class MyEventsCommand extends BaseCommand { 30 | 31 | public MyEventsCommand(UUID channelId) { 32 | this._command = channelId.toString(); 33 | } 34 | 35 | @Override 36 | public String argument() { 37 | return this._command; 38 | } 39 | 40 | @Override 41 | public String command() { 42 | return "myevents"; 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/NixEventCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public class NixEventCommand extends BaseCommand { 22 | 23 | public NixEventCommand(String eventName) { 24 | this._command = eventName; 25 | } 26 | 27 | @Override 28 | public String argument() { 29 | return this._command; 30 | } 31 | 32 | @Override 33 | public String command() { 34 | return "nixevent"; 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/NoEventsCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public class NoEventsCommand extends BaseCommand { 22 | 23 | /** 24 | * 25 | */ 26 | public NoEventsCommand() { 27 | } 28 | 29 | 30 | @Override 31 | public String argument() { 32 | return ""; 33 | } 34 | 35 | @Override 36 | public String command() { 37 | return "noevents"; 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/NologCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * Disable any logging previously enabled with setLogLevel() 20 | * 21 | * @author Arsene Tochemey GANDOTE 22 | */ 23 | public class NologCommand extends BaseCommand { 24 | 25 | /** 26 | * 27 | */ 28 | public NologCommand() { 29 | } 30 | 31 | 32 | @Override 33 | public String argument() { 34 | return ""; 35 | } 36 | 37 | 38 | @Override 39 | public String command() { 40 | return "nolog"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/OriginateCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | 19 | import io.freeswitch.ChannelVariable; 20 | import io.freeswitch.IEndPoint; 21 | import org.apache.commons.lang3.StringUtils; 22 | 23 | import java.util.ArrayList; 24 | import java.util.List; 25 | import java.util.UUID; 26 | 27 | /** 28 | * Originate. It is used to originate a new call. 29 | * 30 | * @author Arsene Tochemey GANDOTE 31 | */ 32 | public class OriginateCommand extends BaseCommand { 33 | 34 | /** 35 | * originate uuid. 36 | */ 37 | public UUID originateId; 38 | /** 39 | * call session heartbeat. very useful while billing a call. 40 | */ 41 | public int sessionHeartbeat; 42 | /** 43 | * Additional attributes to add to the originate command 44 | */ 45 | public String option; 46 | /** 47 | * originate channel variables 48 | */ 49 | private List _channelVariables; 50 | private IEndPoint _destination; 51 | private IEndPoint _application; 52 | 53 | public OriginateCommand(IEndPoint destination, IEndPoint application) { 54 | _destination = destination; 55 | _application = application; 56 | _channelVariables = new ArrayList(); 57 | } 58 | 59 | @Override 60 | public String argument() { 61 | AddVariable("origination_uuid", originateId.toString()); 62 | AddVariable("ignore_early_media", "true"); 63 | AddVariable("enable_heartbeat_events", String.valueOf(sessionHeartbeat)); 64 | 65 | String variables = ""; 66 | if (_channelVariables.isEmpty()) { 67 | for (ChannelVariable channelVariable : _channelVariables) { 68 | variables += channelVariable.toString() + ","; 69 | } 70 | 71 | if (StringUtils.isEmpty(option)) 72 | variables = "{" + StringUtils.removeEnd(variables, ",") + "}"; 73 | else 74 | variables = "{" + option + "," 75 | + StringUtils.removeEnd(variables, ",") + "}"; 76 | } 77 | 78 | return String.format("%1s %2s %3s", variables, _destination.toDialString(), _application.toDialString()); 79 | 80 | } 81 | 82 | @Override 83 | public String command() { 84 | return "originate"; 85 | } 86 | 87 | /** 88 | * Helps set originate channel variables 89 | * 90 | * @param name variable name 91 | * @param value variable value 92 | */ 93 | public void AddVariable(String name, String value) { 94 | _channelVariables.add(new ChannelVariable(name, value)); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/PlayAndGetDigitsCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * PlayAndGetDigits. Play a prompt and get digits. {@link https 20 | * ://wiki.freeswitch.org/wiki/Misc._Dialplan_Tools_play_and_get_digits} 21 | * 22 | * @author Arsene Tochemey GANDOTE 23 | */ 24 | public class PlayAndGetDigitsCommand extends BaseCommand { 25 | 26 | /** 27 | * Maximum number of digits to fetch (maximum value of 128) 28 | */ 29 | public int maxNumberOfDigits; 30 | 31 | /** 32 | * Minimum number of digits to fetch (minimum value of 0) 33 | */ 34 | public int minNumberOfDigits; 35 | 36 | /** 37 | * digits used to end input if less than digits have 38 | * been pressed. (Typically '#') 39 | */ 40 | public char terminators; 41 | 42 | /** 43 | * numbers of tries for the sound to play 44 | */ 45 | public int retries; 46 | 47 | /** 48 | * Regular expression to match digits 49 | */ 50 | public String regex; 51 | 52 | /** 53 | * Inter-digit timeout; number of milliseconds allowed between digits; once 54 | * this number is reached, PAGD assumes that the caller has no more digits 55 | * to dial. 56 | */ 57 | public int digitTimeout; 58 | 59 | /** 60 | * Number of milliseconds to wait for a dialed response after the file 61 | * playback ends and before PAGD does a retry. 62 | */ 63 | public int timeout; 64 | 65 | /** 66 | * Sound file to play while digits are fetched. 67 | */ 68 | public String audioFile; 69 | 70 | /** 71 | * Channel variable into which digits should be placed. 72 | */ 73 | public String variableName; 74 | 75 | /** 76 | * Sound file to play when digits don't match the regexp. 77 | */ 78 | public String invalidFile; 79 | 80 | public PlayAndGetDigitsCommand() { 81 | maxNumberOfDigits = 128; 82 | minNumberOfDigits = 0; 83 | terminators = '#'; 84 | retries = 1; 85 | regex = "1234567890*#"; 86 | digitTimeout = (2 * 1000); 87 | timeout = (5 * 1000); 88 | invalidFile = "silence_stream://150"; 89 | } 90 | 91 | @Override 92 | public String argument() { 93 | return String.format("%1d %2d %3d %4d '%5s' '%6s' %7s %8s %9s %10d", 94 | minNumberOfDigits, maxNumberOfDigits, retries, timeout, 95 | terminators, audioFile, invalidFile, variableName, regex, 96 | digitTimeout); 97 | } 98 | 99 | @Override 100 | public String command() { 101 | return "play_and_get_digits"; 102 | } 103 | 104 | } 105 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/PlaybackCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import io.freeswitch.ChannelVariable; 19 | import org.apache.commons.lang3.StringUtils; 20 | 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | 25 | /** 26 | * Playback. Plays a sound file on the current channel. 27 | * 28 | * @author Arsene Tochemey GANDOTE 29 | */ 30 | public class PlaybackCommand extends BaseCommand { 31 | 32 | /** 33 | * Playback channel variables 34 | */ 35 | private List _channelVariables; 36 | 37 | /** 38 | * Number of times to play the audio file 39 | */ 40 | private int _loop; 41 | /** 42 | * Audio file to play 43 | */ 44 | private String _audioFile; 45 | 46 | public PlaybackCommand() { 47 | _channelVariables = new ArrayList(); 48 | _loop = 1; 49 | _audioFile = ""; 50 | } 51 | 52 | public PlaybackCommand(String audioFile) { 53 | _audioFile = audioFile; 54 | _loop = 1; 55 | _channelVariables = new ArrayList(); 56 | } 57 | 58 | public PlaybackCommand(String audioFile, List variables) { 59 | _audioFile = audioFile; 60 | _loop = 1; 61 | _channelVariables = variables; 62 | } 63 | 64 | public PlaybackCommand(String audioFile, int loop, List variables) { 65 | _audioFile = audioFile; 66 | _loop = loop; 67 | _channelVariables = variables; 68 | } 69 | 70 | public int loop() { 71 | return _loop; 72 | } 73 | 74 | @Override 75 | public String argument() { 76 | String variables = ""; 77 | if (_channelVariables.isEmpty()) { 78 | for (ChannelVariable channelVariable : _channelVariables) { 79 | variables += channelVariable.toString() + ","; 80 | } 81 | 82 | variables = "{" + StringUtils.removeEnd(variables, ",") + "}"; 83 | } 84 | 85 | return String.format("%1s %2s", variables, _audioFile); 86 | } 87 | 88 | @Override 89 | public String command() { 90 | return "playback"; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/RecordCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * Record. Record is used to record voice messages, such as in a voicemail 20 | * system. 21 | * 22 | * @author Arsene Tochemey GANDOTE 23 | */ 24 | public class RecordCommand extends BaseCommand { 25 | 26 | /** 27 | * File to record 28 | */ 29 | public String recordFile; 30 | 31 | /** 32 | * how many seconds of audio below silence_thresh will be tolerated before 33 | * the recording stops. When omitted, the default value is 3. 34 | */ 35 | public long silenceHit; 36 | 37 | /** 38 | * the energy level below which is considered silence. 39 | */ 40 | public long silenceTreshold; 41 | 42 | /** 43 | * the maximum duration of the recording in seconds. 44 | */ 45 | public long timeLimit; 46 | 47 | public RecordCommand() { 48 | silenceHit = 3; 49 | } 50 | 51 | @Override 52 | public String argument() { 53 | return String.format("%1s %4d %4d %4d", recordFile, timeLimit, 54 | silenceTreshold, silenceHit); 55 | } 56 | 57 | @Override 58 | public String command() { 59 | return "record"; 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/ResumeCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * Resume. socket_resume: If this variable is set to true, the dialplan will 20 | * resume execution with the next action after the call to the socket 21 | * application. This can be used for example to allow you to do something 22 | * intelligent in the dialplan if your IVR application gets killed in an unclean 23 | * way. If there is a bridge active when the disconnect happens, it is killed. 24 | * To do this from your application after the socket is already connected, issue 25 | * the resume command. 26 | * 27 | * @author Arsene Tochemey GANDOTE 28 | */ 29 | public class ResumeCommand extends BaseCommand { 30 | 31 | @Override 32 | public String argument() { 33 | return ""; 34 | } 35 | 36 | @Override 37 | public String command() { 38 | return "resume"; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/SayCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import io.freeswitch.SayGender; 19 | import io.freeswitch.SayMethod; 20 | import io.freeswitch.SayType; 21 | 22 | /** 23 | * Say. The say application will use the pre-recorded sound files to read or say 24 | * various things like dates, times, digits, etc. The say application can read 25 | * digits and numbers as well as dollar amounts, date/time values and IP 26 | * addresses. It can also spell out alpha-numeric text, including punctuation 27 | * marks. 28 | * 29 | * @author Arsene Tochemey GANDOTE 30 | */ 31 | public class SayCommand extends BaseCommand { 32 | 33 | /** 34 | * say language or module 35 | */ 36 | public String language; 37 | /** 38 | * say type 39 | * 40 | * @see SayType 41 | */ 42 | public SayType type; 43 | /** 44 | * say method 45 | * 46 | * @see SayMethod 47 | */ 48 | public SayMethod method; 49 | /** 50 | * say gender 51 | * 52 | * @see SayGender 53 | */ 54 | public SayGender gender; 55 | /** 56 | * Text to read. 57 | */ 58 | private String _text; 59 | 60 | public SayCommand(String text) { 61 | _text = text; 62 | } 63 | 64 | @Override 65 | public String argument() { 66 | return language + " " + type.toString() + " " 67 | + method.toString().replace("_", "/") + " " + gender.toString() 68 | + " " + _text; 69 | } 70 | 71 | @Override 72 | public String command() { 73 | return "say"; 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/SchedApiCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * SchedApi. Schedule an API call in the future. 20 | * 21 | * @author Arsene Tochemey GANDOTE 22 | */ 23 | public class SchedApiCommand extends BaseCommand { 24 | /** 25 | * The command to schedule 26 | */ 27 | private BaseCommand _command; 28 | 29 | /** 30 | * Group name 31 | */ 32 | private String _groupName; 33 | 34 | /** 35 | * Asynchronous mode or not 36 | */ 37 | private boolean _async; 38 | 39 | /** 40 | * How many times to execute the command 41 | */ 42 | private boolean _repetitive; 43 | 44 | /** 45 | * specifies the number of seconds to wait before executing the command. 46 | * _unixTime is the UNIX timestamp at which the command should be executed 47 | */ 48 | private long _unixTime; 49 | 50 | public SchedApiCommand(BaseCommand command, long time, String groupName, 51 | boolean async, boolean repetitive) { 52 | _command = command; 53 | _unixTime = time; 54 | _groupName = groupName; 55 | _async = async; 56 | _repetitive = repetitive; 57 | } 58 | 59 | @Override 60 | public String argument() { 61 | String arg = String.format("+%1d %2s %3s %4s", _unixTime, _groupName, 62 | _command.toString(), _async ? "&" : ""); 63 | if (_repetitive) 64 | arg = String.format("@%1d %2s %3s %4s", _unixTime, _groupName, 65 | _command, _async ? "&" : ""); 66 | return arg; 67 | } 68 | 69 | @Override 70 | public String command() { 71 | return "sched_api"; 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/SendMsgCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import java.util.UUID; 19 | 20 | /** 21 | * @author Arsene Tochemey GANDOTE 22 | */ 23 | public class SendMsgCommand extends BaseCommand { 24 | 25 | final UUID EMPTY_UUID = new UUID(0L, 0L); 26 | 27 | /** 28 | * channel Id 29 | */ 30 | private UUID _channelId; 31 | 32 | /** 33 | * application to be executed 34 | */ 35 | private String _application; 36 | 37 | /** 38 | * application argument 39 | */ 40 | private String _argument; 41 | 42 | /** 43 | * States whether to execute the application in blocking mode or not. 44 | */ 45 | private boolean _eventLock; 46 | 47 | /** 48 | * Number of times to execute the application 49 | */ 50 | private int _loop; 51 | 52 | public SendMsgCommand(UUID channelId, String app, String arg, int loop, 53 | boolean blockingMode) { 54 | _channelId = channelId; 55 | _application = app; 56 | _argument = arg; 57 | _loop = loop; 58 | _eventLock = blockingMode; 59 | } 60 | 61 | public SendMsgCommand(String app, String arg, int loop, boolean blockingMode) { 62 | _channelId = EMPTY_UUID; 63 | _application = app; 64 | _argument = arg; 65 | _loop = loop; 66 | _eventLock = blockingMode; 67 | } 68 | 69 | public SendMsgCommand(String app, String arg) { 70 | _channelId = EMPTY_UUID; 71 | _application = app; 72 | _argument = arg; 73 | _loop = 1; 74 | _eventLock = false; 75 | } 76 | 77 | @Override 78 | public String argument() { 79 | return ""; 80 | } 81 | 82 | @Override 83 | public String command() { 84 | String cmd = ""; 85 | if (_channelId.equals(EMPTY_UUID)) { 86 | cmd = String 87 | .format("sendmsg call-command: %1s\nexecute-app-name: %2s\nexecute-app-arg: %3s\nloops: %4d", 88 | "execute", _application, _argument, _loop); 89 | } else { 90 | cmd = String 91 | .format("sendmsg %1s\ncall-command: %2s\nexecute-app-name: %3s\nexecute-app-arg: %4s\nloops: %5d", 92 | _channelId.toString(), "execute", _application, 93 | _argument, _loop); 94 | } 95 | 96 | if (_eventLock) 97 | cmd += String.format("\nevent-lock: %s", "true"); 98 | 99 | return cmd; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/SetVarCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import java.util.UUID; 19 | 20 | /** 21 | * SetVar. 22 | * It is used to set channel variable 23 | * 24 | * @author Arsene Tochemey GANDOTE 25 | */ 26 | public class SetVarCommand extends BaseCommand { 27 | 28 | /** 29 | * channel Id 30 | */ 31 | private UUID _channelId; 32 | 33 | /** 34 | * variable name 35 | */ 36 | private String _name; 37 | 38 | /** 39 | * variable value 40 | */ 41 | private String _value; 42 | 43 | public SetVarCommand(UUID channelId, String var, String val) { 44 | _channelId = channelId; 45 | _name = var; 46 | _value = val; 47 | } 48 | 49 | @Override 50 | public String argument() { 51 | return String.format("%1s %2s %3s", _channelId.toString(), _name, 52 | _value); 53 | } 54 | 55 | @Override 56 | public String command() { 57 | return "uuid_setvar"; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/SleepCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | /** 19 | * Sleep Pause the channel for a given number of milliseconds, consuming the 20 | * audio for that period of time. Calling sleep also will consume any 21 | * outstanding RTP on the operating system's input queue, which can be very 22 | * useful in situations where audio becomes backlogged. 23 | * 24 | * @author Arsene Tochemey GANDOTE 25 | */ 26 | public class SleepCommand extends BaseCommand { 27 | 28 | /** 29 | * number of milliseconds to sleep 30 | */ 31 | private long _duration; 32 | 33 | public SleepCommand(long duration) { 34 | _duration = 1000 * duration; 35 | } 36 | 37 | @Override 38 | public String argument() { 39 | return String.valueOf(_duration); 40 | } 41 | 42 | @Override 43 | public String command() { 44 | return "sleep"; 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/command/SpeakCommand.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.command; 17 | 18 | import org.apache.commons.lang3.StringUtils; 19 | 20 | /** 21 | * Speaks a string or file of text to the channel using the defined speech 22 | * engine. 23 | * 24 | * @author Arsene Tochemey GANDOTE 25 | */ 26 | public class SpeakCommand extends BaseCommand { 27 | 28 | /** 29 | * TTS engine 30 | */ 31 | public String engine; 32 | 33 | /** 34 | * TTS engine voice 35 | */ 36 | public String voice; 37 | 38 | /** 39 | * Text to read 40 | */ 41 | public String text; 42 | 43 | /** 44 | * Timer name 45 | */ 46 | public String timerName; 47 | 48 | public SpeakCommand() { 49 | engine = "flite"; 50 | voice = "kal"; 51 | } 52 | 53 | @Override 54 | public String argument() { 55 | return engine + "|" + voice + "|" + text 56 | + (!StringUtils.isEmpty(timerName) ? "|" + timerName : ""); 57 | } 58 | 59 | @Override 60 | public String command() { 61 | return "speak"; 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/common/HangupCauses.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.common; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public enum HangupCauses { 22 | NONE(0), 23 | UNALLOCATED_NUMBER(1), 24 | NO_ROUTE_TRANSIT_NET(2), 25 | NO_ROUTE_DESTINATION(3), 26 | CHANNEL_UNACCEPTABLE(6), 27 | CALL_AWARDED_DELIVERED(7), 28 | NORMAL_CLEARING(16), 29 | USER_BUSY(17), 30 | NO_USER_RESPONSE(18), 31 | NO_ANSWER(19), 32 | SUBSCRIBER_ABSENT(20), 33 | CALL_REJECTED(21), 34 | NUMBER_CHANGED(22), 35 | REDIRECTION_TO_NEW_DESTINATION(23), 36 | EXCHANGE_ROUTING_ERROR(25), 37 | DESTINATION_OUT_OF_ORDER(27), 38 | INVALID_NUMBER_FORMAT(28), 39 | FACILITY_REJECTED(29), 40 | RESPONSE_TO_STATUS_ENQUIRY(30), 41 | NORMAL_UNSPECIFIED(31), 42 | NORMAL_CIRCUIT_CONGESTION(34), 43 | NETWORK_OUT_OF_ORDER(38), 44 | NORMAL_TEMPORARY_FAILURE(41), 45 | SWITCH_CONGESTION(42), 46 | ACCESS_INFO_DISCARDED(43), 47 | REQUESTED_CHAN_UNAVAIL(44), 48 | PRE_EMPTED(45), 49 | FACILITY_NOT_SUBSCRIBED(50), 50 | OUTGOING_CALL_BARRED(52), 51 | INCOMING_CALL_BARRED(54), 52 | BEARERCAPABILITY_NOTAUTH(57), 53 | BEARERCAPABILITY_NOTAVAIL(58), 54 | SERVICE_UNAVAILABLE(63), 55 | BEARERCAPABILITY_NOTIMPL(65), 56 | CHAN_NOT_IMPLEMENTED(66), 57 | FACILITY_NOT_IMPLEMENTED(69), 58 | SERVICE_NOT_IMPLEMENTED(79), 59 | INVALID_CALL_REFERENCE(81), 60 | INCOMPATIBLE_DESTINATION(88), 61 | INVALID_MSG_UNSPECIFIED(95), 62 | MANDATORY_IE_MISSING(96), 63 | MESSAGE_TYPE_NONEXIST(97), 64 | WRONG_MESSAGE(98), 65 | IE_NONEXIST(99), 66 | INVALID_IE_CONTENTS(100), 67 | WRONG_CALL_STATE(101), 68 | RECOVERY_ON_TIMER_EXPIRE(102), 69 | MANDATORY_IE_LENGTH_ERROR(103), 70 | PROTOCOL_ERROR(111), 71 | INTERWORKING(127), 72 | SUCCESS(142), 73 | ORIGINATOR_CANCEL(487), 74 | CRASH(500), 75 | SYSTEM_SHUTDOWN(501), 76 | LOSE_RACE(502), 77 | MANAGER_REQUEST(503), 78 | BLIND_TRANSFER(600), 79 | ATTENDED_TRANSFER(601), 80 | ALLOTTED_TIMEOUT(602), 81 | USER_CHALLENGE(603), 82 | MEDIA_TIMEOUT(604), 83 | PICKED_OFF(605), 84 | USER_NOT_REGISTERED(606), 85 | PROGRESS_TIMEOUT(607), 86 | UNKNOWN(9999); 87 | 88 | private long val; 89 | 90 | private HangupCauses(long val) { 91 | this.val = val; 92 | } 93 | 94 | public long itutq850Code() { 95 | return val; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/common/LogLevels.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.common; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public enum LogLevels { 22 | CONSOLE(0), 23 | ALERT(1), 24 | CRIT(2), 25 | ERR(3), 26 | WARNING(4), 27 | NOTICE(5), 28 | INFO(6), 29 | DEBUG(7); 30 | 31 | private long val; 32 | 33 | /** 34 | * 35 | */ 36 | private LogLevels(long val) { 37 | this.val = val; 38 | } 39 | 40 | public long level() { 41 | return val; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/common/UuidFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.common; 17 | 18 | import java.util.UUID; 19 | 20 | /** 21 | * @author Arsene Tochemey GANDOTE 22 | */ 23 | public class UuidFactory { 24 | 25 | private static UuidFactory _instance = new UuidFactory(); 26 | 27 | /** 28 | * Generate a new UUID 29 | * 30 | * @return UUID 31 | */ 32 | public static UUID create() { 33 | return _instance.createInternal(); 34 | } 35 | 36 | /** 37 | * Generate a new UUID using the built-in java UUID generator 38 | * 39 | * @return UUID 40 | */ 41 | protected UUID createInternal() { 42 | return UUID.randomUUID(); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/AbstractEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | /** 22 | * This class helps wrap all event received 23 | * 24 | * @author Arsene Tochemey GANDOTE 25 | */ 26 | public abstract class AbstractEvent { 27 | 28 | /** 29 | * FreeSwitch event. 30 | */ 31 | protected final EslEvent _event; 32 | 33 | /** 34 | * 35 | */ 36 | public AbstractEvent(EslEvent event) { 37 | _event = event; 38 | } 39 | 40 | /** 41 | * Gets the event headers. It is very useful to grab all the desired event 42 | * headers. 43 | * 44 | * @return map of event header values 45 | */ 46 | public Map headers() { 47 | return _event.eventHeaders(); 48 | } 49 | 50 | /** 51 | * Gets the event body lines 52 | * 53 | * @return list of decoded event body lines, may be an empty list. 54 | */ 55 | public List bodyLines() { 56 | return _event.eventBodyLines(); 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/BackgroundJob.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | import java.util.List; 19 | import java.util.UUID; 20 | 21 | /** 22 | * Event emitted by FreeSwitch when a bgapi is executed. 23 | * 24 | * @author Arsene Tochemey GANDOTE 25 | */ 26 | public class BackgroundJob extends AbstractEvent { 27 | 28 | /** 29 | * @param event 30 | */ 31 | public BackgroundJob(EslEvent event) { 32 | super(event); 33 | } 34 | 35 | /** 36 | * jobId(). It returns the background job id 37 | * 38 | * @return UUID the job id 39 | */ 40 | public UUID jobId() { 41 | String uniqueId = _event.eventHeaders().get(EventHeaders.JOB_UUID); 42 | return UUID.fromString(uniqueId); 43 | } 44 | 45 | /** 46 | * It returns the command executed by bgapi. 47 | * 48 | * @return the job command. 49 | */ 50 | public String commandName() { 51 | return _event.eventHeaders().get(EventHeaders.JOB_COMMAND); 52 | } 53 | 54 | /** 55 | * It returns the argument of the command executed by bgapi. 56 | * 57 | * @return 58 | */ 59 | public String commandArg() { 60 | return _event.eventHeaders().get("Job-Command-Arg"); 61 | } 62 | 63 | /** 64 | * It returns the result of the command executed by bgapi. 65 | * 66 | * @return 67 | */ 68 | public List result() { 69 | return _event.eventBodyLines(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/ChannelBridge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | import java.util.UUID; 19 | 20 | /** 21 | * This event is raised when two calls have connected using the bridge 22 | * application. 23 | * 24 | * @author Arsene Tochemey GANDOTE 25 | */ 26 | public class ChannelBridge extends AbstractEvent { 27 | 28 | /** 29 | * @param event 30 | */ 31 | public ChannelBridge(EslEvent event) { 32 | super(event); 33 | } 34 | 35 | /** 36 | * Gets the call direction 37 | * 38 | * @return String value of the event data "Call-Direction" 39 | */ 40 | public String callDirection() { 41 | return _event.eventHeaders().get("Call-Direction"); 42 | } 43 | 44 | /** 45 | * Gets the end point disposition. It describes the state of the call. 46 | * 47 | * @return String value of the event data "endpoint_disposition" 48 | */ 49 | public String endpointDisposition() { 50 | return _event.eventHeaders().get("variable_endpoint_disposition"); 51 | } 52 | 53 | /** 54 | * Gets the A-leg UUID. 55 | * 56 | * @return UUID value of the A-leg UUID. 57 | */ 58 | public UUID alegUUID() { 59 | return UUID.fromString(_event.eventHeaders().get("Bridge-A-Unique-ID")); 60 | } 61 | 62 | /** 63 | * Gets the B-leg UUID. 64 | * 65 | * @return UUID value of the B-leg UUID. 66 | */ 67 | public UUID blegUUID() { 68 | return UUID.fromString(_event.eventHeaders().get("Bridge-B-Unique-ID")); 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/ChannelExecute.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | /** 19 | * This event is raised whenever an application is being executed. 20 | * This event indicates that the PBX is doing something with the call 21 | * 22 | * @author Arsene Tochemey GANDOTE 23 | */ 24 | public class ChannelExecute extends AbstractEvent { 25 | 26 | /** 27 | * @param event 28 | */ 29 | public ChannelExecute(EslEvent event) { 30 | super(event); 31 | } 32 | 33 | /** 34 | * Gets the application that is being executed. 35 | * 36 | * @return String value of the application that is being executed. 37 | */ 38 | public String application() { 39 | return _event.eventHeaders().get("Application"); 40 | } 41 | 42 | /** 43 | * Gets the application argument 44 | * 45 | * @return String value of the application argument. 46 | */ 47 | public String applicationData() { 48 | return _event.eventHeaders().get("Application-Data"); 49 | } 50 | 51 | /** 52 | * Gets the application response 53 | * 54 | * @return String value of the application response. 55 | */ 56 | public String applicationResponse() { 57 | return _event.eventHeaders().get("Application-Response"); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/ChannelExecuteComplete.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | /** 19 | * This event is raised whenever an application has completed its execution. 20 | * 21 | * @author Arsene Tochemey GANDOTE 22 | */ 23 | public class ChannelExecuteComplete extends AbstractEvent { 24 | 25 | /** 26 | * @param event 27 | */ 28 | public ChannelExecuteComplete(EslEvent event) { 29 | super(event); 30 | } 31 | 32 | /** 33 | * Gets the application that has completed its execution. 34 | * 35 | * @return String value of the application that has completed its execution. 36 | */ 37 | public String application() { 38 | return _event.eventHeaders().get("Application"); 39 | } 40 | 41 | /** 42 | * Gets the application argument 43 | * 44 | * @return String value of the application argument. 45 | */ 46 | public String applicationData() { 47 | return _event.eventHeaders().get("Application-Data"); 48 | } 49 | 50 | /** 51 | * Gets the application response 52 | * 53 | * @return String value of the application response. 54 | */ 55 | public String applicationResponse() { 56 | return _event.eventHeaders().get("Application-Response"); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/ChannelHangup.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | import io.freeswitch.common.HangupCauses; 19 | 20 | /** 21 | * This event is raised whenever a call is hangup. 22 | * 23 | * @author Arsene Tochemey GANDOTE 24 | */ 25 | public class ChannelHangup extends AbstractEvent { 26 | 27 | /** 28 | * @param event 29 | */ 30 | public ChannelHangup(EslEvent event) { 31 | super(event); 32 | } 33 | 34 | /** 35 | * Gets the hangup cause. 36 | * 37 | * @return HnagupCause value of the hangup cause. 38 | */ 39 | public HangupCauses reason() { 40 | if (_event.eventHeaders().containsKey("Hangup-Cause")) { 41 | String cause = _event.eventHeaders().get("Hangup-Cause"); 42 | return Enum.valueOf(HangupCauses.class, cause); 43 | } 44 | 45 | return HangupCauses.NONE; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/ChannelHangupComplete.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | import io.freeswitch.common.HangupCauses; 19 | import org.apache.commons.lang3.StringUtils; 20 | import org.joda.time.DateTime; 21 | import org.joda.time.format.DateTimeFormatter; 22 | import org.joda.time.format.ISODateTimeFormat; 23 | 24 | /** 25 | * This event is raised whenever a call is hanged up. 26 | * 27 | * @author Arsene Tochemey GANDOTE 28 | */ 29 | public class ChannelHangupComplete extends AbstractEvent { 30 | 31 | /** 32 | * @param event 33 | */ 34 | public ChannelHangupComplete(EslEvent event) { 35 | super(event); 36 | } 37 | 38 | /** 39 | * Gets the hangup cause. 40 | * 41 | * @return HangupCause value of the hangup cause. 42 | */ 43 | public HangupCauses reason() { 44 | if (_event.eventHeaders().containsKey("Hangup-Cause")) { 45 | String cause = _event.eventHeaders().get("Hangup-Cause"); 46 | return Enum.valueOf(HangupCauses.class, cause); 47 | } 48 | 49 | return HangupCauses.NONE; 50 | } 51 | 52 | /** 53 | * Gets the call start time. Timestamp when the call was answered (eg, SIP 54 | * 200 OK is received), in ISO 8601 format (YYYY-MM-DD hh:mm:ss), in the 55 | * local timezone (not UTC). If the call is not answered, this will be an 56 | * empty string. 57 | * 58 | * @return DateTime value of the call start time. 59 | * @see DateTime 60 | */ 61 | public DateTime startTime() { 62 | DateTimeFormatter iso = ISODateTimeFormat.basicDateTimeNoMillis(); 63 | return iso.parseDateTime(_event.eventHeaders().get( 64 | "variable_start_stamp")); 65 | } 66 | 67 | /** 68 | * Gets the call end time. Timestamp when the call was hung up, in ISO 8601 69 | * format (YYYY-MM-DD hh:mm:ss), in the local timezone (not UTC). 70 | * 71 | * @return DateTime value of the call end time 72 | * @see DateTime 73 | */ 74 | public DateTime endTime() { 75 | DateTimeFormatter iso = ISODateTimeFormat.basicDateTimeNoMillis(); 76 | return iso.parseDateTime(_event.eventHeaders() 77 | .get("variable_end_stamp")); 78 | } 79 | 80 | /** 81 | * Gets the billing value of the call in seconds. The answered or billing 82 | * span of the calls in seconds, i.e. "end_stamp - answer_stamp". Should 83 | * only be > 0 in calls where HANGUP_CAUSE == NORMAL_CLEARING (16). 84 | * 85 | * @return long value of the billing span. 86 | */ 87 | public long billSec() { 88 | return Long.parseLong(_event.eventHeaders().get("variable_billsec")); 89 | } 90 | 91 | /** 92 | * Gets the call duration. The entire duration of the call, end to end in 93 | * seconds, i.e. "start_stamp - end_stamp". Duration should always be >= 94 | * billsec. 95 | * 96 | * @return long value of the call duration. 97 | */ 98 | public long duration() { 99 | return Long.parseLong(_event.eventHeaders().get("variable_duration")); 100 | } 101 | 102 | /** 103 | * Gets the time the call has been answered. Timestamp when the call was 104 | * answered (eg, SIP 200 OK is received), in ISO 8601 format (YYYY-MM-DD 105 | * hh:mm:ss), in the local timezone (not UTC). If the call is not answered, 106 | * this will be an empty string. 107 | * 108 | * @return DateTime value of the answer time. 109 | */ 110 | public DateTime answerTime() { 111 | String stamp = _event.eventHeaders().get("variable_answer_stamp"); 112 | if (StringUtils.isEmpty(stamp)) 113 | return null; 114 | DateTimeFormatter iso = ISODateTimeFormat.basicDateTimeNoMillis(); 115 | return iso.parseDateTime(stamp); 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/ChannelProgress.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | /** 19 | * Outbound a call,other party is alerting or a inbound call,this party is alerting 20 | * 21 | * @author Arsene Tochemey GANDOTE 22 | */ 23 | public class ChannelProgress extends AbstractEvent { 24 | 25 | /** 26 | * @param event 27 | */ 28 | public ChannelProgress(EslEvent event) { 29 | super(event); 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/ChannelProgressMedia.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | /** 19 | * Outbound a call,other party is alerting or a inbound call,this party is 20 | * alerting 21 | * 22 | * @author Arsene Tochemey GANDOTE 23 | */ 24 | public class ChannelProgressMedia extends AbstractEvent { 25 | 26 | /** 27 | * @param event 28 | */ 29 | public ChannelProgressMedia(EslEvent event) { 30 | super(event); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/ChannelUnbridge.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | /** 19 | * A bridge has been terminated. The call itself will most probably be 20 | * terminated since bridges exist during a call's lifespan. 21 | * 22 | * @author Arsene Tochemey GANDOTE 23 | */ 24 | public class ChannelUnbridge extends AbstractEvent { 25 | 26 | /** 27 | * @param event 28 | */ 29 | public ChannelUnbridge(EslEvent event) { 30 | super(event); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/Dtmf.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public class Dtmf extends AbstractEvent { 22 | 23 | /** 24 | * @param event 25 | */ 26 | public Dtmf(EslEvent event) { 27 | super(event); 28 | } 29 | 30 | /** 31 | * Gets the digit entered 32 | * 33 | * @return char value of the entered digit. 34 | */ 35 | public char digit() { 36 | String digits = _event.eventHeaders().get("DTMF-Digit"); 37 | return digits.charAt(0); 38 | } 39 | 40 | /** 41 | * Gets the digit duration. 42 | * 43 | * @return long value of the duration of the entered digit. 44 | */ 45 | public long duration() { 46 | String duration = _event.eventHeaders().get("DTMF-Duration"); 47 | return Long.parseLong(duration); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/EslEvent.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | import io.freeswitch.codec.FreeSwitchMessageHeaders; 19 | import io.freeswitch.codec.FreeSwitchMessageHeaders.HeaderName; 20 | import io.freeswitch.codec.FreeSwitchMessageHeaders.HeaderValue; 21 | import io.freeswitch.codec.HeaderParser; 22 | import io.freeswitch.message.FreeSwitchMessage; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import java.io.UnsupportedEncodingException; 27 | import java.net.URLDecoder; 28 | import java.util.*; 29 | 30 | /** 31 | * FreeSWITCH Event Socket event are decoded into this data 32 | * object. 33 | *

34 | * An ESL event is modelled as a collection of text lines. An event always has 35 | * several eventHeader lines, and optionally may have some eventBody lines. In 36 | * addition the messageHeaders of the original containing {@link FreeSwitchMessage} 37 | * which carried the event are also available. 38 | *

39 | * The eventHeader lines are parsed and cached in a map keyed by the eventHeader 40 | * name string. An event is always expected to have an "Event-HeaderName" eventHeader. 41 | * Commonly used eventHeader names are coded in {@link EslEventHeaderNames} 42 | *

43 | * Any eventBody lines are cached in a list. 44 | *

45 | * The messageHeader lines from the original message are cached in a map keyed 46 | * by {@link FreeSwitchMessageHeaders.HeaderName}. 47 | * 48 | * @author david varnes 49 | * @author Arsene Tochemey GANDOTE 50 | * @see EventHeaders 51 | */ 52 | public class EslEvent { 53 | private final Logger log = LoggerFactory.getLogger(this.getClass()); 54 | 55 | private final Map messageHeaders; 56 | private final Map eventHeaders; 57 | private final List eventBody; 58 | private boolean decodeEventHeaders = true; 59 | 60 | public EslEvent(FreeSwitchMessage rawMessage) { 61 | this(rawMessage, false); 62 | } 63 | 64 | public EslEvent(FreeSwitchMessage rawMessage, boolean parseCommandReply) { 65 | messageHeaders = rawMessage.headers(); 66 | eventHeaders = new HashMap(rawMessage.bodyLines() 67 | .size()); 68 | eventBody = new ArrayList(); 69 | // plain or xml body 70 | if (rawMessage.contentType().equals(HeaderValue.TEXT_EVENT_PLAIN)) { 71 | parsePlainBody(rawMessage.bodyLines()); 72 | } else if (rawMessage.contentType().equals(HeaderValue.TEXT_EVENT_XML)) { 73 | throw new IllegalStateException("XML event are not yet supported"); 74 | } else if (rawMessage.contentType().equals(HeaderValue.COMMAND_REPLY) 75 | && parseCommandReply) { 76 | parsePlainBody(rawMessage.bodyLines()); 77 | } else { 78 | throw new IllegalStateException("Unexpected EVENT content-type: " 79 | + rawMessage.contentType()); 80 | } 81 | } 82 | 83 | /** 84 | * The message headers of the original ESL message from which this event was 85 | * decoded. The message headers are stored in a map keyed by 86 | * {@link FreeSwitchMessageHeaders.HeaderName}. The string mapped value is the parsed content of 87 | * the header line (ie, it does not include the header name). 88 | * 89 | * @return map of header values 90 | */ 91 | public Map headers() { 92 | return messageHeaders; 93 | } 94 | 95 | /** 96 | * The event headers of this event. The headers are parsed and stored in a 97 | * map keyed by the string name of the header, and the string mapped value 98 | * is the parsed content of the event header line (ie, it does not include 99 | * the header name). 100 | * 101 | * @return map of event header values 102 | */ 103 | public Map eventHeaders() { 104 | return eventHeaders; 105 | } 106 | 107 | /** 108 | * Any event body lines that were present in the event. 109 | * 110 | * @return list of decoded event body lines, may be an empty list. 111 | */ 112 | public List eventBodyLines() { 113 | return eventBody; 114 | } 115 | 116 | /** 117 | * Convenience method. 118 | * 119 | * @return the string value of the event header "Event-HeaderName" 120 | */ 121 | public String eventName() { 122 | return eventHeaders().get(EventHeaders.EVENT_NAME); 123 | } 124 | 125 | /** 126 | * Convenience method. 127 | * 128 | * @return long value of the event header "Event-Date-Timestamp" 129 | */ 130 | public long eventDateTimestamp() { 131 | return Long 132 | .valueOf(eventHeaders().get(EventHeaders.EVENT_DATE_TIMESTAMP)); 133 | } 134 | 135 | /** 136 | * Convenience method. 137 | * 138 | * @return long value of the event header "Event-Date-Local" 139 | */ 140 | public String eventDateLocal() { 141 | return eventHeaders().get(EventHeaders.EVENT_DATE_LOCAL); 142 | } 143 | 144 | /** 145 | * Convenience method. 146 | * 147 | * @return long value of the event header "Event-Date-GMT" 148 | */ 149 | public String eventDateGmt() { 150 | return eventHeaders().get(EventHeaders.EVENT_DATE_GMT); 151 | } 152 | 153 | /** 154 | * Convenience method. 155 | * 156 | * @return UUID value of the event header "Unique-ID" 157 | */ 158 | public UUID uniqueId() { 159 | String uniqueId = eventHeaders().get(EventHeaders.UNIQUE_ID); 160 | return UUID.fromString(uniqueId); 161 | } 162 | 163 | /** 164 | * Convenience method. 165 | * 166 | * @return true if the eventBody list is not empty. 167 | */ 168 | public boolean hasEventBody() { 169 | return !eventBody.isEmpty(); 170 | } 171 | 172 | private void parsePlainBody(final List rawBodyLines) { 173 | boolean isEventBody = false; 174 | for (String rawLine : rawBodyLines) { 175 | if (!isEventBody) { 176 | // split the line 177 | String[] headerParts = HeaderParser.splitHeader(rawLine); 178 | if (decodeEventHeaders) { 179 | try { 180 | String decodedValue = URLDecoder.decode(headerParts[1], 181 | "UTF-8"); 182 | log.trace("decoded from: [{}]", headerParts[1]); 183 | log.trace("decoded to: [{}]", decodedValue); 184 | eventHeaders.put(headerParts[0], decodedValue); 185 | } catch (UnsupportedEncodingException e) { 186 | log.warn("Could not URL decode [{}]", headerParts[1]); 187 | eventHeaders.put(headerParts[0], headerParts[1]); 188 | } 189 | } else { 190 | eventHeaders.put(headerParts[0], headerParts[1]); 191 | } 192 | if (headerParts[0].equals(EventHeaders.CONTENT_LENGTH)) { 193 | // the remaining lines will be considered body lines 194 | isEventBody = true; 195 | } 196 | } else { 197 | // ignore blank line (always is one following the content-length 198 | if (rawLine.length() > 0) { 199 | eventBody.add(rawLine); 200 | } 201 | } 202 | } 203 | 204 | } 205 | 206 | @Override 207 | public String toString() { 208 | StringBuilder sb = new StringBuilder("EslEvent: name=["); 209 | sb.append(eventName()); 210 | sb.append("] headers="); 211 | sb.append(messageHeaders.size()); 212 | sb.append(", eventHeaders="); 213 | sb.append(eventHeaders.size()); 214 | sb.append(", eventBody="); 215 | sb.append(eventBody.size()); 216 | sb.append(" lines."); 217 | 218 | return sb.toString(); 219 | } 220 | 221 | } 222 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/EventHeaders.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | /** 19 | * @author Arsene Tocheme GANDOTE 20 | */ 21 | public class EventHeaders { 22 | /** 23 | * {@code "Event-HeaderName"} 24 | */ 25 | public static final String EVENT_NAME = "Event-HeaderName"; 26 | /** 27 | * {@code "Event-Date-Local"} 28 | */ 29 | public static final String EVENT_DATE_LOCAL = "Event-Date-Local"; 30 | /** 31 | * {@code "Event-Date-GMT"} 32 | */ 33 | public static final String EVENT_DATE_GMT = "Event-Date-GMT"; 34 | /** 35 | * {@code "Event-Date-Timestamp"} 36 | */ 37 | public static final String EVENT_DATE_TIMESTAMP = "Event-Date-Timestamp"; 38 | /** 39 | * {@code "Event-Calling-File"} 40 | */ 41 | public static final String EVENT_CALLING_FILE = "Event-Calling-File"; 42 | /** 43 | * {@code "Event-Calling-Function"} 44 | */ 45 | public static final String EVENT_CALLING_FUNCTION = "Event-Calling-Function"; 46 | /** 47 | * {@code "Event-Calling-Line-Number"} 48 | */ 49 | public static final String EVENT_CALLING_LINE_NUMBER = "Event-Calling-Line-Number"; 50 | /** 51 | * {@code "FreeSWITCH-Hostname"} 52 | */ 53 | public static final String FREESWITCH_HOSTNAME = "FreeSWITCH-Hostname"; 54 | /** 55 | * {@code "FreeSWITCH-IPv4"} 56 | */ 57 | public static final String FREESWITCH_IPV4 = "FreeSWITCH-IPv4"; 58 | /** 59 | * {@code "FreeSWITCH-IPv6"} 60 | */ 61 | public static final String FREESWITCH_IPV6 = "FreeSWITCH-IPv6"; 62 | /** 63 | * {@code "Core-UUID"} 64 | */ 65 | public static final String CORE_UUID = "Core-UUID"; 66 | /** 67 | * {@code "Content-Length"} 68 | */ 69 | public static final String CONTENT_LENGTH = "Content-Length"; 70 | /** 71 | * {@code "Job-Command"} 72 | */ 73 | public static final String JOB_COMMAND = "Job-Command"; 74 | /** 75 | * {@code "Job-UUID"} 76 | */ 77 | public static final String JOB_UUID = "Job-UUID"; 78 | /** 79 | * {@code "Event-Sequence"} 80 | */ 81 | public static final String EVENT_SEQUENCE = "Event-Sequence"; 82 | /** 83 | * {@code "Event-Subclass"} 84 | */ 85 | public static final String EVENT_SUBCLASS = "Event-Subclass"; 86 | /** 87 | * {@code "Unique-ID"} 88 | */ 89 | public static final String UNIQUE_ID = "Unique-ID"; 90 | 91 | private EventHeaders() { 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/IEventsListener.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | /** 19 | * Interface for observers wanting to be notified of incoming FreeSWITCH Event Socket event. 20 | *

21 | * Incoming event arrive asynchronously and are processed into two queues, one for server 22 | * initiated event, and one for the results of client requested background jobs. 23 | *

24 | * Each queue is serviced by a different thread pool (to ensure lowest latency for event-driven event) 25 | * and each queue is guaranteed to be processed (and listeners notified) in the order in which the 26 | * event are received off the wire. 27 | *

28 | * This design ensures that incoming event processing is not blocked by any long-running listener process. 29 | * However multiple listeners will be notified sequentially, and so one slow listener can cause latency 30 | * to other listeners. 31 | * 32 | * @author david varnes 33 | * @author Arsene Tochemey GANDOTE 34 | */ 35 | public interface IEventsListener { 36 | /** 37 | * Signal of a server initiated event. 38 | * 39 | * @param event as an {@link EslEvent} 40 | */ 41 | void eventReceived(EslEvent event); 42 | 43 | /** 44 | * Signal of an event containing the result of a client requested background job. The Job-UUID will 45 | * be available as an event header of that name. 46 | * 47 | * @param event as an {@link EslEvent} 48 | */ 49 | void backgroundJobEventReceived(EslEvent event); 50 | } 51 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/RecordStop.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public class RecordStop extends AbstractEvent { 22 | 23 | /** 24 | * @param event 25 | */ 26 | public RecordStop(EslEvent event) { 27 | super(event); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/event/SessionHeartbeat.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.event; 17 | 18 | /** 19 | * @author Arsene Tochemey GANDOTE 20 | */ 21 | public class SessionHeartbeat extends AbstractEvent { 22 | 23 | /** 24 | * @param event 25 | */ 26 | public SessionHeartbeat(EslEvent event) { 27 | super(event); 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/inbound/DefaultFreeSwitchHandler.java: -------------------------------------------------------------------------------- 1 | package io.freeswitch.inbound; 2 | 3 | import io.freeswitch.event.EslEvent; 4 | import org.apache.commons.lang3.StringUtils; 5 | import org.jboss.netty.channel.ChannelHandlerContext; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | 9 | import java.net.SocketAddress; 10 | import java.util.UUID; 11 | 12 | /** 13 | * Created by Arsene Tochemey GANDOTE on 19/11/2015. 14 | */ 15 | public class DefaultFreeSwitchHandler extends FreeSwitchHandler { 16 | 17 | private final Logger log = LoggerFactory.getLogger(this.getClass()); 18 | 19 | /** 20 | * instance of the connected freeSwitch 21 | */ 22 | private FreeSwitchSession _session; 23 | 24 | @Override 25 | protected void handleConnectResponse(ChannelHandlerContext ctx, EslEvent event) { 26 | if(StringUtils.equalsIgnoreCase(event.eventName(), "CHANNEL_DATA")){ 27 | // Let us get the necessary data to set the session 28 | SocketAddress address = ctx.getChannel().getRemoteAddress(); 29 | UUID sessionId = event.uniqueId(); 30 | UUID callerId = UUID.fromString(event.eventHeaders().get("Caller-Unique-ID")); 31 | String callerIdNumber = event.eventHeaders().get("Caller-Caller-ID-Number"); 32 | String destinationNumber = event.eventHeaders().get("Channel-Destination-Number"); 33 | _session = new FreeSwitchSession(sessionId, callerId, callerIdNumber, destinationNumber, event.eventHeaders(), address); 34 | return; 35 | } 36 | throw new IllegalStateException( "Unexpected event after connect: [" + event.eventName() + ']' ); 37 | } 38 | 39 | @Override 40 | protected void handleDisconnectionNotice(ChannelHandlerContext ctx) { 41 | 42 | } 43 | 44 | @Override 45 | protected void handleEslEvent(ChannelHandlerContext ctx, EslEvent event) { 46 | // // TODO: 19/11/2015 implements the events handlers 47 | } 48 | 49 | @Override 50 | protected void handleException(ChannelHandlerContext ctx, Throwable cause) { 51 | // // TODO: 19/11/2015 implements the exception handling 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/inbound/DefaultInboundPipelineFactory.java: -------------------------------------------------------------------------------- 1 | package io.freeswitch.inbound; 2 | 3 | /** 4 | * Created by Arsene Tochemey GANDOTE on 19/11/2015. 5 | */ 6 | public class DefaultInboundPipelineFactory extends FreeSwitchPipelineFactory { 7 | @Override 8 | protected FreeSwitchHandler buildHandler() { 9 | return null; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/inbound/FreeSwitchHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.inbound; 17 | 18 | import io.freeswitch.codec.FreeSwitchMessageHeaders.HeaderName; 19 | import io.freeswitch.codec.FreeSwitchMessageHeaders.HeaderValue; 20 | import io.freeswitch.command.ConnectCommand; 21 | import io.freeswitch.event.EslEvent; 22 | import io.freeswitch.message.FreeSwitchMessage; 23 | import org.jboss.netty.channel.*; 24 | import org.slf4j.Logger; 25 | import org.slf4j.LoggerFactory; 26 | 27 | import java.util.Queue; 28 | import java.util.concurrent.ConcurrentLinkedQueue; 29 | import java.util.concurrent.CountDownLatch; 30 | import java.util.concurrent.locks.Lock; 31 | import java.util.concurrent.locks.ReentrantLock; 32 | 33 | /** 34 | * @author Arsene Tochemey GANDOTE 35 | */ 36 | @ChannelHandler.Sharable 37 | public abstract class FreeSwitchHandler extends SimpleChannelUpstreamHandler { 38 | 39 | public static final String MESSAGE_TERMINATOR = "\n\n"; 40 | public static final String LINE_TERMINATOR = "\n"; 41 | protected final Logger log = LoggerFactory.getLogger(this.getClass()); 42 | private final Lock syncLock = new ReentrantLock(); 43 | private final Queue syncCallbacks = new ConcurrentLinkedQueue(); 44 | 45 | @Override 46 | public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { 47 | 48 | Channel channel = ctx.getChannel(); 49 | // Have received a connection from FreeSWITCH server, send connect 50 | // response 51 | log.debug( 52 | "Received new connection from server [{}], sending connect message", 53 | channel.getLocalAddress().toString()); 54 | ConnectCommand connect = new ConnectCommand(); 55 | FreeSwitchMessage response = sendSyncCommand(ctx.getChannel(), 56 | connect.toString()); 57 | // The message decoder for outbound, treats most of this incoming 58 | // message as an 'event' in 59 | // message body, so it parse now 60 | EslEvent channelDataEvent = new EslEvent(response, true); 61 | // Let implementing sub classes choose what to do next 62 | handleConnectResponse(ctx, channelDataEvent); 63 | } 64 | 65 | @Override 66 | public void channelDisconnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { 67 | handleDisconnectionNotice(ctx); 68 | } 69 | 70 | @Override 71 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) 72 | throws Exception { 73 | if (e.getMessage() instanceof FreeSwitchMessage) { 74 | FreeSwitchMessage message = (FreeSwitchMessage) e.getMessage(); 75 | String contentType = message.contentType(); 76 | if (contentType.equals(HeaderValue.TEXT_EVENT_PLAIN) 77 | || contentType.equals(HeaderValue.TEXT_EVENT_XML)) { 78 | // transform into an event 79 | EslEvent eslEvent = new EslEvent(message); 80 | handleEslEvent(ctx, eslEvent); 81 | } else { 82 | handleEslMessage(ctx, (FreeSwitchMessage) e.getMessage()); 83 | } 84 | return; 85 | } 86 | throw new IllegalStateException("Unexpected message type: " 87 | + e.getMessage().getClass()); 88 | } 89 | 90 | @Override 91 | public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) { 92 | handleException(ctx, e.getCause()); 93 | } 94 | 95 | protected abstract void handleConnectResponse(ChannelHandlerContext ctx, 96 | EslEvent event); 97 | 98 | protected abstract void handleDisconnectionNotice(ChannelHandlerContext ctx); 99 | 100 | protected abstract void handleEslEvent(ChannelHandlerContext ctx, 101 | EslEvent event); 102 | 103 | protected void handleEslMessage(ChannelHandlerContext ctx, 104 | FreeSwitchMessage message) { 105 | log.info("Received message: [{}]", message); 106 | String contentType = message.contentType(); 107 | 108 | if (contentType.equals(HeaderValue.API_RESPONSE)) { 109 | log.debug("Api response received [{}]", message); 110 | syncCallbacks.poll().handle(message); 111 | } else if (contentType.equals(HeaderValue.COMMAND_REPLY)) { 112 | log.debug("Command reply received [{}]", message); 113 | syncCallbacks.poll().handle(message); 114 | } else if (contentType.equals(HeaderValue.TEXT_DISCONNECT_NOTICE)) { 115 | log.debug("Disconnect notice received [{}]", message); 116 | handleDisconnectionNotice(ctx); 117 | } else { 118 | log.warn("Unexpected message content type [{}]", contentType); 119 | } 120 | } 121 | 122 | protected abstract void handleException(ChannelHandlerContext ctx, 123 | Throwable cause); 124 | 125 | /** 126 | * Returns the Job UUID of that the response event will have. 127 | * 128 | * @param channel 129 | * @param command 130 | * @return Job-UUID as a string 131 | */ 132 | public String sendAsyncCommand(Channel channel, final String command) { 133 | /* 134 | * Send synchronously to get the Job-UUID to return, the results of the 135 | * actual job request will be returned by the server as an async event. 136 | */ 137 | FreeSwitchMessage response = sendSyncCommand(channel, command); 138 | if (response.hasHeader(HeaderName.JOB_UUID)) { 139 | return response.headerValue(HeaderName.JOB_UUID); 140 | } else { 141 | throw new IllegalStateException( 142 | "Missing Job-UUID header in bgapi response"); 143 | } 144 | } 145 | 146 | 147 | /** 148 | * Synthesise a synchronous command/response by creating a callback object 149 | * which is placed in queue and blocks waiting for another IO thread to 150 | * process an incoming {@link FreeSwitchMessage} and attach it to the callback. 151 | * 152 | * @param channel 153 | * @param command single string to send 154 | * @return the {@link FreeSwitchMessage} attached to this command's callback 155 | */ 156 | public FreeSwitchMessage sendSyncCommand(Channel channel, 157 | final String command) { 158 | SyncCallback callback = new SyncCallback(); 159 | syncLock.lock(); 160 | try { 161 | syncCallbacks.add(callback); 162 | channel.write(command + MESSAGE_TERMINATOR); 163 | } finally { 164 | syncLock.unlock(); 165 | } 166 | 167 | // Block until the response is available 168 | return callback.get(); 169 | } 170 | 171 | private static class SyncCallback { 172 | 173 | private static final Logger log = LoggerFactory 174 | .getLogger(SyncCallback.class); 175 | private final CountDownLatch latch = new CountDownLatch(1); 176 | private FreeSwitchMessage response; 177 | 178 | /** 179 | * Block waiting for the countdown latch to be released, then return the 180 | * associated response object. 181 | * 182 | * @return 183 | */ 184 | FreeSwitchMessage get() { 185 | try { 186 | log.trace("awaiting latch ... "); 187 | latch.await(); 188 | } catch (InterruptedException e) { 189 | throw new RuntimeException(e); 190 | } 191 | 192 | log.trace("returning response [{}]", response); 193 | return response; 194 | } 195 | 196 | /** 197 | * Attach this response to the callback and release the countdown latch. 198 | * 199 | * @param response 200 | */ 201 | void handle(FreeSwitchMessage response) { 202 | this.response = response; 203 | log.trace("releasing latch for response [{}]", response); 204 | latch.countDown(); 205 | } 206 | } 207 | 208 | } 209 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/inbound/FreeSwitchPipelineFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.inbound; 17 | 18 | import io.freeswitch.ExecutionHandler; 19 | import io.freeswitch.codec.FreeSwitchDecoder; 20 | import org.jboss.netty.channel.ChannelPipelineFactory; 21 | import org.jboss.netty.channel.Channels; 22 | import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor; 23 | 24 | /** 25 | * @author Arsene Tochemey GANDOTE 26 | */ 27 | public abstract class FreeSwitchPipelineFactory implements ChannelPipelineFactory { 28 | 29 | public org.jboss.netty.channel.ChannelPipeline getPipeline() throws Exception { 30 | org.jboss.netty.channel.ChannelPipeline pipeline = Channels.pipeline(); 31 | pipeline.addLast("encoder", new org.jboss.netty.handler.codec.string.StringEncoder()); 32 | pipeline.addLast("decoder", new FreeSwitchDecoder(8192, true)); 33 | // Add an executor to ensure separate thread for each upstream message from here 34 | pipeline.addLast("executor", new ExecutionHandler( 35 | new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576))); 36 | 37 | // now the inbound client logic 38 | pipeline.addLast("clientHandler", buildHandler()); 39 | 40 | return pipeline; 41 | } 42 | 43 | protected abstract FreeSwitchHandler buildHandler(); 44 | } 45 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/inbound/FreeSwitchServer.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.inbound; 17 | 18 | import org.jboss.netty.bootstrap.ServerBootstrap; 19 | import org.jboss.netty.channel.Channel; 20 | import org.jboss.netty.channel.ChannelFactory; 21 | import org.jboss.netty.channel.group.ChannelGroup; 22 | import org.jboss.netty.channel.group.ChannelGroupFuture; 23 | import org.jboss.netty.channel.group.DefaultChannelGroup; 24 | import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; 25 | import org.slf4j.Logger; 26 | import org.slf4j.LoggerFactory; 27 | 28 | import java.net.InetSocketAddress; 29 | import java.util.concurrent.Executors; 30 | 31 | /** 32 | * Entry point to run a socket client that a running FreeSWITCH Event Socket 33 | * Library module can make outbound connections to. 34 | *

35 | * This class provides for what the FreeSWITCH documentation refers to as 36 | * 'Outbound' connections from the Event Socket module. That is, with reference 37 | * to the module running on the FreeSWITCH server, this client accepts an 38 | * outbound connection from the server module. 39 | *

40 | * See http://wiki. 41 | * freeswitch.org/wiki/Mod_event_socket 42 | * 43 | * @author Arsene Tochemey GANDOTE 44 | */ 45 | public class FreeSwitchServer { 46 | 47 | private final Logger log = LoggerFactory.getLogger(this.getClass()); 48 | 49 | /** 50 | * Binding port. 51 | */ 52 | private final int port; 53 | private final ChannelGroup allChannels = new DefaultChannelGroup("esl-socket-server"); 54 | 55 | private final ChannelFactory channelFactory; 56 | private final FreeSwitchPipelineFactory pipelineFactory; 57 | 58 | /** 59 | * 60 | */ 61 | public FreeSwitchServer(int port, 62 | FreeSwitchPipelineFactory pipelineFactory) { 63 | this.port = port; 64 | this.pipelineFactory = pipelineFactory; 65 | this.channelFactory = new NioServerSocketChannelFactory( 66 | Executors.newCachedThreadPool(), 67 | Executors.newCachedThreadPool()); 68 | } 69 | 70 | /** 71 | * start() 72 | * 73 | * @throws InterruptedException 74 | */ 75 | public void start() throws InterruptedException { 76 | ServerBootstrap bootstrap = new ServerBootstrap(channelFactory); 77 | 78 | bootstrap.setPipelineFactory(pipelineFactory); 79 | bootstrap.setOption("child.tcpNoDelay", true); 80 | bootstrap.setOption("child.keepAlive", true); 81 | Channel serverChannel = bootstrap.bind(new InetSocketAddress(port)); 82 | allChannels.add(serverChannel); 83 | log.info("SocketClient waiting for connections on port [{}] ...", port); 84 | } 85 | 86 | /** 87 | * stop() 88 | * 89 | * @throws InterruptedException 90 | */ 91 | public void stop() throws InterruptedException { 92 | // Wait until the server socket is closed. 93 | ChannelGroupFuture future = allChannels.close(); 94 | future.awaitUninterruptibly(); 95 | channelFactory.releaseExternalResources(); 96 | log.info("SocketClient stopped"); 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/inbound/FreeSwitchSession.java: -------------------------------------------------------------------------------- 1 | package io.freeswitch.inbound; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | 6 | import java.net.SocketAddress; 7 | import java.util.Map; 8 | import java.util.UUID; 9 | 10 | /** 11 | * Created by Arsene Tochemey GANDOTE on 19/11/2015. 12 | */ 13 | public class FreeSwitchSession { 14 | private final Logger log = LoggerFactory.getLogger(this.getClass()); 15 | 16 | /** 17 | * Unique Identifier of the connected freeSwitch. It is the same as the Unique-ID of the connected call. 18 | */ 19 | private final UUID _sessionId; 20 | 21 | /** 22 | * The caller id. Value of the channel variable Caller-Unique-ID 23 | */ 24 | private final UUID _callerId; 25 | 26 | /** 27 | * The caller id number. Value of the channel variable Caller-Caller-ID-Number 28 | */ 29 | private final String _callerIdNumber; 30 | 31 | /** 32 | * The destination number. Value of the channel variable Channel-Destination-Number 33 | */ 34 | private final String _destinationNumber; 35 | 36 | /** 37 | * Other channel variables 38 | */ 39 | private final Map _channelVariables; 40 | 41 | /** 42 | * Connection details 43 | */ 44 | private final SocketAddress _address; 45 | 46 | public FreeSwitchSession(UUID sessionId, UUID callerId, String callerIdNumber, String destinationNumber, Map variables, SocketAddress address) { 47 | _sessionId = sessionId; 48 | _callerId = callerId; 49 | _callerIdNumber = callerIdNumber; 50 | _destinationNumber = destinationNumber; 51 | _channelVariables = variables; 52 | _address = address; 53 | } 54 | 55 | public UUID sessionId() { 56 | return _sessionId; 57 | } 58 | 59 | public UUID callerId() { 60 | return _callerId; 61 | } 62 | 63 | public String callerIdNumber() { 64 | return _callerIdNumber; 65 | } 66 | 67 | public String destinationNumber() { 68 | return _destinationNumber; 69 | } 70 | 71 | public Map channelVariables() { 72 | return _channelVariables; 73 | } 74 | 75 | public SocketAddress address() { 76 | return _address; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/message/CommandReply.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.message; 17 | 18 | import io.freeswitch.codec.FreeSwitchMessageHeaders; 19 | 20 | /** 21 | * @author Arsene Tochemey GANDOTE 22 | */ 23 | public class CommandReply { 24 | private final String _command; 25 | private final String _replyText; 26 | private final FreeSwitchMessage _response; 27 | private final boolean _success; 28 | 29 | public CommandReply(String command, FreeSwitchMessage response) { 30 | this._command = command; 31 | this._response = response; 32 | this._replyText = response.headerValue(FreeSwitchMessageHeaders.HeaderName.REPLY_TEXT); 33 | this._success = _replyText.startsWith("+OK"); 34 | } 35 | 36 | /** 37 | * @return the original command sent to the server 38 | */ 39 | public String command() { 40 | return _command; 41 | } 42 | 43 | /** 44 | * @return true if and only if the response Reply-Text line starts with 45 | * "+OK" 46 | */ 47 | public boolean isOk() { 48 | return _success; 49 | } 50 | 51 | /** 52 | * @return the full response Reply-Text line. 53 | */ 54 | public String replyText() { 55 | return _replyText; 56 | } 57 | 58 | /** 59 | * @return {@link FreeSwitchMessage} the full response from the server 60 | */ 61 | public FreeSwitchMessage response() { 62 | return _response; 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/message/FreeSwitchMessage.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.message; 17 | 18 | import io.freeswitch.codec.FreeSwitchMessageHeaders; 19 | import io.freeswitch.codec.FreeSwitchMessageHeaders.HeaderName; 20 | import org.slf4j.Logger; 21 | import org.slf4j.LoggerFactory; 22 | 23 | import java.util.ArrayList; 24 | import java.util.HashMap; 25 | import java.util.List; 26 | import java.util.Map; 27 | 28 | /** 29 | * @author Arsene Tochemey GANDOTE 30 | * @author david varnes 31 | */ 32 | public class FreeSwitchMessage { 33 | private final Logger log = LoggerFactory.getLogger(this.getClass()); 34 | 35 | private final Map headers = new HashMap(); 36 | private final List body = new ArrayList(); 37 | 38 | private Integer contentLength = null; 39 | 40 | /** 41 | * All the received message headers in a map keyed by 42 | * {@link FreeSwitchMessageHeaders.HeaderName}. The string mapped value is the parsed content of 43 | * the header line (ie, it does not include the header name). 44 | * 45 | * @return map of header values 46 | */ 47 | public Map headers() { 48 | return headers; 49 | } 50 | 51 | /** 52 | * Convenience method 53 | * 54 | * @param headerName as a {@link FreeSwitchMessageHeaders.HeaderName} 55 | * @return true if an only if there is a header entry with the supplied 56 | * header name 57 | */ 58 | public boolean hasHeader(FreeSwitchMessageHeaders.HeaderName headerName) { 59 | return headers.containsKey(headerName); 60 | } 61 | 62 | /** 63 | * Convenience method 64 | * 65 | * @param headerName as a {@link HeaderName} 66 | * @return same as getHeaders().get( headerName ) 67 | */ 68 | public String headerValue(FreeSwitchMessageHeaders.HeaderName headerName) { 69 | return headers.get(headerName); 70 | } 71 | 72 | /** 73 | * Convenience method 74 | * 75 | * @return true if and only if a header exists with name "Content-Length" 76 | */ 77 | public boolean hasContentLength() { 78 | return headers.containsKey(FreeSwitchMessageHeaders.HeaderName.CONTENT_LENGTH); 79 | } 80 | 81 | /** 82 | * Convenience method 83 | * 84 | * @return integer value of header with name "Content-Length" 85 | */ 86 | public Integer contentLength() { 87 | if (contentLength != null) { 88 | return contentLength; 89 | } 90 | if (hasContentLength()) { 91 | contentLength = Integer.valueOf(headers.get(FreeSwitchMessageHeaders.HeaderName.CONTENT_LENGTH)); 92 | } 93 | return contentLength; 94 | } 95 | 96 | /** 97 | * Convenience method 98 | * 99 | * @return header value of header with name "Content-Type" 100 | */ 101 | public String contentType() { 102 | return headers.get(FreeSwitchMessageHeaders.HeaderName.CONTENT_TYPE); 103 | } 104 | 105 | /** 106 | * Any received message body lines 107 | * 108 | * @return list with a string for each line received, may be an empty list 109 | */ 110 | public List bodyLines() { 111 | return body; 112 | } 113 | 114 | /** 115 | * Used by the {@link FreeSwitchDecoder}. 116 | * 117 | * @param name 118 | * @param value 119 | */ 120 | public void addHeader(FreeSwitchMessageHeaders.HeaderName name, String value) { 121 | log.debug("adding header [{}] [{}]", name, value); 122 | headers.put(name, value); 123 | } 124 | 125 | /** 126 | * Used by the {@link FreeSwitchDecoder} 127 | * 128 | * @param line 129 | */ 130 | public void addBodyLine(String line) { 131 | if (line == null) { 132 | return; 133 | } 134 | body.add(line); 135 | } 136 | 137 | @Override 138 | public String toString() { 139 | StringBuilder sb = new StringBuilder("FreeSwitchMessage: contentType=["); 140 | sb.append(contentType()); 141 | sb.append("] headers="); 142 | sb.append(headers.size()); 143 | sb.append(", body="); 144 | sb.append(body.size()); 145 | sb.append(" lines."); 146 | 147 | return sb.toString(); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/outbound/DefaultFreeSwitchClientHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.outbound; 17 | 18 | import io.freeswitch.IProtocolListener; 19 | import io.freeswitch.codec.FreeSwitchMessageHeaders.HeaderValue; 20 | import io.freeswitch.command.AuthCommand; 21 | import io.freeswitch.event.EslEvent; 22 | import io.freeswitch.message.CommandReply; 23 | import io.freeswitch.message.FreeSwitchMessage; 24 | import org.jboss.netty.channel.ChannelHandlerContext; 25 | 26 | /** 27 | * @author Arsene Tochemey GANDOTE 28 | */ 29 | public class DefaultFreeSwitchClientHandler extends FreeSwitchClientHandler { 30 | 31 | private final String password; 32 | private final IProtocolListener listener; 33 | 34 | public DefaultFreeSwitchClientHandler(String password, IProtocolListener listener) { 35 | this.password = password; 36 | this.listener = listener; 37 | } 38 | 39 | @Override 40 | protected void handleEslEvent(ChannelHandlerContext ctx, EslEvent event) { 41 | log.debug("Received event: [{}]", event); 42 | listener.eventReceived(event); 43 | } 44 | 45 | @Override 46 | protected void handleAuthRequest(ChannelHandlerContext ctx) { 47 | log.debug("Auth requested, sending [auth {}]", "*****"); 48 | AuthCommand auth = new AuthCommand(password); 49 | FreeSwitchMessage response = sendSyncCommand(ctx.getChannel(), auth.toString()); 50 | log.debug("Auth response [{}]", response); 51 | if (response.contentType().equals(HeaderValue.COMMAND_REPLY)) { 52 | CommandReply commandResponse = new CommandReply("auth " + password, 53 | response); 54 | listener.authResponseReceived(commandResponse); 55 | } else { 56 | log.error("Bad auth response message [{}]", response); 57 | throw new IllegalStateException("Incorrect auth response"); 58 | } 59 | } 60 | 61 | @Override 62 | protected void handleDisconnectionNotice() { 63 | log.debug("Received disconnection notice"); 64 | listener.disconnected(); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/outbound/DefaultFreeSwitchClientPipelineFactory.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.outbound; 17 | 18 | import io.freeswitch.ExecutionHandler; 19 | import io.freeswitch.codec.FreeSwitchDecoder; 20 | import org.jboss.netty.channel.ChannelHandler; 21 | import org.jboss.netty.channel.ChannelPipeline; 22 | import org.jboss.netty.channel.ChannelPipelineFactory; 23 | import org.jboss.netty.channel.Channels; 24 | import org.jboss.netty.handler.codec.string.StringEncoder; 25 | import org.jboss.netty.handler.execution.OrderedMemoryAwareThreadPoolExecutor; 26 | 27 | /** 28 | * @author Arsene Tochemey GANDOTE 29 | */ 30 | public class DefaultFreeSwitchClientPipelineFactory implements ChannelPipelineFactory { 31 | 32 | private final ChannelHandler handler; 33 | 34 | public DefaultFreeSwitchClientPipelineFactory(ChannelHandler handler) { 35 | this.handler = handler; 36 | } 37 | 38 | public ChannelPipeline getPipeline() throws Exception { 39 | ChannelPipeline pipeline = Channels.pipeline(); 40 | pipeline.addLast("encoder", new StringEncoder()); 41 | pipeline.addLast("decoder", new FreeSwitchDecoder(8192)); 42 | // Add an executor to ensure separate thread for each upstream message from here 43 | pipeline.addLast("executor", new ExecutionHandler( 44 | new OrderedMemoryAwareThreadPoolExecutor(16, 1048576, 1048576))); 45 | 46 | // now the inbound client logic 47 | pipeline.addLast("clientHandler", handler); 48 | 49 | return pipeline; 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/outbound/FreeSwitchClient.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.outbound; 17 | 18 | import io.freeswitch.ConnectionFailure; 19 | import io.freeswitch.IProtocolListener; 20 | import io.freeswitch.command.*; 21 | import io.freeswitch.common.LogLevels; 22 | import io.freeswitch.event.EslEvent; 23 | import io.freeswitch.event.IEventsListener; 24 | import io.freeswitch.message.CommandReply; 25 | import io.freeswitch.message.FreeSwitchMessage; 26 | import org.apache.commons.lang3.StringUtils; 27 | import org.jboss.netty.bootstrap.ClientBootstrap; 28 | import org.jboss.netty.channel.Channel; 29 | import org.jboss.netty.channel.ChannelFuture; 30 | import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; 31 | import org.slf4j.Logger; 32 | import org.slf4j.LoggerFactory; 33 | 34 | import java.net.InetSocketAddress; 35 | import java.util.List; 36 | import java.util.UUID; 37 | import java.util.concurrent.*; 38 | import java.util.concurrent.atomic.AtomicBoolean; 39 | import java.util.concurrent.atomic.AtomicInteger; 40 | 41 | /** 42 | * Entry point to connect to a running FreeSWITCH Event Socket Library module, 43 | * as a client. 44 | *

45 | * This class provides what the FreeSWITCH documentation refers to as an 46 | * 'Inbound' connection to the Event Socket module. That is, with reference to 47 | * the socket listening on the FreeSWITCH server, this client occurs as an 48 | * inbound connection to the server. 49 | *

50 | * See http://wiki. 51 | * freeswitch.org/wiki/Mod_event_socket 52 | * 53 | * @author Arsene Tochemey GANDOTE 54 | */ 55 | public class FreeSwitchClient { 56 | 57 | private final Logger log = LoggerFactory.getLogger(this.getClass()); 58 | 59 | private final List eventListeners = new CopyOnWriteArrayList(); 60 | private final Executor eventListenerExecutor = Executors 61 | .newSingleThreadExecutor(new ThreadFactory() { 62 | AtomicInteger threadNumber = new AtomicInteger(1); 63 | 64 | public Thread newThread(Runnable r) { 65 | return new Thread(r, "EslEventNotifier-" 66 | + threadNumber.getAndIncrement()); 67 | } 68 | }); 69 | 70 | private final Executor backgroundJobListenerExecutor = Executors 71 | .newSingleThreadExecutor(new ThreadFactory() { 72 | AtomicInteger threadNumber = new AtomicInteger(1); 73 | 74 | public Thread newThread(Runnable r) { 75 | return new Thread(r, "EslBackgroundJobNotifier-" 76 | + threadNumber.getAndIncrement()); 77 | } 78 | }); 79 | 80 | private AtomicBoolean authenticatorResponded = new AtomicBoolean(false); 81 | private boolean authenticated; 82 | private CommandReply authenticationResponse; 83 | /* 84 | * Internal observer of the ESL protocol 85 | */ 86 | private final IProtocolListener protocolListener = new IProtocolListener() { 87 | public void authResponseReceived(CommandReply response) { 88 | authenticatorResponded.set(true); 89 | authenticated = response.isOk(); 90 | authenticationResponse = response; 91 | log.debug("Auth response success={}, message=[{}]", authenticated, 92 | response.replyText()); 93 | } 94 | 95 | public void disconnected() { 96 | log.info("Disconnected .."); 97 | } 98 | 99 | public void eventReceived(final EslEvent event) { 100 | log.debug("Event received [{}]", event); 101 | /* 102 | * Notify listeners in a different thread in order to: - not to 103 | * block the IO threads with potentially long-running listeners - 104 | * generally be defensive running other people's code Use a 105 | * different worker thread pool for async job results than for event 106 | * driven event to keep the latency as low as possible. 107 | */ 108 | if (event.eventName().equals("BACKGROUND_JOB")) { 109 | for (final IEventsListener listener : eventListeners) { 110 | backgroundJobListenerExecutor.execute(new Runnable() { 111 | public void run() { 112 | try { 113 | listener.backgroundJobEventReceived(event); 114 | } catch (Throwable t) { 115 | log.error( 116 | "Error caught notifying listener of job result [" 117 | + event + ']', t); 118 | } 119 | } 120 | }); 121 | } 122 | } else { 123 | for (final IEventsListener listener : eventListeners) { 124 | eventListenerExecutor.execute(new Runnable() { 125 | public void run() { 126 | try { 127 | listener.eventReceived(event); 128 | } catch (Throwable t) { 129 | log.error( 130 | "Error caught notifying listener of event [" 131 | + event + ']', t); 132 | } 133 | } 134 | }); 135 | } 136 | } 137 | } 138 | }; 139 | private Channel channel; 140 | 141 | public void addEventListener(IEventsListener listener) { 142 | if (listener != null) { 143 | eventListeners.add(listener); 144 | } 145 | } 146 | 147 | /** 148 | * Sends a FreeSWITCH API command to the server and blocks, waiting for an 149 | * immediate response from the server. 150 | *

151 | * The outcome of the command from the server is returned in an 152 | * {@link FreeSwitchMessage} object. 153 | * 154 | * @param command API command to send 155 | * @param arg command arguments 156 | * @return an {@link FreeSwitchMessage} containing command results 157 | */ 158 | public FreeSwitchMessage api(String command, String arg) { 159 | checkConnected(); 160 | DefaultFreeSwitchClientHandler handler = (DefaultFreeSwitchClientHandler) channel.getPipeline().getLast(); 161 | 162 | StringBuilder sb = new StringBuilder(); 163 | if (!StringUtils.isEmpty(command)) { 164 | sb.append(command); 165 | } 166 | if (!StringUtils.isEmpty(arg)) { 167 | sb.append(' '); 168 | sb.append(arg); 169 | } 170 | 171 | // Here no command is really set to be executed 172 | if (StringUtils.isEmpty(sb.toString())) { 173 | return null; 174 | } 175 | 176 | ApiCommand api = new ApiCommand(sb.toString()); 177 | return handler.sendSyncCommand(channel, api.toString()); 178 | } 179 | 180 | /** 181 | * Submit a FreeSWITCH API command to the server to be executed in 182 | * background mode. A synchronous response from the server provides a UUID 183 | * to identify the job execution results. When the server has completed the 184 | * job execution it fires a BACKGROUND_JOB Event with the execution results. 185 | *

186 | * Note that this Client must be subscribed in the normal way to 187 | * BACKGOUND_JOB Events, in order to receive this event. 188 | * 189 | * @param command API command to send 190 | * @param arg command arguments 191 | * @return UUID Job-UUID that the server will tag result event with. 192 | */ 193 | public UUID bgApi(String command, String arg) { 194 | checkConnected(); 195 | DefaultFreeSwitchClientHandler handler = (DefaultFreeSwitchClientHandler) channel.getPipeline().getLast(); 196 | if (StringUtils.isEmpty(command)) { 197 | // Here no command is set to be executed. 198 | return null; 199 | } 200 | 201 | BgApiCommand bgapi = new BgApiCommand(command, arg); 202 | return UUID.fromString(handler.sendAsyncCommand(channel, 203 | bgapi.toString())); 204 | } 205 | 206 | public boolean canSend() { 207 | return channel != null && channel.isConnected() && authenticated; 208 | } 209 | 210 | /** 211 | * Check whether the connection to the FreeSwitch mod_event_socket is alive 212 | * or not. 213 | */ 214 | private void checkConnected() { 215 | if (!canSend()) { 216 | throw new IllegalStateException( 217 | "Not connected to FreeSWITCH Event Socket"); 218 | } 219 | } 220 | 221 | /** 222 | * Close the socket connection 223 | * 224 | * @return a {@link CommandReply} with the server's response. 225 | */ 226 | public CommandReply close() { 227 | checkConnected(); 228 | DefaultFreeSwitchClientHandler handler = (DefaultFreeSwitchClientHandler) channel.getPipeline().getLast(); 229 | ExitCommand exit = new ExitCommand(); 230 | FreeSwitchMessage response = handler.sendSyncCommand(channel, 231 | exit.toString()); 232 | 233 | return new CommandReply(exit.toString(), response); 234 | } 235 | 236 | /** 237 | * Attempt to establish an authenticated connection to the nominated 238 | * FreeSWITCH ESL server socket. This call will block, waiting for an 239 | * authentication handshake to occur, or timeout after the supplied number 240 | * of seconds. 241 | * 242 | * @param host can be either ip address or hostname 243 | * @param port tcp port that server socket is listening on (set in 244 | * event_socket_conf.xml) 245 | * @param password server event socket is expecting (set in 246 | * event_socket_conf.xml) 247 | * @param timeoutSeconds number of seconds to wait for the server socket 248 | * before aborting 249 | * @throws ConnectionFailure 250 | * @throws java.lang.InterruptedException 251 | */ 252 | public void connect(String host, int port, String password, 253 | int timeoutSeconds) throws ConnectionFailure, InterruptedException { 254 | 255 | // If already connected, disconnect first 256 | if (canSend()) { 257 | close(); 258 | } 259 | 260 | // Configure this client 261 | ClientBootstrap bootstrap = new ClientBootstrap( 262 | new NioClientSocketChannelFactory( 263 | Executors.newCachedThreadPool(), 264 | Executors.newCachedThreadPool())); 265 | 266 | // Add ESL handler 267 | DefaultFreeSwitchClientHandler handler = new DefaultFreeSwitchClientHandler(password, protocolListener); 268 | bootstrap.setPipelineFactory(new DefaultFreeSwitchClientPipelineFactory(handler)); 269 | 270 | // Make the connection attempt. 271 | ChannelFuture future = bootstrap.connect(new InetSocketAddress(host, port)); 272 | 273 | // Wait till attempt succeeds, fails or timeouts 274 | if (!future.awaitUninterruptibly(timeoutSeconds, TimeUnit.SECONDS)) { 275 | throw new ConnectionFailure("Timeout connecting to " + host + ":" + port); 276 | } 277 | 278 | // But may have failed anyway 279 | if (!future.isSuccess()) { 280 | log.warn("Failed to connect to [{}:{}]", host, port); 281 | channel = null; 282 | bootstrap.releaseExternalResources(); 283 | throw new ConnectionFailure("Could not connect to " + host + ":" 284 | + port, future.getCause()); 285 | } 286 | 287 | channel = future.getChannel(); 288 | // Wait for the authentication handshake to call back 289 | while (!authenticatorResponded.get()) { 290 | try { 291 | Thread.sleep(250); 292 | } catch (InterruptedException e) { 293 | // ignore 294 | } 295 | } 296 | 297 | if (!authenticated) { 298 | throw new ConnectionFailure("Authentication failed: " 299 | + authenticationResponse.replyText()); 300 | } 301 | } 302 | 303 | /** 304 | * Set the current event subscription for this connection to the server. 305 | * Examples of the event argument are: 306 | *

307 | *

308 |      *   ALL
309 |      *   CHANNEL_CREATE CHANNEL_DESTROY HEARTBEAT
310 |      *   CUSTOM conference::maintenance
311 |      *   CHANNEL_CREATE CHANNEL_DESTROY CUSTOM conference::maintenance sofia::register sofia::expire
312 |      * 
313 | *

314 | * Subsequent calls to this method replaces any previous subscriptions that 315 | * were set.

Note: current implementation can only process 'plain' 316 | * event. 317 | * 318 | * @param format can be { plain | xml } 319 | * @param events { all | space separated list of event } 320 | * @return a {@link CommandReply} with the server's response. 321 | */ 322 | public CommandReply event(String format, String events) { 323 | // temporary hack 324 | if (!format.equals("plain")) { 325 | throw new IllegalStateException( 326 | "Only 'plain' event format is supported at present"); 327 | } 328 | 329 | checkConnected(); 330 | DefaultFreeSwitchClientHandler handler = (DefaultFreeSwitchClientHandler) channel.getPipeline().getLast(); 331 | StringBuilder sb = new StringBuilder(); 332 | if (format != null && !format.isEmpty()) { 333 | sb.append(format); 334 | } 335 | if (events != null && !events.isEmpty()) { 336 | sb.append(' '); 337 | sb.append(events); 338 | } 339 | 340 | // Here no command is really set to be executed 341 | if (StringUtils.isEmpty(sb.toString())) { 342 | return null; 343 | } 344 | EventCommand event = new EventCommand(sb.toString()); 345 | FreeSwitchMessage response = handler.sendSyncCommand(channel, 346 | event.toString()); 347 | return new CommandReply(event.toString(), response); 348 | } 349 | 350 | /** 351 | * Send a {@link SendMsgCommand} command to FreeSWITCH. This client requires that 352 | * the {@link SendMsgCommand} has a call UUID parameter. 353 | * 354 | * @param sendMsg a {@link SendMsgCommand} with call UUID 355 | * @return a {@link CommandReply} with the server's response. 356 | */ 357 | public CommandReply execute(SendMsgCommand sendMsg) { 358 | checkConnected(); 359 | DefaultFreeSwitchClientHandler handler = (DefaultFreeSwitchClientHandler) channel.getPipeline().getLast(); 360 | FreeSwitchMessage response = handler.sendSyncCommand(channel, 361 | sendMsg.toString()); 362 | 363 | return new CommandReply(sendMsg.toString(), response); 364 | } 365 | 366 | /** 367 | * Add an event filter to the current set of event filters on this 368 | * connection. Any of the event headers can be used as a filter.

Note 369 | * that event filters follow 'filter-in' semantics. That is, when a filter 370 | * is applied only the filtered values will be received. Multiple filters 371 | * can be added to the current connection.

Example filters: 372 | *

373 | *

374 |      *    eventHeader        valueToFilter
375 |      *    ----------------------------------
376 |      *    Event-HeaderName         CHANNEL_EXECUTE
377 |      *    Channel-State      CS_NEW
378 |      * 
379 | * 380 | * @param eventHeader to filter on 381 | * @param valueToFilter the value to match 382 | * @return a {@link CommandReply} with the server's response. 383 | */ 384 | public CommandReply filter(String eventHeader, String valueToFilter) { 385 | checkConnected(); 386 | DefaultFreeSwitchClientHandler handler = (DefaultFreeSwitchClientHandler) channel.getPipeline().getLast(); 387 | StringBuilder sb = new StringBuilder(); 388 | if (eventHeader != null && !eventHeader.isEmpty()) { 389 | sb.append(eventHeader); 390 | } 391 | if (valueToFilter != null && !valueToFilter.isEmpty()) { 392 | sb.append(' '); 393 | sb.append(valueToFilter); 394 | } 395 | 396 | if (StringUtils.isEmpty(sb.toString())) { 397 | return null; 398 | } 399 | 400 | FilterCommand filter = new FilterCommand(sb.toString()); 401 | FreeSwitchMessage response = handler.sendSyncCommand(channel, 402 | filter.toString()); 403 | return new CommandReply(filter.toString(), response); 404 | } 405 | 406 | /** 407 | * Delete an event filter from the current set of event filters on this 408 | * connection. 409 | * 410 | * @param eventHeader to remove 411 | * @param valueToFilter to remove 412 | * @return a {@link CommandReply} with the server's response. 413 | */ 414 | public CommandReply filterDelete(String eventHeader, String valueToFilter) { 415 | checkConnected(); 416 | DefaultFreeSwitchClientHandler handler = (DefaultFreeSwitchClientHandler) channel.getPipeline().getLast(); 417 | StringBuilder sb = new StringBuilder(); 418 | if (eventHeader != null && !eventHeader.isEmpty()) { 419 | sb.append("delete "); 420 | sb.append(eventHeader); 421 | } 422 | if (valueToFilter != null && !valueToFilter.isEmpty()) { 423 | sb.append(' '); 424 | sb.append(valueToFilter); 425 | } 426 | if (StringUtils.isEmpty(sb.toString())) { 427 | return null; 428 | } 429 | 430 | FilterCommand filter = new FilterCommand(sb.toString()); 431 | FreeSwitchMessage response = handler.sendSyncCommand(channel, 432 | filter.toString()); 433 | return new CommandReply(filter.toString(), response); 434 | } 435 | 436 | /** 437 | * Cancel any existing event subscription. 438 | * 439 | * @return a {@link CommandReply} with the server's response. 440 | */ 441 | public CommandReply noevents() { 442 | checkConnected(); 443 | DefaultFreeSwitchClientHandler handler = (DefaultFreeSwitchClientHandler) channel.getPipeline().getLast(); 444 | NoEventsCommand noevents = new NoEventsCommand(); 445 | FreeSwitchMessage response = handler.sendSyncCommand(channel, 446 | noevents.toString()); 447 | return new CommandReply(noevents.toString(), response); 448 | } 449 | 450 | /** 451 | * Disable any logging previously enabled with setLogLevel(). 452 | * 453 | * @return a {@link CommandReply} with the server's response. 454 | */ 455 | public CommandReply nolog() { 456 | checkConnected(); 457 | DefaultFreeSwitchClientHandler handler = (DefaultFreeSwitchClientHandler) channel.getPipeline().getLast(); 458 | FreeSwitchMessage response = handler.sendSyncCommand(channel, 459 | new NologCommand().toString()); 460 | 461 | return new CommandReply("nolog", response); 462 | } 463 | 464 | /** 465 | * Enable log output. 466 | * 467 | * @param level using the same values as in console.conf 468 | * @return a {@link CommandReply} with the server's response. 469 | */ 470 | public CommandReply setLogLevel(LogLevels level) { 471 | checkConnected(); 472 | DefaultFreeSwitchClientHandler handler = (DefaultFreeSwitchClientHandler) channel.getPipeline().getLast(); 473 | LogCommand log = new LogCommand(level); 474 | FreeSwitchMessage response = handler.sendSyncCommand(channel, 475 | log.toString()); 476 | 477 | return new CommandReply(log.toString(), response); 478 | } 479 | 480 | } 481 | -------------------------------------------------------------------------------- /src/main/java/io/freeswitch/outbound/FreeSwitchClientHandler.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package io.freeswitch.outbound; 17 | 18 | import io.freeswitch.codec.FreeSwitchMessageHeaders; 19 | import io.freeswitch.codec.FreeSwitchMessageHeaders.HeaderValue; 20 | import io.freeswitch.event.EslEvent; 21 | import io.freeswitch.message.FreeSwitchMessage; 22 | import org.jboss.netty.channel.*; 23 | import org.slf4j.Logger; 24 | import org.slf4j.LoggerFactory; 25 | 26 | import java.util.Queue; 27 | import java.util.concurrent.ConcurrentLinkedQueue; 28 | import java.util.concurrent.CountDownLatch; 29 | import java.util.concurrent.locks.Lock; 30 | import java.util.concurrent.locks.ReentrantLock; 31 | 32 | /** 33 | * @author Arsene Tochemey GANDOTE 34 | * @author david varnes 35 | */ 36 | @ChannelHandler.Sharable 37 | public abstract class FreeSwitchClientHandler extends 38 | SimpleChannelUpstreamHandler { 39 | 40 | public static final String MESSAGE_TERMINATOR = "\n\n"; 41 | public static final String LINE_TERMINATOR = "\n"; 42 | 43 | protected final Logger log = LoggerFactory.getLogger(this.getClass()); 44 | 45 | private final Lock syncLock = new ReentrantLock(); 46 | private final Queue syncCallbacks = new ConcurrentLinkedQueue(); 47 | 48 | /** 49 | * 50 | */ 51 | public FreeSwitchClientHandler() { 52 | } 53 | 54 | @Override 55 | public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) 56 | throws Exception { 57 | if (e.getMessage() instanceof FreeSwitchMessage) { 58 | FreeSwitchMessage message = (FreeSwitchMessage) e.getMessage(); 59 | String contentType = message.contentType(); 60 | if (contentType.equals(HeaderValue.TEXT_EVENT_PLAIN) 61 | || contentType.equals(HeaderValue.TEXT_EVENT_XML)) { 62 | // transform into an event 63 | EslEvent eslEvent = new EslEvent(message); 64 | handleEslEvent(ctx, eslEvent); 65 | } else { 66 | handleEslMessage(ctx, (FreeSwitchMessage) e.getMessage()); 67 | } 68 | return; 69 | } 70 | throw new IllegalStateException("Unexpected message type: " 71 | + e.getMessage().getClass()); 72 | } 73 | 74 | protected abstract void handleEslEvent(ChannelHandlerContext ctx, 75 | EslEvent event); 76 | 77 | protected abstract void handleAuthRequest(ChannelHandlerContext ctx); 78 | 79 | protected abstract void handleDisconnectionNotice(); 80 | 81 | protected void handleEslMessage(ChannelHandlerContext ctx, 82 | FreeSwitchMessage message) { 83 | log.info("Received message: [{}]", message); 84 | String contentType = message.contentType(); 85 | 86 | if (contentType.equals(HeaderValue.API_RESPONSE)) { 87 | log.debug("Api response received [{}]", message); 88 | syncCallbacks.poll().handle(message); 89 | } else if (contentType.equals(HeaderValue.COMMAND_REPLY)) { 90 | log.debug("Command reply received [{}]", message); 91 | syncCallbacks.poll().handle(message); 92 | } else if (contentType.equals(HeaderValue.AUTH_REQUEST)) { 93 | log.debug("Auth request received [{}]", message); 94 | handleAuthRequest(ctx); 95 | } else if (contentType.equals(HeaderValue.TEXT_DISCONNECT_NOTICE)) { 96 | log.debug("Disconnect notice received [{}]", message); 97 | handleDisconnectionNotice(); 98 | } else { 99 | log.warn("Unexpected message content type [{}]", contentType); 100 | } 101 | } 102 | 103 | /** 104 | * Synthesise a synchronous command/response by creating a callback object 105 | * which is placed in queue and blocks waiting for another IO thread to 106 | * process an incoming {@link FreeSwitchMessage} and attach it to the callback. 107 | * 108 | * @param channel 109 | * @param command single string to send 110 | * @return the {@link FreeSwitchMessage} attached to this command's callback 111 | */ 112 | public FreeSwitchMessage sendSyncCommand(Channel channel, 113 | final String command) { 114 | SyncCallback callback = new SyncCallback(); 115 | syncLock.lock(); 116 | try { 117 | syncCallbacks.add(callback); 118 | String request = command + MESSAGE_TERMINATOR; 119 | log.debug("Command sent to freeSwitch [{}]", request); 120 | channel.write(request); 121 | } finally { 122 | syncLock.unlock(); 123 | } 124 | 125 | // Block until the response is available 126 | return callback.get(); 127 | } 128 | 129 | 130 | /** 131 | * Returns the Job UUID of that the response event will have. 132 | * 133 | * @param channel 134 | * @param command 135 | * @return Job-UUID as a string 136 | */ 137 | public String sendAsyncCommand(Channel channel, final String command) { 138 | /* 139 | * Send synchronously to get the Job-UUID to return, the results of the 140 | * actual job request will be returned by the server as an async event. 141 | */ 142 | FreeSwitchMessage response = sendSyncCommand(channel, command); 143 | if (response.hasHeader(FreeSwitchMessageHeaders.HeaderName.JOB_UUID)) { 144 | return response.headerValue(FreeSwitchMessageHeaders.HeaderName.JOB_UUID); 145 | } else { 146 | throw new IllegalStateException( 147 | "Missing Job-UUID header in bgapi response"); 148 | } 149 | } 150 | 151 | private static class SyncCallback { 152 | 153 | private static final Logger log = LoggerFactory 154 | .getLogger(SyncCallback.class); 155 | private final CountDownLatch latch = new CountDownLatch(1); 156 | private FreeSwitchMessage response; 157 | 158 | /** 159 | * Block waiting for the countdown latch to be released, then return the 160 | * associated response object. 161 | * 162 | * @return 163 | */ 164 | FreeSwitchMessage get() { 165 | try { 166 | log.trace("awaiting latch ... "); 167 | latch.await(); 168 | } catch (InterruptedException e) { 169 | throw new RuntimeException(e); 170 | } 171 | 172 | log.trace("returning response [{}]", response); 173 | return response; 174 | } 175 | 176 | /** 177 | * Attach this response to the callback and release the countdown latch. 178 | * 179 | * @param response 180 | */ 181 | void handle(FreeSwitchMessage response) { 182 | this.response = response; 183 | log.trace("releasing latch for response [{}]", response); 184 | latch.countDown(); 185 | } 186 | } 187 | 188 | } 189 | -------------------------------------------------------------------------------- /src/main/resources/logback.xml: -------------------------------------------------------------------------------- 1 | 2 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n 27 | 28 | 29 | 30 | 31 | 33 | ${DEV_HOME}/debug.log 34 | 35 | 36 | %d{yyyy-MM-dd HH:mm:ss} - %msg%n 37 | 38 | 39 | 40 | 41 | 42 | ${DEV_HOME}/archived/debug.%d{yyyy-MM-dd}.%i.log 43 | 44 | 46 | 10MB 47 | 48 | 49 | 50 | 51 | 52 | 54 | ${DEV_HOME}/error.log 55 | 56 | 57 | %d{yyyy-MM-dd HH:mm:ss} - %msg%n 58 | 59 | 60 | 61 | 62 | 63 | ${DEV_HOME}/archived/error.%d{yyyy-MM-dd}.%i.log 64 | 65 | 67 | 10MB 68 | 69 | 70 | 71 | 72 | 73 | 74 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | --------------------------------------------------------------------------------