View Javadoc

1   /*
2    *  Copyright 2004-2006 Stefan Reuter
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   */
17  package org.asteriskjava.live.internal;
18  
19  import java.util.ArrayList;
20  import java.util.Collection;
21  import java.util.HashMap;
22  import java.util.Iterator;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.concurrent.atomic.AtomicLong;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  import org.asteriskjava.live.AsteriskChannel;
30  import org.asteriskjava.live.AsteriskQueue;
31  import org.asteriskjava.live.AsteriskServer;
32  import org.asteriskjava.live.AsteriskServerListener;
33  import org.asteriskjava.live.CallerId;
34  import org.asteriskjava.live.ChannelState;
35  import org.asteriskjava.live.LiveException;
36  import org.asteriskjava.live.ManagerCommunicationException;
37  import org.asteriskjava.live.MeetMeRoom;
38  import org.asteriskjava.live.MeetMeUser;
39  import org.asteriskjava.live.NoSuchChannelException;
40  import org.asteriskjava.live.OriginateCallback;
41  import org.asteriskjava.live.Voicemailbox;
42  import org.asteriskjava.manager.ManagerConnection;
43  import org.asteriskjava.manager.ManagerConnectionState;
44  import org.asteriskjava.manager.ManagerEventListener;
45  import org.asteriskjava.manager.ManagerEventListenerProxy;
46  import org.asteriskjava.manager.ResponseEvents;
47  import org.asteriskjava.manager.action.CommandAction;
48  import org.asteriskjava.manager.action.EventGeneratingAction;
49  import org.asteriskjava.manager.action.GetVarAction;
50  import org.asteriskjava.manager.action.MailboxCountAction;
51  import org.asteriskjava.manager.action.ManagerAction;
52  import org.asteriskjava.manager.action.OriginateAction;
53  import org.asteriskjava.manager.action.SetVarAction;
54  import org.asteriskjava.manager.event.AbstractMeetMeEvent;
55  import org.asteriskjava.manager.event.OriginateResponseEvent;
56  import org.asteriskjava.manager.event.CdrEvent;
57  import org.asteriskjava.manager.event.ConnectEvent;
58  import org.asteriskjava.manager.event.DialEvent;
59  import org.asteriskjava.manager.event.DisconnectEvent;
60  import org.asteriskjava.manager.event.HangupEvent;
61  import org.asteriskjava.manager.event.JoinEvent;
62  import org.asteriskjava.manager.event.LeaveEvent;
63  import org.asteriskjava.manager.event.LinkEvent;
64  import org.asteriskjava.manager.event.ManagerEvent;
65  import org.asteriskjava.manager.event.NewCallerIdEvent;
66  import org.asteriskjava.manager.event.NewChannelEvent;
67  import org.asteriskjava.manager.event.NewExtenEvent;
68  import org.asteriskjava.manager.event.NewStateEvent;
69  import org.asteriskjava.manager.event.RenameEvent;
70  import org.asteriskjava.manager.event.ResponseEvent;
71  import org.asteriskjava.manager.event.UnlinkEvent;
72  import org.asteriskjava.manager.response.CommandResponse;
73  import org.asteriskjava.manager.response.MailboxCountResponse;
74  import org.asteriskjava.manager.response.ManagerError;
75  import org.asteriskjava.manager.response.ManagerResponse;
76  import org.asteriskjava.util.DateUtil;
77  import org.asteriskjava.util.Log;
78  import org.asteriskjava.util.LogFactory;
79  
80  /***
81   * Default implementation of the {@link AsteriskServer} interface.
82   * 
83   * @author srt
84   * @version $Id: AsteriskServerImpl.java 877 2007-07-31 15:47:01Z srt $
85   */
86  public class AsteriskServerImpl implements AsteriskServer, ManagerEventListener
87  {
88      private static final String ACTION_ID_PREFIX_ORIGINATE = "AJ_ORIGINATE_";
89      private static final String SHOW_VERSION_COMMAND = "show version";
90      private static final String SHOW_VERSION_FILES_COMMAND = "show version files";
91      private static final Pattern SHOW_VERSION_FILES_PATTERN = Pattern.compile("^([//S]+)//s+Revision: ([0-9//.]+)");
92      private static final String SHOW_VOICEMAIL_USERS_COMMAND = "show voicemail users";
93      private static final Pattern SHOW_VOICEMAIL_USERS_PATTERN = Pattern.compile("^(//S+)//s+(//S+)//s+(.{25})");
94  
95      private final Log logger = LogFactory.getLog(this.getClass());
96  
97      /***
98       * The underlying manager connection used to receive events from Asterisk.
99       */
100     private ManagerConnection eventConnection;
101     private ManagerEventListener eventListener = null;
102     private ManagerEventListenerProxy managerEventListenerProxy = null;
103 
104     private boolean initialized = false;
105 
106     /***
107      * A pool of manager connections to use for sending actions to Asterisk.
108      */
109     private final ManagerConnectionPool connectionPool;
110 
111     private final List<AsteriskServerListener> listeners;
112 
113     private final ChannelManager channelManager;
114     private final MeetMeManager meetMeManager;
115     private final QueueManager queueManager;
116 
117     /***
118      * The exact version string of the Asterisk server we are connected to.
119      * <p>
120      * Contains <code>null</code> until lazily initialized.
121      */
122     private String version;
123 
124     /***
125      * Holds the version of Asterisk's source files.
126      * <p>
127      * That corresponds to the output of the CLI command
128      * <code>show version files</code>.
129      * <p>
130      * Contains <code>null</code> until lazily initialized.
131      */
132     private Map<String, String> versions;
133 
134     /***
135      * Maps the traceId to the corresponding callback data.
136      */
137     private final Map<String, OriginateCallbackData> originateCallbacks;
138 
139     private final AtomicLong idCounter;
140 
141     /* config options */
142 
143     /***
144      * Flag to skip initializing queues as that results in a timeout on Asterisk
145      * 1.0.x.
146      */
147     private boolean skipQueues;
148 
149     /***
150      * Set to <code>true</code> to not handle ManagerEvents in the reader
151      * tread but process them asynchronously. This is a good idea :)
152      */
153     private boolean asyncEventHandling = true;
154 
155     /***
156      * Creates a new instance.
157      */
158     public AsteriskServerImpl()
159     {
160         connectionPool = new ManagerConnectionPool(1);
161         idCounter = new AtomicLong();
162         listeners = new ArrayList<AsteriskServerListener>();
163         originateCallbacks = new HashMap<String, OriginateCallbackData>();
164         channelManager = new ChannelManager(this);
165         meetMeManager = new MeetMeManager(this, channelManager);
166         queueManager = new QueueManager(this, channelManager);
167     }
168 
169     /***
170      * Creates a new instance.
171      * 
172      * @param eventConnection the ManagerConnection to use for receiving events
173      *            from Asterisk.
174      */
175     public AsteriskServerImpl(ManagerConnection eventConnection)
176     {
177         this();
178         setManagerConnection(eventConnection);
179     }
180 
181     /***
182      * Determines if queue status is retrieved at startup. If you don't need
183      * queue information and still run Asterisk 1.0.x you can set this to
184      * <code>true</code> to circumvent the startup delay caused by the missing
185      * QueueStatusComplete event.
186      * <p>
187      * Default is <code>false</code>.
188      * 
189      * @param skipQueues <code>true</code> to skip queue initialization,
190      *            <code>false</code> to not skip.
191      * @since 0.2
192      */
193     public void setSkipQueues(boolean skipQueues)
194     {
195         this.skipQueues = skipQueues;
196     }
197 
198     public void setManagerConnection(ManagerConnection eventConnection)
199     {
200         if (this.eventConnection != null)
201         {
202             throw new IllegalStateException("ManagerConnection already set.");
203         }
204 
205         this.eventConnection = eventConnection;
206         this.connectionPool.clear();
207         this.connectionPool.add(eventConnection);
208     }
209 
210     public ManagerConnection getManagerConnection()
211     {
212         return eventConnection;
213     }
214 
215     public void initialize() throws ManagerCommunicationException
216     {
217         initializeIfNeeded();
218     }
219 
220     private synchronized void initializeIfNeeded() throws ManagerCommunicationException
221     {
222         if (initialized)
223         {
224             return;
225         }
226 
227         if (eventConnection.getState() == ManagerConnectionState.INITIAL
228                 || eventConnection.getState() == ManagerConnectionState.DISCONNECTED)
229         {
230             try
231             {
232                 eventConnection.login();
233             }
234             catch (Exception e)
235             {
236                 throw new ManagerCommunicationException("Unable to login", e);
237             }
238         }
239 
240         channelManager.initialize();
241         meetMeManager.initialize();
242         if (!skipQueues)
243         {
244             queueManager.initialize();
245         }
246 
247         if (asyncEventHandling && managerEventListenerProxy == null)
248         {
249             managerEventListenerProxy = new ManagerEventListenerProxy(this);
250             eventConnection.addEventListener(managerEventListenerProxy);
251         } 
252         else if (!asyncEventHandling && eventListener == null)
253         {
254             eventListener = this;
255             eventConnection.addEventListener(eventListener);
256         }
257         logger.info("Initializing done");
258         initialized = true;
259     }
260 
261     /* Implementation of the AsteriskServer interface */
262 
263     public AsteriskChannel originateToExtension(String channel, String context, String exten, int priority, long timeout)
264             throws ManagerCommunicationException, NoSuchChannelException
265     {
266         return originateToExtension(channel, context, exten, priority, timeout, null, null);
267     }
268 
269     public AsteriskChannel originateToExtension(String channel, String context, String exten, int priority, long timeout,
270             CallerId callerId, Map<String, String> variables) throws ManagerCommunicationException, NoSuchChannelException
271     {
272         final OriginateAction originateAction;
273 
274         originateAction = new OriginateAction();
275         originateAction.setChannel(channel);
276         originateAction.setContext(context);
277         originateAction.setExten(exten);
278         originateAction.setPriority(priority);
279         originateAction.setTimeout(timeout);
280         if (callerId != null)
281         {
282             originateAction.setCallerId(callerId.toString());
283         }
284         originateAction.setVariables(variables);
285 
286         return originate(originateAction);
287     }
288 
289     public AsteriskChannel originateToApplication(String channel, String application, String data, long timeout)
290             throws ManagerCommunicationException, NoSuchChannelException
291     {
292         return originateToApplication(channel, application, data, timeout, null, null);
293     }
294 
295     public AsteriskChannel originateToApplication(String channel, String application, String data, long timeout,
296             CallerId callerId, Map<String, String> variables) throws ManagerCommunicationException, NoSuchChannelException
297     {
298         final OriginateAction originateAction;
299 
300         originateAction = new OriginateAction();
301         originateAction.setChannel(channel);
302         originateAction.setApplication(application);
303         originateAction.setData(data);
304         originateAction.setTimeout(timeout);
305         if (callerId != null)
306         {
307             originateAction.setCallerId(callerId.toString());
308         }
309         originateAction.setVariables(variables);
310 
311         return originate(originateAction);
312     }
313 
314     private AsteriskChannel originate(OriginateAction originateAction) throws ManagerCommunicationException,
315             NoSuchChannelException
316     {
317         final ResponseEvents responseEvents;
318         final Iterator<ResponseEvent> responseEventIterator;
319         String uniqueId = null;
320         AsteriskChannel channel = null;
321 
322         // must set async to true to receive OriginateEvents.
323         originateAction.setAsync(Boolean.TRUE);
324 
325         initializeIfNeeded();
326 
327         // 2000 ms extra for the OriginateFailureEvent should be fine
328         responseEvents = sendEventGeneratingAction(originateAction, originateAction.getTimeout() + 2000);
329 
330         responseEventIterator = responseEvents.getEvents().iterator();
331         if (responseEventIterator.hasNext())
332         {
333             ResponseEvent responseEvent;
334 
335             responseEvent = responseEventIterator.next();
336             if (responseEvent instanceof OriginateResponseEvent)
337             {
338                 uniqueId = ((OriginateResponseEvent) responseEvent).getUniqueId();
339                 logger.debug(responseEvent.getClass().getName() + " received with uniqueId " + uniqueId);
340                 channel = getChannelById(uniqueId);
341             }
342         }
343 
344         if (channel == null)
345         {
346             throw new NoSuchChannelException("Channel '" + originateAction.getChannel() + "' is not available");
347         }
348 
349         return channel;
350     }
351 
352     public void originateToExtensionAsync(String channel, String context, String exten, int priority, long timeout,
353             OriginateCallback cb) throws ManagerCommunicationException
354     {
355         originateToExtensionAsync(channel, context, exten, priority, timeout, null, null, cb);
356     }
357 
358     public void originateToExtensionAsync(String channel, String context, String exten, int priority, long timeout,
359             CallerId callerId, Map<String, String> variables, OriginateCallback cb) throws ManagerCommunicationException
360     {
361         final OriginateAction originateAction;
362 
363         originateAction = new OriginateAction();
364         originateAction.setChannel(channel);
365         originateAction.setContext(context);
366         originateAction.setExten(exten);
367         originateAction.setPriority(priority);
368         originateAction.setTimeout(timeout);
369         if (callerId != null)
370         {
371             originateAction.setCallerId(callerId.toString());
372         }
373         originateAction.setVariables(variables);
374 
375         originateAsync(originateAction, cb);
376     }
377 
378     public void originateToApplicationAsync(String channel, String application, String data, long timeout,
379             OriginateCallback cb) throws ManagerCommunicationException
380     {
381         originateToApplicationAsync(channel, application, data, timeout, null, null, cb);
382     }
383 
384     public void originateToApplicationAsync(String channel, String application, String data, long timeout,
385             CallerId callerId, Map<String, String> variables, OriginateCallback cb) throws ManagerCommunicationException
386     {
387         final OriginateAction originateAction;
388 
389         originateAction = new OriginateAction();
390         originateAction.setChannel(channel);
391         originateAction.setApplication(application);
392         originateAction.setData(data);
393         originateAction.setTimeout(timeout);
394         if (callerId != null)
395         {
396             originateAction.setCallerId(callerId.toString());
397         }
398         originateAction.setVariables(variables);
399 
400         originateAsync(originateAction, cb);
401     }
402 
403     private void originateAsync(OriginateAction originateAction, OriginateCallback cb) throws ManagerCommunicationException
404     {
405         final Map<String, String> variables;
406         final String traceId;
407 
408         traceId = ACTION_ID_PREFIX_ORIGINATE + idCounter.getAndIncrement();
409         if (originateAction.getVariables() == null)
410         {
411             variables = new HashMap<String, String>();
412         }
413         else
414         {
415             variables = new HashMap<String, String>(originateAction.getVariables());
416         }
417         // prefix variable name by "__" to enable variable inheritence across
418         // channels
419         variables.put("__" + Constants.VARIABLE_TRACE_ID, traceId);
420         originateAction.setVariables(variables);
421 
422         // must set async to true to receive OriginateEvents.
423         originateAction.setAsync(Boolean.TRUE);
424         originateAction.setActionId(traceId);
425 
426         if (cb != null)
427         {
428             OriginateCallbackData callbackData;
429 
430             callbackData = new OriginateCallbackData(originateAction, DateUtil.getDate(), cb);
431             // register callback
432             synchronized (originateCallbacks)
433             {
434                 originateCallbacks.put(traceId, callbackData);
435             }
436         }
437 
438         initializeIfNeeded();
439         sendActionOnEventConnection(originateAction);
440     }
441 
442     public Collection<AsteriskChannel> getChannels() throws ManagerCommunicationException
443     {
444         initializeIfNeeded();
445         return channelManager.getChannels();
446     }
447 
448     public AsteriskChannel getChannelByName(String name) throws ManagerCommunicationException
449     {
450         initializeIfNeeded();
451         return channelManager.getChannelImplByName(name);
452     }
453 
454     public AsteriskChannel getChannelById(String id) throws ManagerCommunicationException
455     {
456         initializeIfNeeded();
457         return channelManager.getChannelImplById(id);
458     }
459 
460     public Collection<MeetMeRoom> getMeetMeRooms() throws ManagerCommunicationException
461     {
462         initializeIfNeeded();
463         return meetMeManager.getMeetMeRooms();
464     }
465 
466     public MeetMeRoom getMeetMeRoom(String name) throws ManagerCommunicationException
467     {
468         initializeIfNeeded();
469         return meetMeManager.getOrCreateRoomImpl(name);
470     }
471 
472     public Collection<AsteriskQueue> getQueues() throws ManagerCommunicationException
473     {
474         initializeIfNeeded();
475         return queueManager.getQueues();
476     }
477 
478     public synchronized String getVersion() throws ManagerCommunicationException
479     {
480         final ManagerResponse response;
481 
482         initializeIfNeeded();
483         if (version == null)
484         {
485             response = sendAction(new CommandAction(SHOW_VERSION_COMMAND));
486             if (response instanceof CommandResponse)
487             {
488                 final List result;
489 
490                 result = ((CommandResponse) response).getResult();
491                 if (result.size() > 0)
492                 {
493                     version = (String) result.get(0);
494                 }
495             }
496             else
497             {
498                 logger.error("Response to CommandAction(\"" + SHOW_VERSION_COMMAND + "\") was not a CommandResponse but "
499                         + response);
500             }
501         }
502 
503         return version;
504     }
505 
506     public int[] getVersion(String file) throws ManagerCommunicationException
507     {
508         String fileVersion = null;
509         String[] parts;
510         int[] intParts;
511 
512         initializeIfNeeded();
513         if (versions == null)
514         {
515             Map<String, String> map;
516             ManagerResponse response;
517 
518             map = new HashMap<String, String>();
519             try
520             {
521                 response = sendAction(new CommandAction(SHOW_VERSION_FILES_COMMAND));
522                 if (response instanceof CommandResponse)
523                 {
524                     List<String> result;
525 
526                     result = ((CommandResponse) response).getResult();
527                     for (int i = 2; i < result.size(); i++)
528                     {
529                         String line;
530                         Matcher matcher;
531 
532                         line = (String) result.get(i);
533                         matcher = SHOW_VERSION_FILES_PATTERN.matcher(line);
534                         if (matcher.find())
535                         {
536                             String key = matcher.group(1);
537                             String value = matcher.group(2);
538 
539                             map.put(key, value);
540                         }
541                     }
542 
543                     fileVersion = (String) map.get(file);
544                     versions = map;
545                 }
546                 else
547                 {
548                     logger.error("Response to CommandAction(\"" + SHOW_VERSION_FILES_COMMAND
549                             + "\") was not a CommandResponse but " + response);
550                 }
551             }
552             catch (Exception e)
553             {
554                 logger.warn("Unable to send '" + SHOW_VERSION_FILES_COMMAND + "' command.", e);
555             }
556         }
557         else
558         {
559             synchronized (versions)
560             {
561                 fileVersion = versions.get(file);
562             }
563         }
564 
565         if (fileVersion == null)
566         {
567             return null;
568         }
569 
570         parts = fileVersion.split("//.");
571         intParts = new int[parts.length];
572 
573         for (int i = 0; i < parts.length; i++)
574         {
575             try
576             {
577                 intParts[i] = Integer.parseInt(parts[i]);
578             }
579             catch (NumberFormatException e)
580             {
581                 intParts[i] = 0;
582             }
583         }
584 
585         return intParts;
586     }
587 
588     public String getGlobalVariable(String variable) throws ManagerCommunicationException
589     {
590         ManagerResponse response;
591         String value;
592 
593         initializeIfNeeded();
594         response = sendAction(new GetVarAction(variable));
595         if (response instanceof ManagerError)
596         {
597             return null;
598         }
599         value = response.getAttribute("Value");
600         if (value == null)
601         {
602             value = response.getAttribute(variable); // for Asterisk 1.0.x
603         }
604         return value;
605     }
606 
607     public void setGlobalVariable(String variable, String value) throws ManagerCommunicationException
608     {
609         ManagerResponse response;
610 
611         initializeIfNeeded();
612         response = sendAction(new SetVarAction(variable, value));
613         if (response instanceof ManagerError)
614         {
615             logger.error("Unable to set global variable '" + variable + "' to '" + value + "':" + response.getMessage());
616         }
617     }
618 
619     public Collection<Voicemailbox> getVoicemailboxes() throws ManagerCommunicationException
620     {
621         final Collection<Voicemailbox> voicemailboxes;
622         ManagerResponse response;
623         final List<String> result;
624 
625         initializeIfNeeded();
626         voicemailboxes = new ArrayList<Voicemailbox>();
627         response = sendAction(new CommandAction(SHOW_VOICEMAIL_USERS_COMMAND));
628         if (!(response instanceof CommandResponse))
629         {
630             logger.error("Response to CommandAction(\"" + SHOW_VOICEMAIL_USERS_COMMAND
631                     + "\") was not a CommandResponse but " + response);
632             return voicemailboxes;
633         }
634 
635         result = ((CommandResponse) response).getResult();
636         if (result == null || result.size() < 1)
637         {
638             return voicemailboxes;
639         }
640 
641         // remove headline
642         result.remove(0);
643 
644         for (String line : result)
645         {
646             final Matcher matcher;
647             final Voicemailbox voicemailbox;
648             final String context;
649             final String mailbox;
650             final String user;
651 
652             matcher = SHOW_VOICEMAIL_USERS_PATTERN.matcher(line);
653             if (!matcher.find())
654             {
655                 continue;
656             }
657 
658             context = matcher.group(1);
659             mailbox = matcher.group(2);
660             user = matcher.group(3).trim();
661 
662             voicemailbox = new Voicemailbox(mailbox, context, user);
663             voicemailboxes.add(voicemailbox);
664         }
665 
666         // get message count for each mailbox
667         for (Voicemailbox voicemailbox : voicemailboxes)
668         {
669             final String fullname;
670 
671             fullname = voicemailbox.getMailbox() + "@" + voicemailbox.getContext();
672             response = sendAction(new MailboxCountAction(fullname));
673             if (response instanceof MailboxCountResponse)
674             {
675                 MailboxCountResponse mailboxCountResponse;
676 
677                 mailboxCountResponse = (MailboxCountResponse) response;
678                 voicemailbox.setNewMessages(mailboxCountResponse.getNewMessages());
679                 voicemailbox.setOldMessages(mailboxCountResponse.getOldMessages());
680             }
681             else
682             {
683                 logger.error("Response to MailboxCountAction was not a MailboxCountResponse but " + response);
684             }
685         }
686 
687         return voicemailboxes;
688     }
689 
690     public List<String> executeCliCommand(String command) throws ManagerCommunicationException
691     {
692         final ManagerResponse response;
693 
694         initializeIfNeeded();
695         response = sendAction(new CommandAction(command));
696         if (!(response instanceof CommandResponse))
697         {
698             throw new ManagerCommunicationException("Response to CommandAction(\"" + command
699                     + "\") was not a CommandResponse but " + response, null);
700         }
701 
702         return ((CommandResponse) response).getResult();
703     }
704 
705     public void addAsteriskServerListener(AsteriskServerListener listener) throws ManagerCommunicationException
706     {
707         initializeIfNeeded();
708         synchronized (listeners)
709         {
710             listeners.add(listener);
711         }
712     }
713 
714     public void removeAsteriskServerListener(AsteriskServerListener listener)
715     {
716         synchronized (listeners)
717         {
718             listeners.remove(listener);
719         }
720     }
721 
722     void fireNewAsteriskChannel(AsteriskChannel channel)
723     {
724         synchronized (listeners)
725         {
726             for (AsteriskServerListener listener : listeners)
727             {
728                 try
729                 {
730                     listener.onNewAsteriskChannel(channel);
731                 }
732                 catch (Exception e)
733                 {
734                     logger.warn("Exception in onNewAsteriskChannel()", e);
735                 }
736             }
737         }
738     }
739 
740     void fireNewMeetMeUser(MeetMeUser user)
741     {
742         synchronized (listeners)
743         {
744             for (AsteriskServerListener listener : listeners)
745             {
746                 try
747                 {
748                     listener.onNewMeetMeUser(user);
749                 }
750                 catch (Exception e)
751                 {
752                     logger.warn("Exception in onNewMeetMeUser()", e);
753                 }
754             }
755         }
756     }
757 
758     ManagerResponse sendActionOnEventConnection(ManagerAction action) throws ManagerCommunicationException
759     {
760         try
761         {
762             return eventConnection.sendAction(action);
763         }
764         catch (Exception e)
765         {
766             throw ManagerCommunicationExceptionMapper.mapSendActionException(action.getAction(), e);
767         }
768     }
769 
770     ManagerResponse sendAction(ManagerAction action) throws ManagerCommunicationException
771     {
772         // return connectionPool.sendAction(action);
773         try
774         {
775             return eventConnection.sendAction(action);
776         }
777         catch (Exception e)
778         {
779             throw ManagerCommunicationExceptionMapper.mapSendActionException(action.getAction(), e);
780         }
781     }
782 
783     ResponseEvents sendEventGeneratingAction(EventGeneratingAction action) throws ManagerCommunicationException
784     {
785         // return connectionPool.sendEventGeneratingAction(action);
786         try
787         {
788             return eventConnection.sendEventGeneratingAction(action);
789         }
790         catch (Exception e)
791         {
792             throw ManagerCommunicationExceptionMapper.mapSendActionException(action.getAction(), e);
793         }
794     }
795 
796     ResponseEvents sendEventGeneratingAction(EventGeneratingAction action, long timeout)
797             throws ManagerCommunicationException
798     {
799         // return connectionPool.sendEventGeneratingAction(action, timeout);
800         try
801         {
802             return eventConnection.sendEventGeneratingAction(action, timeout);
803         }
804         catch (Exception e)
805         {
806             throw ManagerCommunicationExceptionMapper.mapSendActionException(action.getAction(), e);
807         }
808     }
809 
810     OriginateCallbackData getOriginateCallbackDataByTraceId(String traceId)
811     {
812         synchronized (originateCallbacks)
813         {
814             return originateCallbacks.get(traceId);
815         }
816     }
817 
818     /* Implementation of the ManagerEventListener interface */
819 
820     /***
821      * Handles all events received from the Asterisk server.
822      * <p>
823      * Events are queued until channels and queues are initialized and then
824      * delegated to the dispatchEvent method.
825      */
826     public void onManagerEvent(ManagerEvent event)
827     {
828         if (event instanceof ConnectEvent)
829         {
830             handleConnectEvent((ConnectEvent) event);
831         }
832         else if (event instanceof DisconnectEvent)
833         {
834             handleDisconnectEvent((DisconnectEvent) event);
835         }
836         else if (event instanceof NewChannelEvent)
837         {
838             channelManager.handleNewChannelEvent((NewChannelEvent) event);
839         }
840         else if (event instanceof NewExtenEvent)
841         {
842             channelManager.handleNewExtenEvent((NewExtenEvent) event);
843         }
844         else if (event instanceof NewStateEvent)
845         {
846             channelManager.handleNewStateEvent((NewStateEvent) event);
847         }
848         else if (event instanceof NewCallerIdEvent)
849         {
850             channelManager.handleNewCallerIdEvent((NewCallerIdEvent) event);
851         }
852         else if (event instanceof DialEvent)
853         {
854             channelManager.handleDialEvent((DialEvent) event);
855         }
856         else if (event instanceof LinkEvent)
857         {
858             channelManager.handleLinkEvent((LinkEvent) event);
859         }
860         else if (event instanceof UnlinkEvent)
861         {
862             channelManager.handleUnlinkEvent((UnlinkEvent) event);
863         }
864         else if (event instanceof RenameEvent)
865         {
866             channelManager.handleRenameEvent((RenameEvent) event);
867         }
868         else if (event instanceof HangupEvent)
869         {
870             channelManager.handleHangupEvent((HangupEvent) event);
871         }
872         else if (event instanceof CdrEvent)
873         {
874             channelManager.handleCdrEvent((CdrEvent) event);
875         }
876         else if (event instanceof JoinEvent)
877         {
878             queueManager.handleJoinEvent((JoinEvent) event);
879         }
880         else if (event instanceof LeaveEvent)
881         {
882             queueManager.handleLeaveEvent((LeaveEvent) event);
883         }
884         else if (event instanceof AbstractMeetMeEvent)
885         {
886             meetMeManager.handleMeetMeEvent((AbstractMeetMeEvent) event);
887         }
888         else if (event instanceof OriginateResponseEvent)
889         {
890             handleOriginateEvent((OriginateResponseEvent) event);
891         }
892     }
893 
894     /*
895      * Resets the internal state when the connection to the asterisk server is
896      * lost.
897      */
898     private void handleDisconnectEvent(DisconnectEvent disconnectEvent)
899     {
900         // reset version information as it might have changed while Asterisk
901         // restarted
902         version = null;
903         versions = null;
904 
905         // same for channels and queues, they are reinitialized when reconnected
906         channelManager.disconnected();
907         meetMeManager.disconnected();
908         queueManager.disconnected();
909         initialized = false;
910     }
911 
912     /*
913      * Requests the current state from the asterisk server after the connection
914      * to the asterisk server is restored.
915      */
916     private void handleConnectEvent(ConnectEvent connectEvent)
917     {
918         try
919         {
920             initialize();
921         }
922         catch (Exception e)
923         {
924             logger.error("Unable to reinitialize state after reconnection", e);
925         }
926     }
927 
928     private void handleOriginateEvent(OriginateResponseEvent originateEvent)
929     {
930         final String traceId;
931         final OriginateCallbackData callbackData;
932         final OriginateCallback cb;
933         final AsteriskChannelImpl channel;
934         final AsteriskChannelImpl otherChannel; // the other side if local
935         // channel
936 
937         traceId = originateEvent.getActionId();
938         if (traceId == null)
939         {
940             return;
941         }
942 
943         synchronized (originateCallbacks)
944         {
945             callbackData = originateCallbacks.get(traceId);
946             if (callbackData == null)
947             {
948                 return;
949             }
950             originateCallbacks.remove(traceId);
951         }
952 
953         cb = callbackData.getCallback();
954         channel = channelManager.getChannelImplById(originateEvent.getUniqueId());
955 
956         try
957         {
958             if (channel == null)
959             {
960                 LiveException cause;
961 
962                 cause = new NoSuchChannelException("Channel '" + callbackData.getOriginateAction().getChannel()
963                         + "' is not available");
964                 cb.onFailure(cause);
965                 return;
966             }
967 
968             if (channel.wasInState(ChannelState.UP))
969             {
970                 cb.onSuccess(channel);
971                 return;
972             }
973 
974             if (channel.wasBusy())
975             {
976                 cb.onBusy(channel);
977                 return;
978             }
979 
980             otherChannel = channelManager.getOtherSideOfLocalChannel(channel);
981             // special treatment of local channels:
982             // the interesting things happen to the other side so we have a look
983             // at that
984             if (otherChannel != null)
985             {
986                 final AsteriskChannel dialedChannel;
987 
988                 dialedChannel = otherChannel.getDialedChannel();
989 
990                 // on busy the other channel is in state busy when we receive
991                 // the originate event
992                 if (otherChannel.wasBusy())
993                 {
994                     cb.onBusy(channel);
995                     return;
996                 }
997 
998                 // alternative:
999                 // on busy the dialed channel is hung up when we receive the
1000                 // originate event having a look at the hangup cause reveals the
1001                 // information we are interested in
1002                 // this alternative has the drawback that there might by
1003                 // multiple channels that have been dialed by the local channel
1004                 // but we only look at the last one.
1005                 if (dialedChannel != null && dialedChannel.wasBusy())
1006                 {
1007                     cb.onBusy(channel);
1008                     return;
1009                 }
1010             }
1011 
1012             // if nothing else matched we asume no answer
1013             cb.onNoAnswer(channel);
1014         }
1015         catch (Throwable t)
1016         {
1017             logger.warn("Exception dispatching originate progress", t);
1018         }
1019     }
1020 
1021     public void shutdown() {
1022         
1023         if (eventConnection != null && eventConnection.getState() == ManagerConnectionState.CONNECTED) 
1024         {
1025             eventConnection.logoff();
1026             eventConnection = null;
1027         }
1028         if (managerEventListenerProxy != null) 
1029         {
1030             managerEventListenerProxy.shutdown();
1031         }
1032         managerEventListenerProxy = null;
1033         eventListener = null;
1034     }
1035 }