This is an automated email from the ASF dual-hosted git repository. davsclaus pushed a commit to branch CAMEL-26185-asterisk-event-actions in repository https://gitbox.apache.org/repos/asf/camel.git
commit c0c730f855b081580bc5d24cf34d6528de3a61cd Author: Claus Ibsen <[email protected]> AuthorDate: Mon Nov 2 23:46:48 2026 +0100 CAMEL-26185: camel-asterisk - QUEUE_STATUS and SIP_PEERS return the events of the list QUEUE_STATUS and SIP_PEERS are event generating actions: Asterisk answers with a "will follow" response and sends the data as events. The producer sent every action with sendAction, so the body was only that response and the queue and peer events were lost. The producer now sends an EventGeneratingAction with sendEventGeneratingAction and sets the ResponseEvents (response and events) as the body. EXTENSION_STATE still gives the ManagerResponse. Adds a test against a fake AMI server, updates the body types, the component doc and the upgrade guide. Co-Authored-By: Claude Opus 5.5 <[email protected]> Signed-off-by: Claus Ibsen <[email protected]> --- .../apache/camel/catalog/components/asterisk.json | 2 +- .../camel/catalog/docs/asterisk-component.adoc | 9 + .../apache/camel/component/asterisk/asterisk.json | 2 +- .../src/main/docs/asterisk-component.adoc | 9 + .../component/asterisk/AsteriskConnection.java | 16 ++ .../camel/component/asterisk/AsteriskEndpoint.java | 19 +- .../camel/component/asterisk/AsteriskProducer.java | 20 +- .../asterisk/AsteriskProducerFakeServerTest.java | 251 +++++++++++++++++++++ .../ROOT/pages/camel-4x-upgrade-guide-4_24.adoc | 8 + 9 files changed, 326 insertions(+), 10 deletions(-) diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/asterisk.json b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/asterisk.json index 473fa1f31d1d..aa4a08e4aa59 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/asterisk.json +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/components/asterisk.json @@ -37,7 +37,7 @@ "bodyTypes": { "consumerOut": { "carriers": [ "pojo" ], "javaType": "org.asteriskjava.manager.event.ManagerEvent", "description": "The AMI event: a subclass of ManagerEvent such as NewChannelEvent or HangupEvent, with its simple class name in the CamelAsteriskEventName header. The ConnectEvent of the login comes first" }, "producerIn": { "carriers": [ "any" ], "description": "The body is not read: the action comes from the action option or the CamelAsteriskAction header, and the extension and context of EXTENSION_STATE from the CamelAsteriskExtension and CamelAsteriskContext headers" }, - "producerOut": { "carriers": [ "pojo" ], "javaType": "org.asteriskjava.manager.response.ManagerResponse", "description": "The ManagerResponse of the action, such as an ExtensionStateResponse; a ManagerError when Asterisk answers with an error. QUEUE_STATUS and SIP_PEERS give only the first response, not the events of the list" } + "producerOut": { "carriers": [ "pojo" ], "byOption": [ { "option": "action", "value": "QUEUE_STATUS", "carriers": [ "pojo" ], "javaType": "org.asteriskjava.manager.ResponseEvents" }, { "option": "action", "value": "SIP_PEERS", "carriers": [ "pojo" ], "javaType": "org.asteriskjava.manager.ResponseEvents" }, { "option": "action", "value": "EXTENSION_STATE", "carriers": [ "pojo" ], "javaType": "org.asteriskjava.manager.response.ManagerResponse" } ], "description": "QUEUE_STATUS and SIP_ [...] }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of component" }, diff --git a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/asterisk-component.adoc b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/asterisk-component.adoc index 5bb9ae53215f..fbfd1c6669a6 100644 --- a/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/asterisk-component.adoc +++ b/catalog/camel-catalog/src/generated/resources/org/apache/camel/catalog/docs/asterisk-component.adoc @@ -49,6 +49,15 @@ Supported actions are: * SIP_PEERS: List SIP peers * EXTENSION_STATE: Check extension status +QUEUE_STATUS and SIP_PEERS are answered with a list of events. The producer waits for the whole list and sets +the `org.asteriskjava.manager.ResponseEvents` as the message body: `getResponse()` is the response of Asterisk, +and `getEvents()` the events of the list, such as `QueueParamsEvent` and `QueueMemberEvent` for QUEUE_STATUS or +`PeerEntryEvent` for SIP_PEERS, ending with the complete event (`QueueStatusCompleteEvent` or +`PeerlistCompleteEvent`). To route each event on its own, split on `${body.events}`. + +EXTENSION_STATE sets the `ManagerResponse` (an `ExtensionStateResponse`) as the message body. +When Asterisk answers with an error, the response is a `ManagerError`. + == Examples === Producer Example diff --git a/components/camel-asterisk/src/generated/resources/META-INF/org/apache/camel/component/asterisk/asterisk.json b/components/camel-asterisk/src/generated/resources/META-INF/org/apache/camel/component/asterisk/asterisk.json index 473fa1f31d1d..aa4a08e4aa59 100644 --- a/components/camel-asterisk/src/generated/resources/META-INF/org/apache/camel/component/asterisk/asterisk.json +++ b/components/camel-asterisk/src/generated/resources/META-INF/org/apache/camel/component/asterisk/asterisk.json @@ -37,7 +37,7 @@ "bodyTypes": { "consumerOut": { "carriers": [ "pojo" ], "javaType": "org.asteriskjava.manager.event.ManagerEvent", "description": "The AMI event: a subclass of ManagerEvent such as NewChannelEvent or HangupEvent, with its simple class name in the CamelAsteriskEventName header. The ConnectEvent of the login comes first" }, "producerIn": { "carriers": [ "any" ], "description": "The body is not read: the action comes from the action option or the CamelAsteriskAction header, and the extension and context of EXTENSION_STATE from the CamelAsteriskExtension and CamelAsteriskContext headers" }, - "producerOut": { "carriers": [ "pojo" ], "javaType": "org.asteriskjava.manager.response.ManagerResponse", "description": "The ManagerResponse of the action, such as an ExtensionStateResponse; a ManagerError when Asterisk answers with an error. QUEUE_STATUS and SIP_PEERS give only the first response, not the events of the list" } + "producerOut": { "carriers": [ "pojo" ], "byOption": [ { "option": "action", "value": "QUEUE_STATUS", "carriers": [ "pojo" ], "javaType": "org.asteriskjava.manager.ResponseEvents" }, { "option": "action", "value": "SIP_PEERS", "carriers": [ "pojo" ], "javaType": "org.asteriskjava.manager.ResponseEvents" }, { "option": "action", "value": "EXTENSION_STATE", "carriers": [ "pojo" ], "javaType": "org.asteriskjava.manager.response.ManagerResponse" } ], "description": "QUEUE_STATUS and SIP_ [...] }, "properties": { "name": { "index": 0, "kind": "path", "displayName": "Name", "group": "common", "label": "", "required": true, "type": "string", "javaType": "java.lang.String", "deprecated": false, "deprecationNote": "", "autowired": false, "secret": false, "description": "Name of component" }, diff --git a/components/camel-asterisk/src/main/docs/asterisk-component.adoc b/components/camel-asterisk/src/main/docs/asterisk-component.adoc index 5bb9ae53215f..fbfd1c6669a6 100644 --- a/components/camel-asterisk/src/main/docs/asterisk-component.adoc +++ b/components/camel-asterisk/src/main/docs/asterisk-component.adoc @@ -49,6 +49,15 @@ Supported actions are: * SIP_PEERS: List SIP peers * EXTENSION_STATE: Check extension status +QUEUE_STATUS and SIP_PEERS are answered with a list of events. The producer waits for the whole list and sets +the `org.asteriskjava.manager.ResponseEvents` as the message body: `getResponse()` is the response of Asterisk, +and `getEvents()` the events of the list, such as `QueueParamsEvent` and `QueueMemberEvent` for QUEUE_STATUS or +`PeerEntryEvent` for SIP_PEERS, ending with the complete event (`QueueStatusCompleteEvent` or +`PeerlistCompleteEvent`). To route each event on its own, split on `${body.events}`. + +EXTENSION_STATE sets the `ManagerResponse` (an `ExtensionStateResponse`) as the message body. +When Asterisk answers with an error, the response is a `ManagerError`. + == Examples === Producer Example diff --git a/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskConnection.java b/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskConnection.java index 81a57513e12f..b099aec5d061 100644 --- a/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskConnection.java +++ b/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskConnection.java @@ -19,11 +19,14 @@ package org.apache.camel.component.asterisk; import java.io.IOException; import org.asteriskjava.manager.AuthenticationFailedException; +import org.asteriskjava.manager.EventTimeoutException; import org.asteriskjava.manager.ManagerConnection; import org.asteriskjava.manager.ManagerConnectionFactory; import org.asteriskjava.manager.ManagerConnectionState; import org.asteriskjava.manager.ManagerEventListener; +import org.asteriskjava.manager.ResponseEvents; import org.asteriskjava.manager.TimeoutException; +import org.asteriskjava.manager.action.EventGeneratingAction; import org.asteriskjava.manager.action.ManagerAction; import org.asteriskjava.manager.response.ManagerResponse; import org.slf4j.Logger; @@ -43,6 +46,13 @@ public final class AsteriskConnection { this.password = password; } + AsteriskConnection(ManagerConnection managerConnection) { + this.host = managerConnection.getHostname(); + this.username = managerConnection.getUsername(); + this.password = managerConnection.getPassword(); + this.managerConnection = managerConnection; + } + public void connect() { if (managerConnection == null) { LOG.debug("asterisk connection attempt to {} username: {}", host, username); @@ -104,4 +114,10 @@ public final class AsteriskConnection { return managerConnection.sendAction(action); } + + public ResponseEvents sendEventGeneratingAction(EventGeneratingAction action) + throws IllegalArgumentException, IllegalStateException, IOException, EventTimeoutException { + + return managerConnection.sendEventGeneratingAction(action); + } } diff --git a/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskEndpoint.java b/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskEndpoint.java index 88d35537b63d..97740ea38bd1 100644 --- a/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskEndpoint.java +++ b/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskEndpoint.java @@ -47,10 +47,21 @@ import org.apache.camel.util.ObjectHelper; + " CamelAsteriskAction header, and the extension and context of" + " EXTENSION_STATE from the CamelAsteriskExtension and" + " CamelAsteriskContext headers"), - producerOut = @BodyType(value = Carrier.POJO, javaType = "org.asteriskjava.manager.response.ManagerResponse", - description = "The ManagerResponse of the action, such as an ExtensionStateResponse; a" - + " ManagerError when Asterisk answers with an error. QUEUE_STATUS and" - + " SIP_PEERS give only the first response, not the events of the list")) + producerOut = @BodyType(value = Carrier.POJO, + byOption = { + @BodyType.When(option = "action", value = "QUEUE_STATUS", carriers = Carrier.POJO, + javaType = "org.asteriskjava.manager.ResponseEvents"), + @BodyType.When(option = "action", value = "SIP_PEERS", carriers = Carrier.POJO, + javaType = "org.asteriskjava.manager.ResponseEvents"), + @BodyType.When(option = "action", value = "EXTENSION_STATE", + carriers = Carrier.POJO, + javaType = "org.asteriskjava.manager.response.ManagerResponse") }, + description = "QUEUE_STATUS and SIP_PEERS give the ResponseEvents of asterisk-java: the" + + " ManagerResponse in getResponse() and the events of the list in" + + " getEvents(), such as QueueParamsEvent and QueueMemberEvent or" + + " PeerEntryEvent, ending with the complete event. EXTENSION_STATE gives" + + " the ManagerResponse, an ExtensionStateResponse. A ManagerError is the" + + " response when Asterisk answers with an error")) public class AsteriskEndpoint extends DefaultEndpoint implements EndpointServiceLocation { @UriPath(description = "Name of component") @Metadata(required = true) diff --git a/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskProducer.java b/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskProducer.java index 0e85e34a3ba2..db1b1e5c7215 100644 --- a/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskProducer.java +++ b/components/camel-asterisk/src/main/java/org/apache/camel/component/asterisk/AsteriskProducer.java @@ -19,6 +19,8 @@ package org.apache.camel.component.asterisk; import org.apache.camel.Exchange; import org.apache.camel.support.DefaultProducer; import org.apache.camel.util.ObjectHelper; +import org.asteriskjava.manager.ResponseEvents; +import org.asteriskjava.manager.action.EventGeneratingAction; import org.asteriskjava.manager.action.ManagerAction; import org.asteriskjava.manager.response.ManagerResponse; import org.slf4j.Logger; @@ -35,10 +37,14 @@ public class AsteriskProducer extends DefaultProducer { private final AsteriskConnection connection; public AsteriskProducer(AsteriskEndpoint endpoint) { + this(endpoint, new AsteriskConnection(endpoint.getHostname(), endpoint.getUsername(), endpoint.getPassword())); + } + + AsteriskProducer(AsteriskEndpoint endpoint, AsteriskConnection connection) { super(endpoint); this.endpoint = endpoint; - this.connection = new AsteriskConnection(endpoint.getHostname(), endpoint.getUsername(), endpoint.getPassword()); + this.connection = connection; } @Override @@ -70,9 +76,15 @@ public class AsteriskProducer extends DefaultProducer { LOG.debug("Send action {}", action); ManagerAction managerAction = action.apply(exchange); - ManagerResponse managerResponse = connection.sendAction(managerAction); - - exchange.getIn().setBody(managerResponse); + if (managerAction instanceof EventGeneratingAction eventGeneratingAction) { + // such as QUEUE_STATUS and SIP_PEERS: the response only says that the + // list will follow, the data comes as events until the complete event + ResponseEvents responseEvents = connection.sendEventGeneratingAction(eventGeneratingAction); + exchange.getIn().setBody(responseEvents); + } else { + ManagerResponse managerResponse = connection.sendAction(managerAction); + exchange.getIn().setBody(managerResponse); + } } } diff --git a/components/camel-asterisk/src/test/java/org/apache/camel/component/asterisk/AsteriskProducerFakeServerTest.java b/components/camel-asterisk/src/test/java/org/apache/camel/component/asterisk/AsteriskProducerFakeServerTest.java new file mode 100644 index 000000000000..a73478d8a5ec --- /dev/null +++ b/components/camel-asterisk/src/test/java/org/apache/camel/component/asterisk/AsteriskProducerFakeServerTest.java @@ -0,0 +1,251 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.apache.camel.component.asterisk; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.net.InetAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.nio.charset.StandardCharsets; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.camel.Exchange; +import org.apache.camel.test.junit6.CamelTestSupport; +import org.asteriskjava.manager.ResponseEvents; +import org.asteriskjava.manager.event.PeerEntryEvent; +import org.asteriskjava.manager.event.PeerlistCompleteEvent; +import org.asteriskjava.manager.event.QueueMemberEvent; +import org.asteriskjava.manager.event.QueueParamsEvent; +import org.asteriskjava.manager.event.QueueStatusCompleteEvent; +import org.asteriskjava.manager.event.ResponseEvent; +import org.asteriskjava.manager.internal.ManagerConnectionImpl; +import org.asteriskjava.manager.response.ExtensionStateResponse; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertInstanceOf; + +/** + * Runs the producer against a fake Asterisk Manager Interface (AMI) server on a random port. + */ +public class AsteriskProducerFakeServerTest extends CamelTestSupport { + + private FakeAmiServer server; + private AsteriskProducer producer; + + @BeforeEach + void startFakeServer() throws Exception { + server = new FakeAmiServer(); + server.start(); + + AsteriskEndpoint endpoint = context.getEndpoint( + "asterisk:fake?hostname=localhost&username=admin&password=secret&action=EXTENSION_STATE", + AsteriskEndpoint.class); + + ManagerConnectionImpl managerConnection = new ManagerConnectionImpl(); + managerConnection.setHostname(InetAddress.getLoopbackAddress().getHostAddress()); + managerConnection.setPort(server.getPort()); + managerConnection.setUsername("admin"); + managerConnection.setPassword("secret"); + + producer = new AsteriskProducer(endpoint, new AsteriskConnection(managerConnection)); + producer.start(); + } + + @AfterEach + void stopFakeServer() throws Exception { + if (producer != null) { + producer.stop(); + } + if (server != null) { + server.close(); + } + } + + @Test + void testQueueStatusReturnsTheEvents() throws Exception { + Exchange exchange = send(AsteriskAction.QUEUE_STATUS); + + ResponseEvents responseEvents = assertInstanceOf(ResponseEvents.class, exchange.getMessage().getBody()); + assertEquals("Success", responseEvents.getResponse().getResponse()); + + List<ResponseEvent> events = new ArrayList<>(responseEvents.getEvents()); + assertEquals(3, events.size()); + QueueParamsEvent params = assertInstanceOf(QueueParamsEvent.class, events.get(0)); + assertEquals("support", params.getQueue()); + assertEquals(2, params.getCalls()); + QueueMemberEvent member = assertInstanceOf(QueueMemberEvent.class, events.get(1)); + assertEquals("SIP/1001", member.getLocation()); + assertInstanceOf(QueueStatusCompleteEvent.class, events.get(2)); + } + + @Test + void testSipPeersReturnsTheEvents() throws Exception { + Exchange exchange = send(AsteriskAction.SIP_PEERS); + + ResponseEvents responseEvents = assertInstanceOf(ResponseEvents.class, exchange.getMessage().getBody()); + assertEquals("Success", responseEvents.getResponse().getResponse()); + + List<ResponseEvent> events = new ArrayList<>(responseEvents.getEvents()); + assertEquals(3, events.size()); + assertEquals("1001", assertInstanceOf(PeerEntryEvent.class, events.get(0)).getObjectName()); + assertEquals("1002", assertInstanceOf(PeerEntryEvent.class, events.get(1)).getObjectName()); + assertInstanceOf(PeerlistCompleteEvent.class, events.get(2)); + } + + @Test + void testExtensionStateReturnsTheResponse() throws Exception { + Exchange exchange = producer.getEndpoint().createExchange(); + exchange.getMessage().setHeader(AsteriskConstants.EXTENSION, "1001"); + exchange.getMessage().setHeader(AsteriskConstants.CONTEXT, "default"); + producer.process(exchange); + + ExtensionStateResponse response + = assertInstanceOf(ExtensionStateResponse.class, exchange.getMessage().getBody()); + assertEquals("1001", response.getExten()); + assertEquals(0, response.getStatus()); + } + + private Exchange send(AsteriskAction action) throws Exception { + Exchange exchange = producer.getEndpoint().createExchange(); + exchange.getMessage().setHeader(AsteriskConstants.ACTION, action); + producer.process(exchange); + return exchange; + } + + /** + * Answers the AMI actions of asterisk-java: the login, the version lookup and the actions of the producer. + */ + private static final class FakeAmiServer implements AutoCloseable { + + private final ServerSocket serverSocket; + private final List<Socket> sockets = new ArrayList<>(); + + FakeAmiServer() throws IOException { + serverSocket = new ServerSocket(0, 50, InetAddress.getLoopbackAddress()); + } + + int getPort() { + return serverSocket.getLocalPort(); + } + + void start() { + Thread thread = new Thread(this::acceptLoop, "FakeAmiServer"); + thread.setDaemon(true); + thread.start(); + } + + private void acceptLoop() { + while (!serverSocket.isClosed()) { + try { + Socket socket = serverSocket.accept(); + synchronized (sockets) { + sockets.add(socket); + } + Thread thread = new Thread(() -> serve(socket), "FakeAmiServer-connection"); + thread.setDaemon(true); + thread.start(); + } catch (IOException e) { + // closed + return; + } + } + } + + private void serve(Socket socket) { + try (socket; + BufferedReader reader + = new BufferedReader(new InputStreamReader(socket.getInputStream(), StandardCharsets.UTF_8))) { + OutputStream out = socket.getOutputStream(); + write(out, "Asterisk Call Manager/5.0.1\r\n"); + + Map<String, String> packet = new HashMap<>(); + String line; + while ((line = reader.readLine()) != null) { + if (!line.isEmpty()) { + int pos = line.indexOf(':'); + if (pos > 0) { + packet.put(line.substring(0, pos).trim().toLowerCase(), line.substring(pos + 1).trim()); + } + continue; + } + if (!packet.isEmpty()) { + write(out, answer(packet.get("action"), packet.get("actionid"))); + packet.clear(); + } + } + } catch (IOException e) { + // the client logged off or the server is closed + } + } + + private static String answer(String action, String actionId) { + String id = "ActionID: " + actionId + "\r\n"; + switch (action == null ? "" : action.toLowerCase()) { + case "challenge": + return "Response: Success\r\n" + id + "Challenge: 123456789\r\n\r\n"; + case "login": + return "Response: Success\r\n" + id + "Message: Authentication accepted\r\n\r\n"; + case "coresettings": + return "Response: Success\r\n" + id + "AMIversion: 5.0.1\r\nAsteriskVersion: 18.20.0\r\n\r\n"; + case "queuestatus": + return "Response: Success\r\n" + id + "EventList: start\r\nMessage: Queue status will follow\r\n\r\n" + + "Event: QueueParams\r\nQueue: support\r\nMax: 0\r\nStrategy: ringall\r\nCalls: 2\r\n" + id + + "\r\n" + + "Event: QueueMember\r\nQueue: support\r\nName: Alice\r\nLocation: SIP/1001\r\n" + + "StateInterface: SIP/1001\r\nMembership: static\r\nPenalty: 0\r\nCallsTaken: 0\r\n" + + "Status: 1\r\nPaused: 0\r\n" + id + "\r\n" + + "Event: QueueStatusComplete\r\n" + id + "EventList: Complete\r\nListItems: 2\r\n\r\n"; + case "sippeers": + return "Response: Success\r\n" + id + "EventList: start\r\nMessage: Peer status list will follow\r\n\r\n" + + "Event: PeerEntry\r\n" + id + "Channeltype: SIP\r\nObjectName: 1001\r\nStatus: OK (5 ms)\r\n\r\n" + + "Event: PeerEntry\r\n" + id + "Channeltype: SIP\r\nObjectName: 1002\r\nStatus: UNKNOWN\r\n\r\n" + + "Event: PeerlistComplete\r\n" + id + "EventList: Complete\r\nListItems: 2\r\n\r\n"; + case "extensionstate": + return "Response: Success\r\n" + id + "Message: Extension Status\r\nExten: 1001\r\nContext: default\r\n" + + "Hint: SIP/1001\r\nStatus: 0\r\n\r\n"; + case "logoff": + return "Response: Goodbye\r\n" + id + "Message: Thanks for all the fish.\r\n\r\n"; + default: + return "Response: Success\r\n" + id + "\r\n"; + } + } + + private static void write(OutputStream out, String text) throws IOException { + out.write(text.getBytes(StandardCharsets.UTF_8)); + out.flush(); + } + + @Override + public void close() throws IOException { + serverSocket.close(); + synchronized (sockets) { + for (Socket socket : sockets) { + socket.close(); + } + } + } + } +} diff --git a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc index 10f5a47c1b89..5a5fcb003cb9 100644 --- a/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc +++ b/docs/user-manual/modules/ROOT/pages/camel-4x-upgrade-guide-4_24.adoc @@ -903,6 +903,14 @@ explicitly (for example `partner.example:8443`). A deployment that lists a bare host and receives asynchronous MDNs on a non-default port must add the port to the entry. +=== camel-asterisk - QUEUE_STATUS and SIP_PEERS return the events of the list + +The `QUEUE_STATUS` and `SIP_PEERS` actions of the `asterisk` producer now wait for the whole list and set the +`org.asteriskjava.manager.ResponseEvents` as the body: `getResponse()` is the response and `getEvents()` the +events, such as `QueueParamsEvent`, `QueueMemberEvent` and `PeerEntryEvent`. Before, the body was only the +`ManagerResponse` saying that the list will follow, and the events were lost. A route that read that response from +the body now finds it in `${body.response}`. `EXTENSION_STATE` still sets the `ManagerResponse` as the body. + === camel-jbang - camel validate and camel overview report the same endpoints no route consumes `camel validate source` and `camel overview` now use the same rules for a `direct:`, `seda:` or `disruptor:` endpoint
