[Asterisk-java-users] Sending actions from event handling thread (was: Problem with ConnectEvent)
Brought to you by:
srt
From: Stefan R. <sr...@re...> - 2005-12-19 14:16:00
|
Brett, thanks for publishing your solution, i am sure it will be of help for others with similar requirements. I am currently thinking about the options to fix this for Asterisk-Java 0.3. What I thought about is: a) using one connection only for event receiving and using a pool of connections for sending actions b) separating the reader and the event handling, i.e. there would be one reader threat that places asterisks output into a buffer and a threadpool that dispatches the events to the listeners. There are some issues with both option that I would like to discuss. Option a) has the problem that multiple connections are needed. Thats probably fine if you run one central instance to control asterisk but calls for trouble when using Asterisk-Java on the desktop. Option b) is better in this regard but no longer guarantees that events are delivered in the order they are received. This could be adressed by adding a sequence to the events but places additional burdon on the developer. What do you think? Are there other options? What would you prefer? =3DStefan On Sun, 2005-12-18 at 22:37 +1100, Brett Sutton wrote: > Here's a technique I use to get arround the problem of trying to send > an Action when you are in an event handler. > If you don't know; its simply not possible. >=20 > The call to 'new RecieveFax(event)' creates its own connection to the > API Manager. >=20 > I do wonder whether it would be better if the MangerEventHandler > method was called in a seperate thread from that which the > ManagerReader runs in however that may have other undesirable side > affects.=20 > Alternatively the call to SendAction should probably throw an > IllegalStateException rather than just timing out. As it stand its > hard to diagnose why the call to SendAction fails. >=20 > Basically It's necessary to send the subsequent action in a different > thread from the the eventHandler otherwise the ManagerReader locks up > and nothing works. The subsequent actionn should also be sent using a > separate instance of the ManagerConnection. >=20 > You could potentionally send the subsequent action in the event > handler thread (using a separate ManagerConnection) but you will block > the ManagerReader from notifying your application of other events and > I'm not certain, but I suspect that there is little to be gained from > holding the ManagerReader up whilst you do other work as I doubt that > Asterisk is waiting on the ManagerReader and as such you really arn't > doing the operation synchronously (with respect to Asterisk). >=20 > So here is a sample class demonstating one possible solution. >=20 > I hope this of some use. >=20 > Regards, > Brett. >=20 > import java.io.IOException; > import java.util.TreeSet; > import java.util.Vector; > import java.util.concurrent.LinkedBlockingQueue; >=20 > import net.sf.asterisk.manager.AuthenticationFailedException; > import net.sf.asterisk.manager.ManagerConnection; > import net.sf.asterisk.manager.ManagerConnectionFactory; > import net.sf.asterisk.manager.ManagerEventHandler; > import net.sf.asterisk.manager.TimeoutException; > import net.sf.asterisk.manager.action.StatusAction; > import net.sf.asterisk.manager.event.ManagerEvent; > import net.sf.asterisk.manager.event.NewChannelEvent; > import net.sf.asterisk.manager.event.NewStateEvent; >=20 > import org.apache.commons.configuration.ConfigurationException; > import org.apache.log4j.Logger; >=20 >=20 > public class FaxManagerInbound extends Thread implements > ManagerEventHandler > { > static Logger logger =3D > Logger.getLogger(FaxManagerInbound.class.getName()); >=20 > private static FaxManagerInbound self; >=20 > private boolean running; >=20 > private ManagerConnection managerConnection; >=20 > private Vector<Channel> channels; >=20 > private static final LinkedBlockingQueue<NewChannelEvent> queue =3D > new LinkedBlockingQueue<NewChannelEvent>(); >=20 > static public FaxManagerInbound getInstance() > throws ConfigurationException, > IOException, > AuthenticationFailedException, > TimeoutException > { > if (self =3D=3D null) > { > self =3D new FaxManagerInbound(Configuration.getInstance() > .getChannels()); > self.start(); > } > return self; > } >=20 > private FaxManagerInbound(TreeSet<Channel> channels) > throws IOException, > AuthenticationFailedException, > TimeoutException > { > super("FaxManager Inbound"); > managerConnection =3D connect(); >=20 > // Get the list of channels we are interested in > for (Channel channel : channels) > { > if (channel.type =3D=3D ChannelManager.ChannelType.Inbound > || channel.type =3D=3D > ChannelManager.ChannelType.Both) > { > this.channels.add(channel); > } > } > } >=20 > public void run() > { > running =3D true; > logger.info("Inbound FaxManager started"); > while (running) > { > try > { > if (!managerConnection.isConnected()) > { > logger > .info("Inbound Manager API Connection > lost; reconnecting."); > managerConnection =3D connect(); > logger.info("Inbound Manager API: reconnected"); > } >=20 > NewChannelEvent event =3D queue.take(); > new RecieveFax(event); >=20 > } > catch (Throwable e) > { > // We don't like the manager shuting down just becuase > of a > // problem with a single fax. > logger.error(e.getMessage(), e); > } > } > logger.info("Inbound FaxManager Stopping"); > } >=20 > public void shutdown() > { > running =3D false; > queue.notify(); > notify(); > } >=20 > ManagerConnection connect() > throws IOException, > AuthenticationFailedException, > TimeoutException > { > ManagerConnection connection =3D null; > ManagerConnectionFactory factory =3D new > ManagerConnectionFactory(); >=20 > connection =3D factory.getManagerConnection(Configuration > .getAsteriskHost(), > Configuration.getAsteriskUsername(), > Configuration.getAsteriskPassword()); >=20 > // register for events > connection.addEventHandler(this); >=20 > // connect to Asterisk and log in > managerConnection.login(); >=20 > // request channel state > managerConnection.sendAction(new StatusAction()); >=20 > return connection; > } >=20 > public void handleEvent(ManagerEvent managerEvent) > { > if (managerEvent instanceof NewChannelEvent) > { > NewChannelEvent event =3D (NewChannelEvent) managerEvent; > System.out.println(event.getState()); > if (event.getState().compareToIgnoreCase("Ringing") =3D=3D 0 > || (event.getState().compareToIgnoreCase("Ring") > =3D=3D 0)) > { > queue.add(event); > } >=20 > } > else if (managerEvent instanceof NewStateEvent) > { > NewStateEvent event =3D (NewStateEvent) managerEvent; > System.out.println("State:" + event.getState()); > System.out.println(event); >=20 > } > else >=20 > // just print received events > System.out.println("Event: " + managerEvent); > } >=20 > } >=20 |