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 org.asteriskjava.live.*;
20  import org.asteriskjava.manager.*;
21  import org.asteriskjava.manager.action.*;
22  import org.asteriskjava.manager.event.*;
23  import org.asteriskjava.manager.response.*;
24  import org.asteriskjava.util.DateUtil;
25  import org.asteriskjava.util.Log;
26  import org.asteriskjava.util.LogFactory;
27  import org.asteriskjava.util.AstUtil;
28  import org.asteriskjava.config.ConfigFile;
29  import org.asteriskjava.AsteriskVersion;
30  
31  import java.util.*;
32  import java.util.concurrent.atomic.AtomicLong;
33  import java.util.regex.Matcher;
34  import java.util.regex.Pattern;
35  
36  /**
37   * Default implementation of the {@link AsteriskServer} interface.
38   *
39   * @author srt
40   * @version $Id$
41   */
42  public class AsteriskServerImpl implements AsteriskServer, ManagerEventListener
43  {
44      private static final String ACTION_ID_PREFIX_ORIGINATE = "AJ_ORIGINATE_";
45      private static final String SHOW_VERSION_COMMAND = "show version";
46      private static final String SHOW_VERSION_1_6_COMMAND = "core show version";
47      private static final String SHOW_VERSION_FILES_COMMAND = "show version files";
48      private static final String SHOW_VERSION_FILES_1_6_COMMAND = "core show file version";
49      private static final Pattern SHOW_VERSION_FILES_PATTERN = Pattern.compile("^([\\S]+)\\s+Revision: ([0-9\\.]+)");
50      private static final String SHOW_VOICEMAIL_USERS_COMMAND = "show voicemail users";
51      private static final String SHOW_VOICEMAIL_USERS_1_6_COMMAND = "voicemail show users";
52      private static final Pattern SHOW_VOICEMAIL_USERS_PATTERN = Pattern.compile("^(\\S+)\\s+(\\S+)\\s+(.{25})");
53  
54      private final Log logger = LogFactory.getLog(this.getClass());
55  
56      /**
57       * The underlying manager connection used to receive events from Asterisk.
58       */
59      private ManagerConnection eventConnection;
60      private ManagerEventListener eventListener = null;
61      private ManagerEventListenerProxy managerEventListenerProxy = null;
62  
63      private boolean initialized = false;
64  
65      /**
66       * A pool of manager connections to use for sending actions to Asterisk.
67       */
68      private final ManagerConnectionPool connectionPool;
69  
70      private final List<AsteriskServerListener> listeners;
71  
72      private final ChannelManager channelManager;
73      private final MeetMeManager meetMeManager;
74      private final QueueManager queueManager;
75      private final AgentManager agentManager;
76  
77      /**
78       * The exact version string of the Asterisk server we are connected to.
79       * <p/>
80       * Contains <code>null</code> until lazily initialized.
81       */
82      private String version;
83  
84      /**
85       * Holds the version of Asterisk's source files.
86       * <p/>
87       * That corresponds to the output of the CLI command
88       * <code>show version files</code>.
89       * <p/>
90       * Contains <code>null</code> until lazily initialized.
91       */
92      private Map<String, String> versions;
93  
94      /**
95       * Maps the traceId to the corresponding callback data.
96       */
97      private final Map<String, OriginateCallbackData> originateCallbacks;
98  
99      private final AtomicLong idCounter;
100 
101     /* config options */
102 
103     /**
104      * Flag to skip initializing queues as that results in a timeout on Asterisk
105      * 1.0.x.
106      */
107     private boolean skipQueues;
108 
109     /**
110      * Set to <code>true</code> to not handle ManagerEvents in the reader
111      * tread but process them asynchronously. This is a good idea :)
112      */
113     private boolean asyncEventHandling = true;
114 
115     /**
116      * Creates a new instance.
117      */
118     public AsteriskServerImpl()
119     {
120         connectionPool = new ManagerConnectionPool(1);
121         idCounter = new AtomicLong();
122         listeners = new ArrayList<AsteriskServerListener>();
123         originateCallbacks = new HashMap<String, OriginateCallbackData>();
124         channelManager = new ChannelManager(this);
125         agentManager = new AgentManager(this);
126         meetMeManager = new MeetMeManager(this, channelManager);
127         queueManager = new QueueManager(this, channelManager);
128     }
129 
130     /**
131      * Creates a new instance.
132      *
133      * @param eventConnection the ManagerConnection to use for receiving events
134      *                        from Asterisk.
135      */
136     public AsteriskServerImpl(ManagerConnection eventConnection)
137     {
138         this();
139         setManagerConnection(eventConnection);  //todo: !!! Possible bug !!!: call to overridable method over object construction 
140     }
141 
142     /**
143      * Determines if queue status is retrieved at startup. If you don't need
144      * queue information and still run Asterisk 1.0.x you can set this to
145      * <code>true</code> to circumvent the startup delay caused by the missing
146      * QueueStatusComplete event.
147      * <p/>
148      * Default is <code>false</code>.
149      *
150      * @param skipQueues <code>true</code> to skip queue initialization,
151      *                   <code>false</code> to not skip.
152      * @since 0.2
153      */
154     public void setSkipQueues(boolean skipQueues)
155     {
156         this.skipQueues = skipQueues;
157     }
158 
159     public void setManagerConnection(ManagerConnection eventConnection)
160     {
161         if (this.eventConnection != null)
162         {
163             throw new IllegalStateException("ManagerConnection already set.");
164         }
165 
166         this.eventConnection = eventConnection;
167         this.connectionPool.clear();
168         this.connectionPool.add(eventConnection);
169     }
170 
171     public ManagerConnection getManagerConnection()
172     {
173         return eventConnection;
174     }
175 
176     public void initialize() throws ManagerCommunicationException
177     {
178         initializeIfNeeded();
179     }
180 
181     private synchronized void initializeIfNeeded() throws ManagerCommunicationException
182     {
183         if (initialized)
184         {
185             return;
186         }
187 
188         if (eventConnection.getState() == ManagerConnectionState.INITIAL
189                 || eventConnection.getState() == ManagerConnectionState.DISCONNECTED)
190         {
191             try
192             {
193                 eventConnection.login();
194             }
195             catch (Exception e)
196             {
197                 throw new ManagerCommunicationException("Unable to login: " + e.getMessage(), e);
198             }
199         }
200 
201         channelManager.initialize();
202         agentManager.initialize();
203         meetMeManager.initialize();
204         if (!skipQueues)
205         {
206             queueManager.initialize();
207         }
208 
209         if (asyncEventHandling && managerEventListenerProxy == null)
210         {
211             managerEventListenerProxy = new ManagerEventListenerProxy(this);
212             eventConnection.addEventListener(managerEventListenerProxy);
213         }
214         else if (!asyncEventHandling && eventListener == null)
215         {
216             eventListener = this;
217             eventConnection.addEventListener(eventListener);
218         }
219         logger.info("Initializing done");
220         initialized = true;
221     }
222 
223     /* Implementation of the AsteriskServer interface */
224 
225     public AsteriskChannel originateToExtension(String channel, String context,
226                                                 String exten, int priority, long timeout)
227             throws ManagerCommunicationException, NoSuchChannelException
228     {
229         return originateToExtension(channel, context, exten, priority, timeout, null, null);
230     }
231 
232     public AsteriskChannel originateToExtension(String channel, String context,
233                                                 String exten, int priority, long timeout, CallerId callerId,
234                                                 Map<String, String> variables)
235             throws ManagerCommunicationException, NoSuchChannelException
236     {
237         final OriginateAction originateAction;
238 
239         originateAction = new OriginateAction();
240         originateAction.setChannel(channel);
241         originateAction.setContext(context);
242         originateAction.setExten(exten);
243         originateAction.setPriority(priority);
244         originateAction.setTimeout(timeout);
245         if (callerId != null)
246         {
247             originateAction.setCallerId(callerId.toString());
248         }
249         originateAction.setVariables(variables);
250 
251         return originate(originateAction);
252     }
253 
254     public AsteriskChannel originateToApplication(String channel,
255                                                   String application, String data, long timeout)
256             throws ManagerCommunicationException, NoSuchChannelException
257     {
258         return originateToApplication(channel, application, data, timeout, null, null);
259     }
260 
261     public AsteriskChannel originateToApplication(String channel,
262                                                   String application, String data, long timeout, CallerId callerId,
263                                                   Map<String, String> variables)
264             throws ManagerCommunicationException, NoSuchChannelException
265     {
266         final OriginateAction originateAction;
267 
268         originateAction = new OriginateAction();
269         originateAction.setChannel(channel);
270         originateAction.setApplication(application);
271         originateAction.setData(data);
272         originateAction.setTimeout(timeout);
273         if (callerId != null)
274         {
275             originateAction.setCallerId(callerId.toString());
276         }
277         originateAction.setVariables(variables);
278 
279         return originate(originateAction);
280     }
281 
282     public AsteriskChannel originate(OriginateAction originateAction) throws ManagerCommunicationException, NoSuchChannelException
283     {
284         final ResponseEvents responseEvents;
285         final Iterator<ResponseEvent> responseEventIterator;
286         String uniqueId;
287         AsteriskChannel channel = null;
288 
289         // must set async to true to receive OriginateEvents.
290         originateAction.setAsync(Boolean.TRUE);
291 
292         initializeIfNeeded();
293 
294         // 2000 ms extra for the OriginateFailureEvent should be fine
295         responseEvents = sendEventGeneratingAction(originateAction, originateAction.getTimeout() + 2000);
296 
297         responseEventIterator = responseEvents.getEvents().iterator();
298         if (responseEventIterator.hasNext())
299         {
300             ResponseEvent responseEvent;
301 
302             responseEvent = responseEventIterator.next();
303             if (responseEvent instanceof OriginateResponseEvent)
304             {
305                 uniqueId = ((OriginateResponseEvent) responseEvent).getUniqueId();
306                 logger.debug(responseEvent.getClass().getName() + " received with uniqueId " + uniqueId);
307                 channel = getChannelById(uniqueId);
308             }
309         }
310 
311         if (channel == null)
312         {
313             throw new NoSuchChannelException("Channel '" + originateAction.getChannel() + "' is not available");
314         }
315 
316         return channel;
317     }
318 
319     public void originateToExtensionAsync(String channel, String context,
320                                           String exten, int priority, long timeout, OriginateCallback cb)
321             throws ManagerCommunicationException
322     {
323         originateToExtensionAsync(channel, context, exten, priority, timeout, null, null, cb);
324     }
325 
326     public void originateToExtensionAsync(String channel, String context,
327                                           String exten, int priority, long timeout, CallerId callerId,
328                                           Map<String, String> variables, OriginateCallback cb)
329             throws ManagerCommunicationException
330     {
331         final OriginateAction originateAction;
332 
333         originateAction = new OriginateAction();
334         originateAction.setChannel(channel);
335         originateAction.setContext(context);
336         originateAction.setExten(exten);
337         originateAction.setPriority(priority);
338         originateAction.setTimeout(timeout);
339         if (callerId != null)
340         {
341             originateAction.setCallerId(callerId.toString());
342         }
343         originateAction.setVariables(variables);
344 
345         originateAsync(originateAction, cb);
346     }
347 
348     public void originateToApplicationAsync(String channel, String application,
349                                             String data, long timeout, OriginateCallback cb)
350             throws ManagerCommunicationException
351     {
352         originateToApplicationAsync(channel, application, data, timeout, null, null, cb);
353     }
354 
355     public void originateToApplicationAsync(String channel, String application,
356                                             String data, long timeout, CallerId callerId,
357                                             Map<String, String> variables, OriginateCallback cb)
358             throws ManagerCommunicationException
359     {
360         final OriginateAction originateAction;
361 
362         originateAction = new OriginateAction();
363         originateAction.setChannel(channel);
364         originateAction.setApplication(application);
365         originateAction.setData(data);
366         originateAction.setTimeout(timeout);
367         if (callerId != null)
368         {
369             originateAction.setCallerId(callerId.toString());
370         }
371         originateAction.setVariables(variables);
372 
373         originateAsync(originateAction, cb);
374     }
375 
376     public void originateAsync(OriginateAction originateAction,
377                                OriginateCallback cb) throws ManagerCommunicationException
378     {
379         final Map<String, String> variables;
380         final String traceId;
381 
382         traceId = ACTION_ID_PREFIX_ORIGINATE + idCounter.getAndIncrement();
383         if (originateAction.getVariables() == null)
384         {
385             variables = new HashMap<String, String>();
386         }
387         else
388         {
389             variables = new HashMap<String, String>(originateAction.getVariables());
390         }
391 
392         // prefix variable name by "__" to enable variable inheritence across channels
393         variables.put("__" + Constants.VARIABLE_TRACE_ID, traceId);
394         originateAction.setVariables(variables);
395 
396         // async must be set to true to receive OriginateEvents.
397         originateAction.setAsync(Boolean.TRUE);
398         originateAction.setActionId(traceId);
399 
400         if (cb != null)
401         {
402             final OriginateCallbackData callbackData;
403 
404             callbackData = new OriginateCallbackData(originateAction, DateUtil.getDate(), cb);
405             // register callback
406             synchronized (originateCallbacks)
407             {
408                 originateCallbacks.put(traceId, callbackData);
409             }
410         }
411 
412         initializeIfNeeded();
413         sendActionOnEventConnection(originateAction);
414     }
415 
416     public Collection<AsteriskChannel> getChannels() throws ManagerCommunicationException
417     {
418         initializeIfNeeded();
419         return channelManager.getChannels();
420     }
421 
422     public AsteriskChannel getChannelByName(String name) throws ManagerCommunicationException
423     {
424         initializeIfNeeded();
425         return channelManager.getChannelImplByName(name);
426     }
427 
428     public AsteriskChannel getChannelById(String id) throws ManagerCommunicationException
429     {
430         initializeIfNeeded();
431         return channelManager.getChannelImplById(id);
432     }
433 
434     public Collection<MeetMeRoom> getMeetMeRooms() throws ManagerCommunicationException
435     {
436         initializeIfNeeded();
437         return meetMeManager.getMeetMeRooms();
438     }
439 
440     public MeetMeRoom getMeetMeRoom(String name) throws ManagerCommunicationException
441     {
442         initializeIfNeeded();
443         return meetMeManager.getOrCreateRoomImpl(name);
444     }
445 
446     public Collection<AsteriskQueue> getQueues() throws ManagerCommunicationException
447     {
448         initializeIfNeeded();
449         return queueManager.getQueues();
450     }
451 
452     public synchronized String getVersion() throws ManagerCommunicationException
453     {
454         final ManagerResponse response;
455         final String command;
456 
457         initializeIfNeeded();
458         if (version == null)
459         {
460             if (eventConnection.getVersion().isAtLeast(AsteriskVersion.ASTERISK_1_6))
461             {
462                 command = SHOW_VERSION_1_6_COMMAND;
463             }
464             else
465             {
466                 command = SHOW_VERSION_COMMAND;
467             }
468 
469             response = sendAction(new CommandAction(command));
470             if (response instanceof CommandResponse)
471             {
472                 final List<?> result;
473 
474                 result = ((CommandResponse) response).getResult();
475                 if (result.size() > 0)
476                 {
477                     version = (String) result.get(0);
478                 }
479             }
480             else
481             {
482                 logger.error("Response to CommandAction(\"" + command + "\") was not a CommandResponse but " + response);
483             }
484         }
485 
486         return version;
487     }
488 
489     public int[] getVersion(String file) throws ManagerCommunicationException
490     {
491         String fileVersion = null;
492         String[] parts;
493         int[] intParts;
494 
495         initializeIfNeeded();
496         if (versions == null)
497         {
498             Map<String, String> map;
499             ManagerResponse response;
500 
501             map = new HashMap<String, String>();
502             try
503             {
504                 final String command;
505 
506                 if (eventConnection.getVersion().isAtLeast(AsteriskVersion.ASTERISK_1_6))
507                 {
508                     command = SHOW_VERSION_FILES_1_6_COMMAND;
509                 }
510                 else
511                 {
512                     command = SHOW_VERSION_FILES_COMMAND;
513                 }
514                 response = sendAction(new CommandAction(command));
515                 if (response instanceof CommandResponse)
516                 {
517                     List<String> result;
518 
519                     result = ((CommandResponse) response).getResult();
520                     for (int i = 2; i < result.size(); i++)
521                     {
522                         String line;
523                         Matcher matcher;
524 
525                         line = result.get(i);
526                         matcher = SHOW_VERSION_FILES_PATTERN.matcher(line);
527                         if (matcher.find())
528                         {
529                             String key = matcher.group(1);
530                             String value = matcher.group(2);
531 
532                             map.put(key, value);
533                         }
534                     }
535 
536                     fileVersion = map.get(file);
537                     versions = map;
538                 }
539                 else
540                 {
541                     logger.error("Response to CommandAction(\""
542                             + command + "\") was not a CommandResponse but " + response);
543                 }
544             }
545             catch (Exception e)
546             {
547                 logger.warn("Unable to send '" + SHOW_VERSION_FILES_COMMAND + "' command.", e);
548             }
549         }
550         else
551         {
552             synchronized (versions)
553             {
554                 fileVersion = versions.get(file);
555             }
556         }
557 
558         if (fileVersion == null)
559         {
560             return null;
561         }
562 
563         parts = fileVersion.split("\\.");
564         intParts = new int[parts.length];
565 
566         for (int i = 0; i < parts.length; i++)
567         {
568             try
569             {
570                 intParts[i] = Integer.parseInt(parts[i]);
571             }
572             catch (NumberFormatException e)
573             {
574                 intParts[i] = 0;
575             }
576         }
577 
578         return intParts;
579     }
580 
581     public String getGlobalVariable(String variable) throws ManagerCommunicationException
582     {
583         ManagerResponse response;
584         String value;
585 
586         initializeIfNeeded();
587         response = sendAction(new GetVarAction(variable));
588         if (response instanceof ManagerError)
589         {
590             return null;
591         }
592         value = response.getAttribute("Value");
593         if (value == null)
594         {
595             value = response.getAttribute(variable); // for Asterisk 1.0.x
596         }
597         return value;
598     }
599 
600     public void setGlobalVariable(String variable, String value) throws ManagerCommunicationException
601     {
602         ManagerResponse response;
603 
604         initializeIfNeeded();
605         response = sendAction(new SetVarAction(variable, value));
606         if (response instanceof ManagerError)
607         {
608             logger.error("Unable to set global variable '" + variable
609                     + "' to '" + value + "':" + response.getMessage());
610         }
611     }
612 
613     public Collection<Voicemailbox> getVoicemailboxes() throws ManagerCommunicationException
614     {
615         final Collection<Voicemailbox> voicemailboxes;
616         ManagerResponse response;
617         final List<String> result;
618 
619         initializeIfNeeded();
620         voicemailboxes = new ArrayList<Voicemailbox>();
621         if (eventConnection.getVersion().isAtLeast(AsteriskVersion.ASTERISK_1_6))
622         {
623             response = sendAction(new CommandAction(SHOW_VOICEMAIL_USERS_1_6_COMMAND));
624         }
625         else
626         {
627             response = sendAction(new CommandAction(SHOW_VOICEMAIL_USERS_COMMAND));
628         }
629         if (!(response instanceof CommandResponse))
630         {
631             logger.error("Response to CommandAction(\"" + SHOW_VOICEMAIL_USERS_COMMAND + "\") 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() + "@"
672                     + voicemailbox.getContext();
673             response = sendAction(new MailboxCountAction(fullname));
674             if (response instanceof MailboxCountResponse)
675             {
676                 MailboxCountResponse mailboxCountResponse;
677 
678                 mailboxCountResponse = (MailboxCountResponse) response;
679                 voicemailbox.setNewMessages(mailboxCountResponse.getNewMessages());
680                 voicemailbox.setOldMessages(mailboxCountResponse.getOldMessages());
681             }
682             else
683             {
684                 logger.error("Response to MailboxCountAction was not a MailboxCountResponse but " + response);
685             }
686         }
687 
688         return voicemailboxes;
689     }
690 
691     public List<String> executeCliCommand(String command) throws ManagerCommunicationException
692     {
693         final ManagerResponse response;
694 
695         initializeIfNeeded();
696         response = sendAction(new CommandAction(command));
697         if (!(response instanceof CommandResponse))
698         {
699             throw new ManagerCommunicationException("Response to CommandAction(\"" + command
700                     + "\") was not a CommandResponse but " + response, null);
701         }
702 
703         return ((CommandResponse) response).getResult();
704     }
705 
706     public boolean isModuleLoaded(String module) throws ManagerCommunicationException
707     {
708         return sendAction(new ModuleCheckAction(module)) instanceof ModuleCheckResponse;
709     }
710 
711     public void loadModule(String module) throws ManagerCommunicationException
712     {
713         sendModuleLoadAction(module, ModuleLoadAction.LOAD_TYPE_LOAD);
714     }
715 
716     public void unloadModule(String module) throws ManagerCommunicationException
717     {
718         sendModuleLoadAction(module, ModuleLoadAction.LOAD_TYPE_UNLOAD);
719     }
720 
721     public void reloadModule(String module) throws ManagerCommunicationException
722     {
723         sendModuleLoadAction(module, ModuleLoadAction.LOAD_TYPE_RELOAD);
724     }
725 
726     public void reloadAllModules() throws ManagerCommunicationException
727     {
728         sendModuleLoadAction(null, ModuleLoadAction.LOAD_TYPE_RELOAD);
729     }
730 
731     protected void sendModuleLoadAction(String module, String loadType) throws ManagerCommunicationException
732     {
733         final ManagerResponse response;
734 
735         response = sendAction(new ModuleLoadAction(module, loadType));
736         if (response instanceof ManagerError)
737         {
738             final ManagerError error = (ManagerError) response;
739             throw new ManagerCommunicationException(error.getMessage(), null);
740         }
741     }
742 
743     public ConfigFile getConfig(String filename) throws ManagerCommunicationException
744     {
745         final ManagerResponse response;
746         final GetConfigResponse getConfigResponse;
747 
748         initializeIfNeeded();
749         response = sendAction(new GetConfigAction(filename));
750         if (!(response instanceof GetConfigResponse))
751         {
752             throw new ManagerCommunicationException("Response to GetConfigAction(\"" + filename
753                     + "\") was not a CommandResponse but " + response, null);
754         }
755 
756         getConfigResponse = (GetConfigResponse) response;
757 
758         final Map<String, List<String>> categories = new LinkedHashMap<String, List<String>>();
759         final Map<Integer, String> categoryMap = getConfigResponse.getCategories();
760         for (Map.Entry<Integer, String> categoryEntry : categoryMap.entrySet())
761         {
762             final List<String> lines;
763             final Map<Integer, String> lineMap = getConfigResponse.getLines(categoryEntry.getKey());
764 
765             if (lineMap == null)
766             {
767                 lines = new ArrayList<String>();
768             }
769             else
770             {
771                 lines = new ArrayList<String>(lineMap.values());
772             }
773 
774             categories.put(categoryEntry.getValue(), lines);
775         }
776 
777         return new ConfigFileImpl(filename, categories);
778     }
779 
780     public void addAsteriskServerListener(AsteriskServerListener listener) throws ManagerCommunicationException
781     {
782         initializeIfNeeded();
783         synchronized (listeners)
784         {
785             listeners.add(listener);
786         }
787     }
788 
789     public void removeAsteriskServerListener(AsteriskServerListener listener)
790     {
791         synchronized (listeners)
792         {
793             listeners.remove(listener);
794         }
795     }
796 
797     void fireNewAsteriskChannel(AsteriskChannel channel)
798     {
799         synchronized (listeners)
800         {
801             for (AsteriskServerListener listener : listeners)
802             {
803                 try
804                 {
805                     listener.onNewAsteriskChannel(channel);
806                 }
807                 catch (Exception e)
808                 {
809                     logger.warn("Exception in onNewAsteriskChannel()", e);
810                 }
811             }
812         }
813     }
814 
815     void fireNewMeetMeUser(MeetMeUser user)
816     {
817         synchronized (listeners)
818         {
819             for (AsteriskServerListener listener : listeners)
820             {
821                 try
822                 {
823                     listener.onNewMeetMeUser(user);
824                 }
825                 catch (Exception e)
826                 {
827                     logger.warn("Exception in onNewMeetMeUser()", e);
828                 }
829             }
830         }
831     }
832 
833     ManagerResponse sendActionOnEventConnection(ManagerAction action) throws ManagerCommunicationException
834     {
835         try
836         {
837             return eventConnection.sendAction(action);
838         }
839         catch (Exception e)
840         {
841             throw ManagerCommunicationExceptionMapper.mapSendActionException(action.getAction(), e);
842         }
843     }
844 
845     ManagerResponse sendAction(ManagerAction action) throws ManagerCommunicationException
846     {
847         // return connectionPool.sendAction(action);
848         try
849         {
850             return eventConnection.sendAction(action);
851         }
852         catch (Exception e)
853         {
854             throw ManagerCommunicationExceptionMapper.mapSendActionException(action.getAction(), e);
855         }
856     }
857 
858     ResponseEvents sendEventGeneratingAction(EventGeneratingAction action) throws ManagerCommunicationException
859     {
860         // return connectionPool.sendEventGeneratingAction(action);
861         try
862         {
863             return eventConnection.sendEventGeneratingAction(action);
864         }
865         catch (Exception e)
866         {
867             throw ManagerCommunicationExceptionMapper.mapSendActionException(action.getAction(), e);
868         }
869     }
870 
871     ResponseEvents sendEventGeneratingAction(EventGeneratingAction action,
872                                              long timeout) throws ManagerCommunicationException
873     {
874         // return connectionPool.sendEventGeneratingAction(action, timeout);
875         try
876         {
877             return eventConnection.sendEventGeneratingAction(action, timeout);
878         }
879         catch (Exception e)
880         {
881             throw ManagerCommunicationExceptionMapper.mapSendActionException(action.getAction(), e);
882         }
883     }
884 
885     OriginateCallbackData getOriginateCallbackDataByTraceId(String traceId)
886     {
887         synchronized (originateCallbacks)
888         {
889             return originateCallbacks.get(traceId);
890         }
891     }
892 
893     /* Implementation of the ManagerEventListener interface */
894 
895     /**
896      * Handles all events received from the Asterisk server.
897      * <p/>
898      * Events are queued until channels and queues are initialized and then
899      * delegated to the dispatchEvent method.
900      */
901     public void onManagerEvent(ManagerEvent event)
902     {
903         // Handle Channel related events
904         if (event instanceof ConnectEvent)
905         {
906             handleConnectEvent((ConnectEvent) event);
907         }
908         else if (event instanceof DisconnectEvent)
909         {
910             handleDisconnectEvent((DisconnectEvent) event);
911         }
912         else if (event instanceof NewChannelEvent)
913         {
914             channelManager.handleNewChannelEvent((NewChannelEvent) event);
915         }
916         else if (event instanceof NewExtenEvent)
917         {
918             channelManager.handleNewExtenEvent((NewExtenEvent) event);
919         }
920         else if (event instanceof NewStateEvent)
921         {
922             channelManager.handleNewStateEvent((NewStateEvent) event);
923         }
924         else if (event instanceof NewCallerIdEvent)
925         {
926             channelManager.handleNewCallerIdEvent((NewCallerIdEvent) event);
927         }
928         else if (event instanceof DialEvent)
929         {
930             channelManager.handleDialEvent((DialEvent) event);
931         }
932         else if (event instanceof BridgeEvent)
933         {
934             channelManager.handleBridgeEvent((BridgeEvent) event);
935         }
936         else if (event instanceof RenameEvent)
937         {
938             channelManager.handleRenameEvent((RenameEvent) event);
939         }
940         else if (event instanceof HangupEvent)
941         {
942             channelManager.handleHangupEvent((HangupEvent) event);
943         }
944         else if (event instanceof CdrEvent)
945         {
946             channelManager.handleCdrEvent((CdrEvent) event);
947         }
948         else if (event instanceof VarSetEvent)
949         {
950             channelManager.handleVarSetEvent((VarSetEvent) event);
951         }
952         else if (event instanceof DtmfEvent)
953         {
954             channelManager.handleDtmfEvent((DtmfEvent) event);
955         }
956         // End of channel related events
957         // Handle parking related event
958         else if (event instanceof ParkedCallEvent)
959         {
960             channelManager.handleParkedCallEvent((ParkedCallEvent) event);
961         }
962         else if (event instanceof ParkedCallGiveUpEvent)
963         {
964             channelManager.handleParkedCallGiveUpEvent((ParkedCallGiveUpEvent) event);
965         }
966         else if (event instanceof ParkedCallTimeOutEvent)
967         {
968             channelManager.handleParkedCallTimeOutEvent((ParkedCallTimeOutEvent) event);
969         }
970         else if (event instanceof UnparkedCallEvent)
971         {
972             channelManager.handleUnparkedCallEvent((UnparkedCallEvent) event);
973         }
974         // End of parking related events
975         // Handle queue related event
976         else if (event instanceof JoinEvent)
977         {
978             queueManager.handleJoinEvent((JoinEvent) event);
979         }
980         else if (event instanceof LeaveEvent)
981         {
982             queueManager.handleLeaveEvent((LeaveEvent) event);
983         }
984         else if (event instanceof QueueMemberStatusEvent)
985         {
986             queueManager.handleQueueMemberStatusEvent((QueueMemberStatusEvent) event);
987         }
988         else if (event instanceof QueueMemberPenaltyEvent)
989         {
990             queueManager.handleQueueMemberPenaltyEvent((QueueMemberPenaltyEvent) event);
991         }
992         else if (event instanceof QueueMemberAddedEvent)
993         {
994             queueManager.handleQueueMemberAddedEvent((QueueMemberAddedEvent) event);
995         }
996         else if (event instanceof QueueMemberRemovedEvent)
997         {
998             queueManager.handleQueueMemberRemovedEvent((QueueMemberRemovedEvent) event);
999         }
1000         else if (event instanceof QueueMemberPausedEvent)
1001         {
1002             queueManager.handleQueueMemberPausedEvent((QueueMemberPausedEvent) event);
1003         }
1004         // >>>>>> AJ 94
1005         // Handle meetMeEvents
1006         else if (event instanceof AbstractMeetMeEvent)
1007         {
1008             meetMeManager.handleMeetMeEvent((AbstractMeetMeEvent) event);
1009         }
1010         else if (event instanceof OriginateResponseEvent)
1011         {
1012             handleOriginateEvent((OriginateResponseEvent) event);
1013         }
1014         // Handle agents-related events
1015         else if (event instanceof AgentsEvent)
1016         {
1017             agentManager.handleAgentsEvent((AgentsEvent) event);
1018         }
1019         else if (event instanceof AgentCalledEvent)
1020         {
1021             agentManager.handleAgentCalledEvent((AgentCalledEvent) event);
1022         }
1023         else if (event instanceof AgentConnectEvent)
1024         {
1025             agentManager.handleAgentConnectEvent((AgentConnectEvent) event);
1026         }
1027         else if (event instanceof AgentCompleteEvent)
1028         {
1029             agentManager.handleAgentCompleteEvent((AgentCompleteEvent) event);
1030         }
1031         else if (event instanceof AgentCallbackLoginEvent)
1032         {
1033             agentManager.handleAgentCallbackLoginEvent((AgentCallbackLoginEvent) event);
1034         }
1035         else if (event instanceof AgentCallbackLogoffEvent)
1036         {
1037             agentManager.handleAgentCallbackLogoffEvent((AgentCallbackLogoffEvent) event);
1038         }
1039         else if (event instanceof AgentLoginEvent)
1040         {
1041             agentManager.handleAgentLoginEvent((AgentLoginEvent) event);
1042         }
1043         else if (event instanceof AgentLogoffEvent)
1044         {
1045             agentManager.handleAgentLogoffEvent((AgentLogoffEvent) event);
1046         }
1047         // End of agent-related events
1048     }
1049 
1050     /*
1051      * Resets the internal state when the connection to the asterisk server is
1052      * lost.
1053      */
1054 
1055     private void handleDisconnectEvent(DisconnectEvent disconnectEvent)
1056     {
1057         // reset version information as it might have changed while Asterisk restarted
1058         version = null;
1059         versions = null;
1060 
1061         // same for channels, agents and queues rooms, they are reinitialized when reconnected
1062         channelManager.disconnected();
1063         agentManager.disconnected();
1064         meetMeManager.disconnected();
1065         queueManager.disconnected();
1066         initialized = false;
1067     }
1068 
1069     /*
1070      * Requests the current state from the asterisk server after the connection
1071      * to the asterisk server is restored.
1072      */
1073 
1074     private void handleConnectEvent(ConnectEvent connectEvent)
1075     {
1076         try
1077         {
1078             initialize();
1079         }
1080         catch (Exception e)
1081         {
1082             logger.error("Unable to reinitialize state after reconnection", e);
1083         }
1084     }
1085 
1086     private void handleOriginateEvent(OriginateResponseEvent originateEvent)
1087     {
1088         final String traceId;
1089         final OriginateCallbackData callbackData;
1090         final OriginateCallback cb;
1091         final AsteriskChannelImpl channel;
1092         final AsteriskChannelImpl otherChannel; // the other side if local channel
1093 
1094         traceId = originateEvent.getActionId();
1095         if (traceId == null)
1096         {
1097             return;
1098         }
1099 
1100         synchronized (originateCallbacks)
1101         {
1102             callbackData = originateCallbacks.get(traceId);
1103             if (callbackData == null)
1104             {
1105                 return;
1106             }
1107             originateCallbacks.remove(traceId);
1108         }
1109 
1110         cb = callbackData.getCallback();
1111         if (!AstUtil.isNull(originateEvent.getUniqueId()))
1112         {
1113             channel = channelManager.getChannelImplById(originateEvent.getUniqueId());
1114         }
1115         else
1116         {
1117             channel = callbackData.getChannel();
1118         }
1119 
1120         try
1121         {
1122             if (channel == null)
1123             {
1124                 final LiveException cause;
1125 
1126                 cause = new NoSuchChannelException("Channel '" + callbackData.getOriginateAction().getChannel() + "' is not available");
1127                 cb.onFailure(cause);
1128                 return;
1129             }
1130 
1131             if (channel.wasBusy())
1132             {
1133                 cb.onBusy(channel);
1134                 return;
1135             }
1136 
1137             otherChannel = channelManager.getOtherSideOfLocalChannel(channel);
1138             // special treatment of local channels:
1139             // the interesting things happen to the other side so we have a look at that
1140             if (otherChannel != null)
1141             {
1142                 final AsteriskChannel dialedChannel;
1143 
1144                 dialedChannel = otherChannel.getDialedChannel();
1145 
1146                 // on busy the other channel is in state busy when we receive the originate event
1147                 if (otherChannel.wasBusy())
1148                 {
1149                     cb.onBusy(channel);
1150                     return;
1151                 }
1152 
1153                 // alternative:
1154                 // on busy the dialed channel is hung up when we receive the
1155                 // originate event having a look at the hangup cause reveals the
1156                 // information we are interested in
1157                 // this alternative has the drawback that there might by
1158                 // multiple channels that have been dialed by the local channel
1159                 // but we only look at the last one.
1160                 if (dialedChannel != null && dialedChannel.wasBusy())
1161                 {
1162                     cb.onBusy(channel);
1163                     return;
1164                 }
1165             }
1166             
1167             if (channel.wasInState(ChannelState.DOWN))
1168             {
1169                 cb.onNoAnswer(channel);
1170                 return;
1171             }
1172 
1173             // if nothing else matched we asume success
1174             cb.onSuccess(channel);
1175         }
1176         catch (Throwable t)
1177         {
1178             logger.warn("Exception dispatching originate progress", t);
1179         }
1180     }
1181 
1182     public void shutdown()
1183     {
1184         if (eventConnection != null && (eventConnection.getState() == ManagerConnectionState.CONNECTED || eventConnection.getState() == ManagerConnectionState.RECONNECTING))
1185         {
1186             eventConnection.logoff();
1187         }
1188         if (managerEventListenerProxy != null)
1189         {
1190             managerEventListenerProxy.shutdown();
1191         }
1192         managerEventListenerProxy = null;
1193         eventListener = null;
1194     }
1195 
1196     public List<PeerEntryEvent> getPeerEntries() throws ManagerCommunicationException
1197     {
1198         ResponseEvents responseEvents = sendEventGeneratingAction(new SipPeersAction(), 2000);
1199         List<PeerEntryEvent> peerEntries = new ArrayList<PeerEntryEvent>(30);
1200         for (ResponseEvent re : responseEvents.getEvents())
1201         {
1202             if (re instanceof PeerEntryEvent)
1203             {
1204                 peerEntries.add((PeerEntryEvent) re);
1205             }
1206         }
1207         return peerEntries;
1208     }
1209 
1210     public DbGetResponseEvent dbGet(String family, String key) throws ManagerCommunicationException
1211     {
1212         ResponseEvents responseEvents = sendEventGeneratingAction(new DbGetAction(family, key), 2000);
1213         DbGetResponseEvent dbgre = null;
1214         for (ResponseEvent re : responseEvents.getEvents())
1215         {
1216             dbgre = (DbGetResponseEvent) re;
1217         }
1218         return dbgre;
1219     }
1220 
1221     public void dbDel(String family, String key) throws ManagerCommunicationException
1222     {
1223         // The following only works with BRIStuffed asrterisk: sendAction(new DbDelAction(family,key));
1224         // Use cli command instead ...
1225         sendAction(new CommandAction("database del " + family + " " + key));
1226     }
1227 
1228     public void dbPut(String family, String key, String value) throws ManagerCommunicationException
1229     {
1230         sendAction(new DbPutAction(family, key, value));
1231     }
1232 
1233     public AsteriskChannel getChannelByNameAndActive(String name) throws ManagerCommunicationException
1234     {
1235         initializeIfNeeded();
1236         return channelManager.getChannelImplByNameAndActive(name);
1237     }
1238 
1239     public Collection<AsteriskAgent> getAgents() throws ManagerCommunicationException
1240     {
1241         initializeIfNeeded();
1242         return agentManager.getAgents();
1243     }
1244 
1245     void fireNewAgent(AsteriskAgentImpl agent)
1246     {
1247         synchronized (listeners)
1248         {
1249             for (AsteriskServerListener listener : listeners)
1250             {
1251                 try
1252                 {
1253                     listener.onNewAgent(agent);
1254                 }
1255                 catch (Exception e)
1256                 {
1257                     logger.warn("Exception in onNewAgent()", e);
1258                 }
1259             }
1260         }
1261     }
1262 
1263     void fireNewQueueEntry(AsteriskQueueEntry entry)
1264     {
1265         synchronized (listeners)
1266         {
1267             for (AsteriskServerListener listener : listeners)
1268             {
1269                 try
1270                 {
1271                     listener.onNewQueueEntry(entry);
1272                 }
1273                 catch (Exception e)
1274                 {
1275                     logger.warn("Exception in onNewQueueEntry()", e);
1276                 }
1277             }
1278         }
1279     }
1280 }