1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
197
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
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
253 originateAction.setAsync(Boolean.TRUE);
254
255
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
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
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
540
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 }