View Javadoc

1   /*
2    * Copyright  2004-2005 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 net.sf.asterisk.manager;
18  
19  import java.io.IOException;
20  import java.util.Collections;
21  import java.util.Date;
22  import java.util.HashMap;
23  import java.util.Iterator;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.regex.Matcher;
27  import java.util.regex.Pattern;
28  
29  import net.sf.asterisk.manager.action.CommandAction;
30  import net.sf.asterisk.manager.action.OriginateAction;
31  import net.sf.asterisk.manager.action.QueueStatusAction;
32  import net.sf.asterisk.manager.action.StatusAction;
33  import net.sf.asterisk.manager.event.ConnectEvent;
34  import net.sf.asterisk.manager.event.DisconnectEvent;
35  import net.sf.asterisk.manager.event.HangupEvent;
36  import net.sf.asterisk.manager.event.JoinEvent;
37  import net.sf.asterisk.manager.event.LeaveEvent;
38  import net.sf.asterisk.manager.event.LinkEvent;
39  import net.sf.asterisk.manager.event.ManagerEvent;
40  import net.sf.asterisk.manager.event.NewCallerIdEvent;
41  import net.sf.asterisk.manager.event.NewChannelEvent;
42  import net.sf.asterisk.manager.event.NewExtenEvent;
43  import net.sf.asterisk.manager.event.NewStateEvent;
44  import net.sf.asterisk.manager.event.OriginateEvent;
45  import net.sf.asterisk.manager.event.OriginateFailureEvent;
46  import net.sf.asterisk.manager.event.QueueEntryEvent;
47  import net.sf.asterisk.manager.event.QueueMemberEvent;
48  import net.sf.asterisk.manager.event.QueueParamsEvent;
49  import net.sf.asterisk.manager.event.RenameEvent;
50  import net.sf.asterisk.manager.event.StatusEvent;
51  import net.sf.asterisk.manager.event.UnlinkEvent;
52  import net.sf.asterisk.manager.response.CommandResponse;
53  import net.sf.asterisk.manager.response.ManagerResponse;
54  import net.sf.asterisk.util.Log;
55  import net.sf.asterisk.util.LogFactory;
56  
57  /***
58   * Default implementation of the AsteriskManager interface.
59   * 
60   * @see net.sf.asterisk.manager.AsteriskManager
61   * @author srt
62   * @version $Id: DefaultAsteriskManager.java,v 1.23 2005/10/29 12:09:05 srt Exp $
63   */
64  public class DefaultAsteriskManager
65          implements
66              AsteriskManager,
67              ManagerEventHandler
68  {
69      private static final Pattern SHOW_VERSION_FILES_PATTERN = Pattern
70              .compile("^([//S]+)//s+Revision: ([0-9//.]+)");
71  
72      private final Log logger = LogFactory.getLog(this.getClass());
73  
74      /***
75       * The underlying manager connection used to talk to Asterisk.
76       */
77      private ManagerConnection connection;
78  
79      /***
80       * A map of all active channel by their unique id.
81       */
82      private final Map channels;
83  
84      /***
85       * A map of ACD queues by there name.
86       */
87      private final Map queues;
88  
89      /***
90       * The version of the Asterisk server we are connected to.<br>
91       * Contains <code>null</code> until lazily initialized.
92       */
93      private String version;
94  
95      /***
96       * Holds the version of Asterisk's source files.<br>
97       * That corresponds to the output of the CLI command
98       * <code>show version files</code>.<br>
99       * Contains <code>null</code> until lazily initialized.
100      */
101     private Map versions;
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      * Creates a new instance.
111      */
112     public DefaultAsteriskManager()
113     {
114         this.channels = Collections.synchronizedMap(new HashMap());
115         this.queues = Collections.synchronizedMap(new HashMap());
116     }
117 
118     /***
119      * Creates a new instance.
120      * 
121      * @param connection the manager connection to use
122      */
123     public DefaultAsteriskManager(ManagerConnection connection)
124     {
125         this();
126         this.connection = connection;
127     }
128 
129     /***
130      * Determines if queue status is retrieved at startup. If you don't need
131      * queue information and still run Asterisk 1.0.x you can set this to
132      * <code>true</code> to circumvent the startup delay caused by the missing
133      * QueueStatusComplete event.<br>
134      * Default is <code>false</code>.
135      * 
136      * @param skipQueues <code>true</code> to skip queue initialization,
137      *            <code>false</code> to not skip.
138      * @since 0.2
139      */
140     public void setSkipQueues(boolean skipQueues)
141     {
142         this.skipQueues = skipQueues;
143     }
144 
145     public void setManagerConnection(ManagerConnection connection)
146     {
147         this.connection = connection;
148     }
149 
150     public void initialize() throws TimeoutException, IOException,
151             AuthenticationFailedException
152     {
153         connection.login();
154 
155         initializeChannels();
156         initializeQueues();
157 
158         connection.addEventHandler(this);
159     }
160 
161     private void initializeChannels() throws EventTimeoutException, IOException
162     {
163         ResponseEvents re;
164         Iterator i;
165 
166         re = connection.sendEventGeneratingAction(new StatusAction());
167         i = re.getEvents().iterator();
168         while (i.hasNext())
169         {
170             ManagerEvent event;
171 
172             event = (ManagerEvent) i.next();
173             if (event instanceof StatusEvent)
174             {
175                 handleStatusEvent((StatusEvent) event);
176             }
177         }
178     }
179 
180     private void initializeQueues() throws IOException
181     {
182         ResponseEvents re;
183         Iterator i;
184 
185         if (skipQueues)
186         {
187             return;
188         }
189 
190         try
191         {
192             re = connection.sendEventGeneratingAction(new QueueStatusAction());
193         }
194         catch (EventTimeoutException e)
195         {
196             // this happens with Asterisk 1.0.x as it doesn't send a
197             // QueueStatusCompleteEvent
198             re = e.getPartialResult();
199         }
200 
201         i = re.getEvents().iterator();
202         while (i.hasNext())
203         {
204             ManagerEvent event;
205 
206             event = (ManagerEvent) i.next();
207             if (event instanceof QueueParamsEvent)
208             {
209                 handleQueueParamsEvent((QueueParamsEvent) event);
210             }
211             else if (event instanceof QueueMemberEvent)
212             {
213                 handleQueueMemberEvent((QueueMemberEvent) event);
214             }
215             else if (event instanceof QueueEntryEvent)
216             {
217                 handleQueueEntryEvent((QueueEntryEvent) event);
218             }
219         }
220     }
221 
222     /* Implementation of the AsteriskManager interface */
223 
224     public Call originateCall(Originate originate) throws TimeoutException,
225             IOException
226     {
227         OriginateAction originateAction;
228         ResponseEvents responseEvents;
229         Long timeout;
230 
231         if (originate.getTimeout() == null)
232         {
233             timeout = new Long(30000);
234         }
235         else
236         {
237             timeout = originate.getTimeout();
238         }
239 
240         originateAction = new OriginateAction();
241         originateAction.setAccount(originate.getAccount());
242         originateAction.setApplication(originate.getApplication());
243         originateAction.setCallerId(originate.getCallerId());
244         originateAction.setChannel(originate.getChannel());
245         originateAction.setContext(originate.getContext());
246         originateAction.setData(originate.getData());
247         originateAction.setExten(originate.getExten());
248         originateAction.setPriority(originate.getPriority());
249         originateAction.setTimeout(timeout);
250         originateAction.setVariables(originate.getVariables());
251 
252         // must set async to true to receive OriginateEvents.
253         originateAction.setAsync(Boolean.TRUE);
254 
255         // 2000 ms extra for the OriginateFailureEvent should be fine
256         responseEvents = connection.sendEventGeneratingAction(originateAction,
257                 timeout.longValue() + 2000);
258 
259         return originateEvent2Call((OriginateEvent) responseEvents.getEvents()
260                 .toArray()[0]);
261     }
262 
263     /***
264      * Returns a map of all active channel by their unique id.
265      */
266     public Map getChannels()
267     {
268         return channels;
269     }
270 
271     public Map getQueues()
272     {
273         return queues;
274     }
275 
276     public String getVersion()
277     {
278         if (version == null)
279         {
280             ManagerResponse response;
281             try
282             {
283                 response = connection.sendAction(new CommandAction(
284                         "show version"));
285                 if (response instanceof CommandResponse)
286                 {
287                     List result;
288 
289                     result = ((CommandResponse) response).getResult();
290                     if (result.size() > 0)
291                     {
292                         version = (String) result.get(0);
293                     }
294                 }
295             }
296             catch (Exception e)
297             {
298                 logger.warn("Unable to send 'show version' command.", e);
299             }
300         }
301 
302         return version;
303     }
304 
305     public int[] getVersion(String file)
306     {
307         String fileVersion = null;
308         String[] parts;
309         int[] intParts;
310 
311         if (versions == null)
312         {
313             Map map;
314             ManagerResponse response;
315 
316             map = new HashMap();
317             try
318             {
319                 response = connection.sendAction(new CommandAction(
320                         "show version files"));
321                 if (response instanceof CommandResponse)
322                 {
323                     List result;
324 
325                     result = ((CommandResponse) response).getResult();
326                     for (int i = 2; i < result.size(); i++)
327                     {
328                         String line;
329                         Matcher matcher;
330 
331                         line = (String) result.get(i);
332                         matcher = SHOW_VERSION_FILES_PATTERN.matcher(line);
333                         if (matcher.find())
334                         {
335                             String key = matcher.group(1);
336                             String value = matcher.group(2);
337 
338                             map.put(key, value);
339                         }
340                     }
341 
342                     fileVersion = (String) map.get(file);
343                     versions = map;
344                 }
345             }
346             catch (Exception e)
347             {
348                 logger.warn("Unable to send 'show version files' command.", e);
349             }
350         }
351         else
352         {
353             synchronized (versions)
354             {
355                 fileVersion = (String) versions.get(file);
356             }
357         }
358 
359         if (fileVersion == null)
360         {
361             return null;
362         }
363 
364         parts = fileVersion.split("//.");
365         intParts = new int[parts.length];
366 
367         for (int i = 0; i < parts.length; i++)
368         {
369             try
370             {
371                 intParts[i] = Integer.parseInt(parts[i]);
372             }
373             catch (NumberFormatException e)
374             {
375                 intParts[i] = 0;
376             }
377         }
378 
379         return intParts;
380     }
381 
382     /* Implementation of the ManagerEventHandler interface */
383 
384     /***
385      * Handles all events received from the asterisk server.<br>
386      * Events are queued until channels and queues are initialized and then
387      * delegated to the dispatchEvent method.
388      */
389     public void handleEvent(ManagerEvent event)
390     {
391         if (event instanceof ConnectEvent)
392         {
393             handleConnectEvent((ConnectEvent) event);
394         }
395         else if (event instanceof DisconnectEvent)
396         {
397             handleDisconnectEvent((DisconnectEvent) event);
398         }
399         else if (event instanceof NewChannelEvent)
400         {
401             handleNewChannelEvent((NewChannelEvent) event);
402         }
403         else if (event instanceof NewExtenEvent)
404         {
405             handleNewExtenEvent((NewExtenEvent) event);
406         }
407         else if (event instanceof NewStateEvent)
408         {
409             handleNewStateEvent((NewStateEvent) event);
410         }
411         else if (event instanceof NewCallerIdEvent)
412         {
413             handleNewCallerIdEvent((NewCallerIdEvent) event);
414         }
415         else if (event instanceof LinkEvent)
416         {
417             handleLinkEvent((LinkEvent) event);
418         }
419         else if (event instanceof UnlinkEvent)
420         {
421             handleUnlinkEvent((UnlinkEvent) event);
422         }
423         else if (event instanceof RenameEvent)
424         {
425             handleRenameEvent((RenameEvent) event);
426         }
427         else if (event instanceof HangupEvent)
428         {
429             handleHangupEvent((HangupEvent) event);
430         }
431         else if (event instanceof JoinEvent)
432         {
433             handleJoinEvent((JoinEvent) event);
434         }
435         else if (event instanceof LeaveEvent)
436         {
437             handleLeaveEvent((LeaveEvent) event);
438         }
439     }
440 
441     /* Private helper methods */
442 
443     protected void addChannel(Channel channel)
444     {
445         synchronized (channels)
446         {
447             channels.put(channel.getId(), channel);
448         }
449     }
450 
451     protected void removeChannel(Channel channel)
452     {
453         synchronized (channels)
454         {
455             channels.remove(channel.getId());
456         }
457     }
458 
459     protected void addQueue(Queue queue)
460     {
461         synchronized (queues)
462         {
463             queues.put(queue.getName(), queue);
464         }
465     }
466 
467     protected void removeQueue(Queue queue)
468     {
469         synchronized (queues)
470         {
471             queues.remove(queue.getName());
472         }
473     }
474 
475     protected void handleStatusEvent(StatusEvent event)
476     {
477         Channel channel;
478         Extension extension;
479         boolean isNew = false;
480 
481         channel = getChannelById(event.getUniqueId());
482         if (channel == null)
483         {
484             channel = new Channel(event.getChannel(), event.getUniqueId());
485             if (event.getSeconds() != null)
486             {
487                 channel.setDateOfCreation(new Date(System.currentTimeMillis()
488                         - (event.getSeconds().intValue() * 1000)));
489             }
490             isNew = true;
491         }
492 
493         if (event.getContext() == null && event.getExtension() == null
494                 && event.getPriority() == null)
495         {
496             extension = null;
497         }
498         else
499         {
500             extension = new Extension(event.getDateReceived(), event
501                     .getContext(), event.getExtension(), event.getPriority());
502         }
503 
504         synchronized (channel)
505         {
506             channel.setCallerId(event.getCallerId());
507             channel.setCallerIdName(event.getCallerIdName());
508             channel.setAccount(event.getAccount());
509             channel.setState(ChannelStateEnum.getEnum(event.getState()));
510             channel.addExtension(extension);
511 
512             if (event.getLink() != null)
513             {
514                 Channel linkedChannel = getChannelByName(event.getLink());
515                 if (linkedChannel != null)
516                 {
517                     channel.setLinkedChannel(linkedChannel);
518                     synchronized (linkedChannel)
519                     {
520                         linkedChannel.setLinkedChannel(channel);
521                     }
522                 }
523             }
524         }
525 
526         if (isNew)
527         {
528             logger.info("Adding new channel " + channel.getName());
529             addChannel(channel);
530         }
531     }
532 
533     /***
534      * Resets the internal state when the connection to the asterisk server is
535      * lost.
536      */
537     protected void handleDisconnectEvent(DisconnectEvent disconnectEvent)
538     {
539         // reset version information as it might have changed while Asterisk
540         // restarted
541         version = null;
542         versions = null;
543 
544         channels.clear();
545         queues.clear();
546     }
547 
548     /***
549      * Requests the current state from the asterisk server after the connection
550      * to the asterisk server is restored.
551      */
552     protected void handleConnectEvent(ConnectEvent connectEvent)
553     {
554         try
555         {
556             initializeChannels();
557         }
558         catch (Exception e)
559         {
560             logger.error("Unable to initialize channels after reconnect.", e);
561         }
562 
563         try
564         {
565             initializeQueues();
566         }
567         catch (IOException e)
568         {
569             logger.error("Unable to initialize queues after reconnect.", e);
570         }
571     }
572 
573     protected void handleQueueParamsEvent(QueueParamsEvent event)
574     {
575         Queue queue;
576         boolean isNew = false;
577 
578         queue = (Queue) queues.get(event.getQueue());
579 
580         if (queue == null)
581         {
582             queue = new Queue(event.getQueue());
583             isNew = true;
584         }
585 
586         synchronized (queue)
587         {
588             queue.setMax(event.getMax());
589         }
590 
591         if (isNew)
592         {
593             logger.info("Adding new queue " + queue.getName());
594             addQueue(queue);
595         }
596     }
597 
598     protected void handleQueueMemberEvent(QueueMemberEvent event)
599     {
600 
601     }
602 
603     protected void handleQueueEntryEvent(QueueEntryEvent event)
604     {
605         Queue queue = (Queue) queues.get(event.getQueue());
606         Channel channel = getChannelByName(event.getChannel());
607 
608         if (queue == null)
609         {
610             logger.error("ignored QueueEntryEvent for unknown queue "
611                     + event.getQueue());
612             return;
613         }
614         if (channel == null)
615         {
616             logger.error("ignored QueueEntryEvent for unknown channel "
617                     + event.getChannel());
618             return;
619         }
620 
621         if (!queue.getEntries().contains(channel))
622         {
623             queue.addEntry(channel);
624         }
625     }
626 
627     protected void handleJoinEvent(JoinEvent event)
628     {
629         Queue queue = (Queue) queues.get(event.getQueue());
630         Channel channel = getChannelByName(event.getChannel());
631 
632         if (queue == null)
633         {
634             logger.error("ignored JoinEvent for unknown queue "
635                     + event.getQueue());
636             return;
637         }
638         if (channel == null)
639         {
640             logger.error("ignored JoinEvent for unknown channel "
641                     + event.getChannel());
642             return;
643         }
644 
645         if (!queue.getEntries().contains(channel))
646         {
647             queue.addEntry(channel);
648         }
649     }
650 
651     protected void handleLeaveEvent(LeaveEvent event)
652     {
653         Queue queue = (Queue) queues.get(event.getQueue());
654         Channel channel = getChannelByName(event.getChannel());
655 
656         if (queue == null)
657         {
658             logger.error("ignored LeaveEvent for unknown queue "
659                     + event.getQueue());
660             return;
661         }
662         if (channel == null)
663         {
664             logger.error("ignored LeaveEvent for unknown channel "
665                     + event.getChannel());
666             return;
667         }
668 
669         if (queue.getEntries().contains(channel))
670         {
671             queue.removeEntry(channel);
672         }
673     }
674 
675     /***
676      * Returns a channel by its name.
677      * 
678      * @param name name of the channel to return
679      * @return the channel with the given name
680      */
681     public Channel getChannelByName(String name)
682     {
683         Channel channel = null;
684 
685         synchronized (channels)
686         {
687             Iterator channelIterator = channels.values().iterator();
688             while (channelIterator.hasNext())
689             {
690                 Channel tmp = (Channel) channelIterator.next();
691                 if (tmp.getName() != null && tmp.getName().equals(name))
692                 {
693                     channel = tmp;
694                 }
695             }
696         }
697         return channel;
698     }
699 
700     /***
701      * Returns a channel by its unique id.
702      * 
703      * @param id the unique id of the channel to return
704      * @return the channel with the given unique id
705      */
706     public Channel getChannelById(String id)
707     {
708         Channel channel = null;
709 
710         synchronized (channels)
711         {
712             channel = (Channel) channels.get(id);
713         }
714         return channel;
715     }
716 
717     protected void handleNewChannelEvent(NewChannelEvent event)
718     {
719         Channel channel = new Channel(event.getChannel(), event.getUniqueId());
720 
721         channel.setDateOfCreation(event.getDateReceived());
722         channel.setCallerId(event.getCallerId());
723         channel.setCallerIdName(event.getCallerIdName());
724         channel.setState(ChannelStateEnum.getEnum(event.getState()));
725 
726         logger.info("Adding channel " + channel.getName());
727         addChannel(channel);
728     }
729 
730     protected void handleNewExtenEvent(NewExtenEvent event)
731     {
732         Channel channel;
733         Extension extension;
734 
735         channel = getChannelById(event.getUniqueId());
736         if (channel == null)
737         {
738             logger.error("Ignored NewExtenEvent for unknown channel "
739                     + event.getChannel());
740             return;
741         }
742 
743         extension = new Extension(event.getDateReceived(), event.getContext(),
744                 event.getExtension(), event.getPriority(), event
745                         .getApplication(), event.getAppData());
746 
747         synchronized (channel)
748         {
749             channel.addExtension(extension);
750         }
751     }
752 
753     protected void handleNewStateEvent(NewStateEvent event)
754     {
755         Channel channel = getChannelById(event.getUniqueId());
756 
757         if (channel == null)
758         {
759             logger.error("Ignored NewStateEvent for unknown channel "
760                     + event.getChannel());
761             return;
762         }
763 
764         synchronized (channel)
765         {
766             channel.setState(ChannelStateEnum.getEnum(event.getState()));
767         }
768     }
769 
770     protected void handleNewCallerIdEvent(NewCallerIdEvent event)
771     {
772         Channel channel = getChannelById(event.getUniqueId());
773 
774         if (channel == null)
775         {
776             logger.error("Ignored NewCallerIdEvent for unknown channel "
777                     + event.getChannel());
778             return;
779         }
780 
781         synchronized (channel)
782         {
783             channel.setCallerId(event.getCallerId());
784             channel.setCallerIdName(event.getCallerIdName());
785         }
786     }
787 
788     protected void handleHangupEvent(HangupEvent event)
789     {
790         Channel channel = getChannelById(event.getUniqueId());
791         if (channel == null)
792         {
793             logger.error("Ignored HangupEvent for unknown channel "
794                     + event.getChannel());
795             return;
796         }
797 
798         synchronized (channel)
799         {
800             channel.setState(ChannelStateEnum.HUNGUP);
801         }
802 
803         logger.info("Removing channel " + channel.getName() + " due to hangup");
804         removeChannel(channel);
805     }
806 
807     protected void handleLinkEvent(LinkEvent event)
808     {
809         Channel channel1 = (Channel) channels.get(event.getUniqueId1());
810         Channel channel2 = (Channel) channels.get(event.getUniqueId2());
811 
812         if (channel1 == null)
813         {
814             logger.error("Ignored LinkEvent for unknown channel "
815                     + event.getChannel1());
816             return;
817         }
818         if (channel2 == null)
819         {
820             logger.error("Ignored LinkEvent for unknown channel "
821                     + event.getChannel2());
822             return;
823         }
824 
825         logger.info("Linking channels " + channel1.getName() + " and "
826                 + channel2.getName());
827         synchronized (this)
828         {
829             channel1.setLinkedChannel(channel2);
830             channel2.setLinkedChannel(channel1);
831         }
832     }
833 
834     protected void handleUnlinkEvent(UnlinkEvent event)
835     {
836         Channel channel1 = getChannelByName(event.getChannel1());
837         Channel channel2 = getChannelByName(event.getChannel2());
838 
839         if (channel1 == null)
840         {
841             logger.error("Ignored UnlinkEvent for unknown channel "
842                     + event.getChannel1());
843             return;
844         }
845         if (channel2 == null)
846         {
847             logger.error("Ignored UnlinkEvent for unknown channel "
848                     + event.getChannel2());
849             return;
850         }
851 
852         logger.info("Unlinking channels " + channel1.getName() + " and "
853                 + channel2.getName());
854         synchronized (channel1)
855         {
856             channel1.setLinkedChannel(null);
857         }
858 
859         synchronized (channel2)
860         {
861             channel2.setLinkedChannel(null);
862         }
863     }
864 
865     protected void handleRenameEvent(RenameEvent event)
866     {
867         Channel channel = getChannelById(event.getUniqueId());
868 
869         if (channel == null)
870         {
871             logger
872                     .error("Ignored RenameEvent for unknown channel with uniqueId "
873                             + event.getUniqueId());
874             return;
875         }
876 
877         logger.info("Renaming channel '" + channel.getName() + "' to '"
878                 + event.getNewname() + "'");
879         channel.setName(event.getNewname());
880     }
881 
882     protected Call originateEvent2Call(OriginateEvent event)
883     {
884         Call call;
885         Channel channel;
886 
887         channel = (Channel) channels.get(event.getUniqueId());
888         call = new Call();
889         call.setUniqueId(event.getUniqueId());
890         call.setChannel(channel);
891         call.setStartTime(event.getDateReceived());
892         if (event instanceof OriginateFailureEvent)
893         {
894             call.setEndTime(event.getDateReceived());
895         }
896         call.setReason(event.getReason());
897 
898         return call;
899     }
900 }