1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.asteriskjava.live.internal;
18
19 import java.util.*;
20
21 import org.asteriskjava.live.AsteriskChannel;
22 import org.asteriskjava.live.AsteriskQueueEntry;
23 import org.asteriskjava.live.CallDetailRecord;
24 import org.asteriskjava.live.CallerId;
25 import org.asteriskjava.live.ChannelState;
26 import org.asteriskjava.live.ChannelStateHistoryEntry;
27 import org.asteriskjava.live.DialedChannelHistoryEntry;
28 import org.asteriskjava.live.Extension;
29 import org.asteriskjava.live.ExtensionHistoryEntry;
30 import org.asteriskjava.live.HangupCause;
31 import org.asteriskjava.live.LinkedChannelHistoryEntry;
32 import org.asteriskjava.live.ManagerCommunicationException;
33 import org.asteriskjava.live.NoSuchChannelException;
34 import org.asteriskjava.manager.action.AbsoluteTimeoutAction;
35 import org.asteriskjava.manager.action.ChangeMonitorAction;
36 import org.asteriskjava.manager.action.GetVarAction;
37 import org.asteriskjava.manager.action.HangupAction;
38 import org.asteriskjava.manager.action.MonitorAction;
39 import org.asteriskjava.manager.action.PauseMonitorAction;
40 import org.asteriskjava.manager.action.PlayDtmfAction;
41 import org.asteriskjava.manager.action.RedirectAction;
42 import org.asteriskjava.manager.action.SetVarAction;
43 import org.asteriskjava.manager.action.StopMonitorAction;
44 import org.asteriskjava.manager.action.UnpauseMonitorAction;
45 import org.asteriskjava.manager.response.ManagerError;
46 import org.asteriskjava.manager.response.ManagerResponse;
47
48
49
50
51
52
53
54 class AsteriskChannelImpl extends AbstractLiveObject implements AsteriskChannel
55 {
56 private static final String CAUSE_VARIABLE_NAME = "PRI_CAUSE";
57
58
59
60
61 private String id;
62
63
64
65
66 private String traceId;
67
68
69
70
71 private final Date dateOfCreation;
72
73
74
75
76 private Date dateOfRemoval;
77
78
79
80
81 private String name;
82
83
84
85
86 private CallerId callerId;
87
88
89
90
91 private ChannelState state;
92
93
94
95
96 private String account;
97 private final List<ExtensionHistoryEntry> extensionHistory;
98 private final List<ChannelStateHistoryEntry> stateHistory;
99 private final List<LinkedChannelHistoryEntry> linkedChannelHistory;
100 private final List<DialedChannelHistoryEntry> dialedChannelHistory;
101
102 private AsteriskChannel dialedChannel;
103
104 private AsteriskChannel dialingChannel;
105
106
107
108
109
110 private AsteriskChannel linkedChannel;
111
112
113
114
115 private boolean wasLinked;
116
117 private HangupCause hangupCause;
118
119 private String hangupCauseText;
120
121 private CallDetailRecordImpl callDetailRecord;
122
123
124
125
126
127 private MeetMeUserImpl meetMeUserImpl;
128
129
130
131
132
133 private AsteriskQueueEntryImpl queueEntryImpl;
134
135
136
137
138
139 private Extension parkedAt;
140
141
142
143
144 private Character dtmfReceived;
145
146
147
148
149 private Character dtmfSent;
150
151 private final Map<String, String> variables;
152
153
154
155
156
157
158
159
160
161
162 AsteriskChannelImpl(final AsteriskServerImpl server, final String name, final String id, final Date dateOfCreation)
163 throws IllegalArgumentException
164 {
165 super(server);
166
167 if (server == null)
168 {
169 throw new IllegalArgumentException("Parameter 'server' passed to AsteriskChannelImpl() must not be null.");
170 }
171 if (name == null)
172 {
173 throw new IllegalArgumentException("Parameter 'name' passed to AsteriskChannelImpl() must not be null.");
174 }
175 if (id == null)
176 {
177 throw new IllegalArgumentException("Parameter 'id' passed to AsteriskChannelImpl() must not be null.");
178 }
179 if (dateOfCreation == null)
180 {
181 throw new IllegalArgumentException("Parameter 'dateOfCreation' passed to AsteriskChannelImpl() must not be null.");
182 }
183
184 this.name = name;
185 this.id = id;
186 this.dateOfCreation = dateOfCreation;
187 this.extensionHistory = new ArrayList<ExtensionHistoryEntry>();
188 this.stateHistory = new ArrayList<ChannelStateHistoryEntry>();
189 this.linkedChannelHistory = new ArrayList<LinkedChannelHistoryEntry>();
190 this.dialedChannelHistory = new ArrayList<DialedChannelHistoryEntry>();
191 this.variables = new HashMap<String, String>();
192 }
193
194 public String getId()
195 {
196 return id;
197 }
198
199
200
201
202
203
204
205 void idChanged(Date date, String id)
206 {
207 final String oldId = this.id;
208
209 if (oldId != null && oldId.equals(id))
210 {
211 return;
212 }
213
214 this.id = id;
215 firePropertyChange(PROPERTY_ID, oldId, id);
216 }
217
218 String getTraceId()
219 {
220 return traceId;
221 }
222
223 void setTraceId(String traceId)
224 {
225 this.traceId = traceId;
226 }
227
228 public String getName()
229 {
230 return name;
231 }
232
233
234
235
236
237
238
239 void nameChanged(Date date, String name)
240 {
241 final String oldName = this.name;
242
243 if (oldName != null && oldName.equals(name))
244 {
245 return;
246 }
247
248 this.name = name;
249 firePropertyChange(PROPERTY_NAME, oldName, name);
250 }
251
252 public CallerId getCallerId()
253 {
254 return callerId;
255 }
256
257
258
259
260
261
262 void setCallerId(final CallerId callerId)
263 {
264 final CallerId oldCallerId = this.callerId;
265
266 this.callerId = callerId;
267 firePropertyChange(PROPERTY_CALLER_ID, oldCallerId, callerId);
268 }
269
270 public ChannelState getState()
271 {
272 return state;
273 }
274
275 public boolean wasInState(ChannelState state)
276 {
277 synchronized (stateHistory)
278 {
279 for (ChannelStateHistoryEntry historyEntry : stateHistory)
280 {
281 if (historyEntry.getState() == state)
282 {
283 return true;
284 }
285 }
286 }
287
288 return false;
289 }
290
291 public boolean wasBusy()
292 {
293 return wasInState(ChannelState.BUSY)
294 || hangupCause == HangupCause.AST_CAUSE_BUSY
295 || hangupCause == HangupCause.AST_CAUSE_USER_BUSY;
296 }
297
298
299
300
301
302
303
304 synchronized void stateChanged(Date date, ChannelState state)
305 {
306 final ChannelStateHistoryEntry historyEntry;
307 final ChannelState oldState = this.state;
308
309 if (oldState == state)
310 {
311 return;
312 }
313
314
315
316 historyEntry = new ChannelStateHistoryEntry(date, state);
317 synchronized (stateHistory)
318 {
319 stateHistory.add(historyEntry);
320 }
321
322 this.state = state;
323 firePropertyChange(PROPERTY_STATE, oldState, state);
324 }
325
326 public String getAccount()
327 {
328 return account;
329 }
330
331
332
333
334
335
336 void setAccount(String account)
337 {
338 final String oldAccount = this.account;
339
340 this.account = account;
341 firePropertyChange(PROPERTY_ACCOUNT, oldAccount, account);
342 }
343
344 public Extension getCurrentExtension()
345 {
346 final Extension extension;
347
348 synchronized (extensionHistory)
349 {
350 if (extensionHistory.isEmpty())
351 {
352 extension = null;
353 }
354 else
355 {
356 extension = extensionHistory.get(extensionHistory.size() - 1).getExtension();
357 }
358 }
359
360 return extension;
361 }
362
363 public Extension getFirstExtension()
364 {
365 final Extension extension;
366
367 synchronized (extensionHistory)
368 {
369 if (extensionHistory.isEmpty())
370 {
371 extension = null;
372 }
373 else
374 {
375 extension = extensionHistory.get(0).getExtension();
376 }
377 }
378
379 return extension;
380 }
381
382 public List<ExtensionHistoryEntry> getExtensionHistory()
383 {
384 final List<ExtensionHistoryEntry> copy;
385
386 synchronized (extensionHistory)
387 {
388 copy = new ArrayList<ExtensionHistoryEntry>(extensionHistory);
389 }
390
391 return copy;
392 }
393
394
395
396
397
398
399
400 void extensionVisited(Date date, Extension extension)
401 {
402 final Extension oldCurrentExtension = getCurrentExtension();
403 final ExtensionHistoryEntry historyEntry;
404
405 historyEntry = new ExtensionHistoryEntry(date, extension);
406
407 synchronized (extensionHistory)
408 {
409 extensionHistory.add(historyEntry);
410 }
411
412 firePropertyChange(PROPERTY_CURRENT_EXTENSION, oldCurrentExtension, extension);
413 }
414
415 public Date getDateOfCreation()
416 {
417 return dateOfCreation;
418 }
419
420 public Date getDateOfRemoval()
421 {
422 return dateOfRemoval;
423 }
424
425 public HangupCause getHangupCause()
426 {
427 return hangupCause;
428 }
429
430 public String getHangupCauseText()
431 {
432 return hangupCauseText;
433 }
434
435 public CallDetailRecord getCallDetailRecord()
436 {
437 return callDetailRecord;
438 }
439
440 void callDetailRecordReceived(Date date, CallDetailRecordImpl callDetailRecord)
441 {
442 final CallDetailRecordImpl oldCallDetailRecord = this.callDetailRecord;
443
444 this.callDetailRecord = callDetailRecord;
445 firePropertyChange(PROPERTY_CALL_DETAIL_RECORD, oldCallDetailRecord, callDetailRecord);
446 }
447
448
449
450
451
452
453
454
455
456 synchronized void hungup(Date dateOfRemoval, HangupCause hangupCause, String hangupCauseText)
457 {
458 this.dateOfRemoval = dateOfRemoval;
459 this.hangupCause = hangupCause;
460 this.hangupCauseText = hangupCauseText;
461
462 stateChanged(dateOfRemoval, ChannelState.HUNGUP);
463 }
464
465
466
467 public AsteriskChannel getDialedChannel()
468 {
469 return dialedChannel;
470 }
471
472 public List<DialedChannelHistoryEntry> getDialedChannelHistory()
473 {
474 final List<DialedChannelHistoryEntry> copy;
475
476 synchronized (linkedChannelHistory)
477 {
478 copy = new ArrayList<DialedChannelHistoryEntry>(dialedChannelHistory);
479 }
480
481 return copy;
482 }
483
484 synchronized void channelDialed(Date date, AsteriskChannel dialedChannel)
485 {
486 final AsteriskChannel oldDialedChannel = this.dialedChannel;
487 final DialedChannelHistoryEntry historyEntry;
488
489 historyEntry = new DialedChannelHistoryEntry(date, dialedChannel);
490 synchronized (dialedChannelHistory)
491 {
492 dialedChannelHistory.add(historyEntry);
493 }
494 this.dialedChannel = dialedChannel;
495 firePropertyChange(PROPERTY_DIALED_CHANNEL, oldDialedChannel, dialedChannel);
496 }
497
498
499
500 public AsteriskChannel getDialingChannel()
501 {
502 return dialingChannel;
503 }
504
505 synchronized void channelDialing(Date date, AsteriskChannel dialingChannel)
506 {
507 final AsteriskChannel oldDialingChannel = this.dialingChannel;
508
509 this.dialingChannel = dialingChannel;
510 firePropertyChange(PROPERTY_DIALING_CHANNEL, oldDialingChannel, dialingChannel);
511 }
512
513
514
515 public AsteriskChannel getLinkedChannel()
516 {
517 return linkedChannel;
518 }
519
520 public List<LinkedChannelHistoryEntry> getLinkedChannelHistory()
521 {
522 final List<LinkedChannelHistoryEntry> copy;
523
524 synchronized (linkedChannelHistory)
525 {
526 copy = new ArrayList<LinkedChannelHistoryEntry>(linkedChannelHistory);
527 }
528
529 return copy;
530 }
531
532 public boolean wasLinked()
533 {
534 return wasLinked;
535 }
536
537
538
539
540
541
542
543 synchronized void channelLinked(Date date, AsteriskChannel linkedChannel)
544 {
545 final AsteriskChannel oldLinkedChannel = this.linkedChannel;
546 final LinkedChannelHistoryEntry historyEntry;
547
548 historyEntry = new LinkedChannelHistoryEntry(date, linkedChannel);
549 synchronized (linkedChannelHistory)
550 {
551 linkedChannelHistory.add(historyEntry);
552 }
553 this.linkedChannel = linkedChannel;
554 this.wasLinked = true;
555 firePropertyChange(PROPERTY_LINKED_CHANNEL, oldLinkedChannel, linkedChannel);
556 }
557
558 synchronized void channelUnlinked(Date date)
559 {
560 final AsteriskChannel oldLinkedChannel = this.linkedChannel;
561 final LinkedChannelHistoryEntry historyEntry;
562
563 synchronized (linkedChannelHistory)
564 {
565 if (linkedChannelHistory.isEmpty())
566 {
567 historyEntry = null;
568 }
569 else
570 {
571 historyEntry = linkedChannelHistory.get(linkedChannelHistory.size() - 1);
572 }
573 }
574
575 if (historyEntry != null)
576 {
577 historyEntry.setDateUnlinked(date);
578 }
579 this.linkedChannel = null;
580 firePropertyChange(PROPERTY_LINKED_CHANNEL, oldLinkedChannel, null);
581 }
582
583
584
585 public MeetMeUserImpl getMeetMeUser()
586 {
587 return meetMeUserImpl;
588 }
589
590 void setMeetMeUserImpl(MeetMeUserImpl meetMeUserImpl)
591 {
592 final MeetMeUserImpl oldMeetMeUserImpl = this.meetMeUserImpl;
593 this.meetMeUserImpl = meetMeUserImpl;
594 firePropertyChange(PROPERTY_MEET_ME_USER, oldMeetMeUserImpl, meetMeUserImpl);
595 }
596
597
598
599 public void hangup() throws ManagerCommunicationException, NoSuchChannelException
600 {
601 hangup(null);
602 }
603
604 public void hangup(HangupCause cause) throws ManagerCommunicationException, NoSuchChannelException
605 {
606 final HangupAction action;
607 final ManagerResponse response;
608
609 if (cause != null)
610 {
611 setVariable(CAUSE_VARIABLE_NAME, Integer.toString(cause.getCode()));
612 action = new HangupAction(name, cause.getCode());
613 }
614 else
615 {
616 action = new HangupAction(name);
617 }
618
619 response = server.sendAction(action);
620 if (response instanceof ManagerError)
621 {
622 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
623 }
624 }
625
626 public void setAbsoluteTimeout(int seconds) throws ManagerCommunicationException, NoSuchChannelException
627 {
628 ManagerResponse response;
629
630 response = server.sendAction(new AbsoluteTimeoutAction(name, seconds));
631 if (response instanceof ManagerError)
632 {
633 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
634 }
635 }
636
637 public void redirect(String context, String exten, int priority) throws ManagerCommunicationException,
638 NoSuchChannelException
639 {
640 ManagerResponse response;
641
642 response = server.sendAction(new RedirectAction(name, context, exten, priority));
643 if (response instanceof ManagerError)
644 {
645 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
646 }
647 }
648
649 public void redirectBothLegs(String context, String exten, int priority) throws ManagerCommunicationException,
650 NoSuchChannelException
651 {
652 ManagerResponse response;
653
654 if (linkedChannel == null)
655 {
656 response = server.sendAction(new RedirectAction(name, context, exten, priority));
657 }
658 else
659 {
660 response = server.sendAction(new RedirectAction(name, linkedChannel.getName(), context, exten, priority,
661 context, exten, priority));
662 }
663
664 if (response instanceof ManagerError)
665 {
666 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
667 }
668 }
669
670 public String getVariable(String variable) throws ManagerCommunicationException, NoSuchChannelException
671 {
672 ManagerResponse response;
673 String value;
674
675 synchronized (variables)
676 {
677
678 value = variables.get(variable);
679 if (value != null)
680 {
681 return value;
682 }
683
684 response = server.sendAction(new GetVarAction(name, variable));
685 if (response instanceof ManagerError)
686 {
687 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
688 }
689 value = response.getAttribute("Value");
690 if (value == null)
691 {
692 value = response.getAttribute(variable);
693 }
694
695 variables.put(variable, value);
696 }
697 return value;
698 }
699
700 public void setVariable(String variable, String value) throws ManagerCommunicationException, NoSuchChannelException
701 {
702 ManagerResponse response;
703
704 response = server.sendAction(new SetVarAction(name, variable, value));
705 if (response instanceof ManagerError)
706 {
707 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
708 }
709 synchronized (variables)
710 {
711 variables.put(variable, value);
712 }
713 }
714
715 public void playDtmf(String digit) throws ManagerCommunicationException, NoSuchChannelException, IllegalArgumentException
716 {
717 ManagerResponse response;
718
719 if (digit == null)
720 {
721 throw new IllegalArgumentException("DTMF digit to send must not be null");
722 }
723
724 response = server.sendAction(new PlayDtmfAction(name, digit));
725 if (response instanceof ManagerError)
726 {
727 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
728 }
729 }
730
731 public void startMonitoring(String filename) throws ManagerCommunicationException, NoSuchChannelException
732 {
733 startMonitoring(filename, null, false);
734 }
735
736 public void startMonitoring(String filename, String format) throws ManagerCommunicationException, NoSuchChannelException
737 {
738 startMonitoring(filename, format, false);
739 }
740
741 public void startMonitoring(String filename, String format, boolean mix) throws ManagerCommunicationException,
742 NoSuchChannelException
743 {
744 ManagerResponse response;
745
746 response = server.sendAction(new MonitorAction(name, filename, format, mix));
747 if (response instanceof ManagerError)
748 {
749 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
750 }
751 }
752
753 public void changeMonitoring(String filename) throws ManagerCommunicationException, NoSuchChannelException, IllegalArgumentException
754 {
755 ManagerResponse response;
756
757 if (filename == null)
758 {
759 throw new IllegalArgumentException("New filename must not be null");
760 }
761
762 response = server.sendAction(new ChangeMonitorAction(name, filename));
763 if (response instanceof ManagerError)
764 {
765 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
766 }
767 }
768
769 public void stopMonitoring() throws ManagerCommunicationException, NoSuchChannelException
770 {
771 ManagerResponse response;
772
773 response = server.sendAction(new StopMonitorAction(name));
774 if (response instanceof ManagerError)
775 {
776 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
777 }
778 }
779
780 public void pauseMonitoring() throws ManagerCommunicationException, NoSuchChannelException
781 {
782 ManagerResponse response;
783
784 response = server.sendAction(new PauseMonitorAction(name));
785 if (response instanceof ManagerError)
786 {
787 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
788 }
789 }
790
791 public void unpauseMonitoring() throws ManagerCommunicationException, NoSuchChannelException
792 {
793 ManagerResponse response;
794
795 response = server.sendAction(new UnpauseMonitorAction(name));
796 if (response instanceof ManagerError)
797 {
798 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
799 }
800 }
801
802 public Extension getParkedAt()
803 {
804
805
806 return parkedAt;
807 }
808
809 void updateVariable(String name, String value)
810 {
811 synchronized (variables)
812 {
813
814 variables.put(name, value);
815
816 }
817 }
818
819 public Map<String, String> getVariables()
820 {
821 synchronized (variables)
822 {
823 return new HashMap<String, String>(variables);
824 }
825 }
826
827 public Character getDtmfReceived()
828 {
829 return this.dtmfReceived;
830 }
831
832 public Character getDtmfSent()
833 {
834 return this.dtmfSent;
835 }
836
837 void dtmfReceived(Character digit)
838 {
839 final Character oldDtmfReceived = this.dtmfReceived;
840
841 this.dtmfReceived = digit;
842 firePropertyChange(PROPERTY_DTMF_RECEIVED, oldDtmfReceived, digit);
843 }
844
845 void dtmfSent(Character digit)
846 {
847 final Character oldDtmfSent = this.dtmfSent;
848
849 this.dtmfSent = digit;
850 firePropertyChange(PROPERTY_DTMF_SENT, oldDtmfSent, digit);
851 }
852
853 void setParkedAt(Extension parkedAt)
854 {
855 final Extension oldParkedAt = this.parkedAt;
856
857 this.parkedAt = parkedAt;
858 firePropertyChange(PROPERTY_PARKED_AT, oldParkedAt, parkedAt);
859 }
860
861 public AsteriskQueueEntryImpl getQueueEntry()
862 {
863 return queueEntryImpl;
864 }
865
866 void setQueueEntry(AsteriskQueueEntryImpl queueEntry)
867 {
868 final AsteriskQueueEntry oldQueueEntry = this.queueEntryImpl;
869
870 this.queueEntryImpl = queueEntry;
871 firePropertyChange(PROPERTY_QUEUE_ENTRY, oldQueueEntry, queueEntry);
872 }
873
874 @Override
875 public String toString()
876 {
877 final StringBuffer sb;
878 final AsteriskChannel dialedChannel;
879 final AsteriskChannel dialingChannel;
880 final AsteriskChannel linkedChannel;
881
882 sb = new StringBuffer("AsteriskChannel[");
883
884 synchronized (this)
885 {
886 sb.append("id='").append(getId()).append("',");
887 sb.append("name='").append(getName()).append("',");
888 sb.append("callerId='").append(getCallerId()).append("',");
889 sb.append("state='").append(getState()).append("',");
890 sb.append("account='").append(getAccount()).append("',");
891 sb.append("dateOfCreation=").append(getDateOfCreation()).append(",");
892 dialedChannel = this.dialedChannel;
893 dialingChannel = this.dialingChannel;
894 linkedChannel = this.linkedChannel;
895 }
896 if (dialedChannel == null)
897 {
898 sb.append("dialedChannel=null,");
899 }
900 else
901 {
902 sb.append("dialedChannel=AsteriskChannel[");
903 synchronized (dialedChannel)
904 {
905 sb.append("id='").append(dialedChannel.getId()).append("',");
906 sb.append("name='").append(dialedChannel.getName()).append("'],");
907 }
908 }
909 if (dialingChannel == null)
910 {
911 sb.append("dialingChannel=null,");
912 }
913 else
914 {
915 sb.append("dialingChannel=AsteriskChannel[");
916 synchronized (dialingChannel)
917 {
918 sb.append("id='").append(dialingChannel.getId()).append("',");
919 sb.append("name='").append(dialingChannel.getName()).append("'],");
920 }
921 }
922 if (linkedChannel == null)
923 {
924 sb.append("linkedChannel=null");
925 }
926 else
927 {
928 sb.append("linkedChannel=AsteriskChannel[");
929 synchronized (linkedChannel)
930 {
931 sb.append("id='").append(linkedChannel.getId()).append("',");
932 sb.append("name='").append(linkedChannel.getName()).append("']");
933 }
934 }
935 sb.append("]");
936
937 return sb.toString();
938 }
939 }