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.ArrayList;
20 import java.util.Date;
21 import java.util.List;
22
23 import org.asteriskjava.live.AsteriskChannel;
24 import org.asteriskjava.live.CallDetailRecord;
25 import org.asteriskjava.live.CallerId;
26 import org.asteriskjava.live.ChannelState;
27 import org.asteriskjava.live.ChannelStateHistoryEntry;
28 import org.asteriskjava.live.DialedChannelHistoryEntry;
29 import org.asteriskjava.live.Extension;
30 import org.asteriskjava.live.ExtensionHistoryEntry;
31 import org.asteriskjava.live.HangupCause;
32 import org.asteriskjava.live.LinkedChannelHistoryEntry;
33 import org.asteriskjava.live.ManagerCommunicationException;
34 import org.asteriskjava.live.NoSuchChannelException;
35 import org.asteriskjava.manager.action.AbsoluteTimeoutAction;
36 import org.asteriskjava.manager.action.ChangeMonitorAction;
37 import org.asteriskjava.manager.action.GetVarAction;
38 import org.asteriskjava.manager.action.HangupAction;
39 import org.asteriskjava.manager.action.MonitorAction;
40 import org.asteriskjava.manager.action.PauseMonitorAction;
41 import org.asteriskjava.manager.action.PlayDtmfAction;
42 import org.asteriskjava.manager.action.RedirectAction;
43 import org.asteriskjava.manager.action.SetVarAction;
44 import org.asteriskjava.manager.action.StopMonitorAction;
45 import org.asteriskjava.manager.action.UnpauseMonitorAction;
46 import org.asteriskjava.manager.response.ManagerError;
47 import org.asteriskjava.manager.response.ManagerResponse;
48
49 /***
50 * Default implementation of the AsteriskChannel interface.
51 *
52 * @author srt
53 * @version $Id: AsteriskChannelImpl.java 729 2007-05-26 05:16:57Z sprior $
54 */
55 class AsteriskChannelImpl extends AbstractLiveObject implements AsteriskChannel
56 {
57 private static final String CAUSE_VARIABLE_NAME = "PRI_CAUSE";
58
59 /***
60 * Unique id of this channel.
61 */
62 private final String id;
63
64 /***
65 * The traceId is used to trace originated channels.
66 */
67 private String traceId;
68
69 /***
70 * Date this channel has been created.
71 */
72 private final Date dateOfCreation;
73
74 /***
75 * Date this channel has left the Asterisk server.
76 */
77 private Date dateOfRemoval;
78
79 /***
80 * Name of this channel.
81 */
82 private String name;
83
84 /***
85 * Caller*ID of this channel.
86 */
87 private CallerId callerId;
88
89 /***
90 * State of this channel.
91 */
92 private ChannelState state;
93
94 /***
95 * Account code used to bill this channel.
96 */
97 private String account;
98 private final List<ExtensionHistoryEntry> extensionHistory;
99 private final List<ChannelStateHistoryEntry> stateHistory;
100 private final List<LinkedChannelHistoryEntry> linkedChannelHistory;
101 private final List<DialedChannelHistoryEntry> dialedChannelHistory;
102
103 private AsteriskChannel dialedChannel;
104
105 private AsteriskChannel dialingChannel;
106
107 /***
108 * If this channel is bridged to another channel, the linkedChannel contains
109 * the channel this channel is bridged with.
110 */
111 private AsteriskChannel linkedChannel;
112
113 /***
114 * Indicates if this channel was linked to another channel at least once.
115 */
116 private boolean wasLinked;
117
118 private HangupCause hangupCause;
119
120 private String hangupCauseText;
121
122 private CallDetailRecordImpl callDetailRecord;
123
124 /***
125 * MeetMe room user associated with this channel if any, <code>null</code>
126 * otherwise.
127 */
128 private MeetMeUserImpl meetMeUserImpl;
129
130 /***
131 * Creates a new Channel.
132 *
133 * @param server server this channel belongs to.
134 * @param name name of this channel, for example "SIP/1310-20da".
135 * @param id unique id of this channel, for example "1099015093.165".
136 * @param dateOfCreation date this channel has been created.
137 * @throws NullPointerException if any of the parameters is null.
138 */
139 AsteriskChannelImpl(final AsteriskServerImpl server, final String name, final String id, final Date dateOfCreation)
140 throws NullPointerException
141 {
142 super(server);
143
144 if (server == null || name == null || id == null || dateOfCreation == null)
145 {
146 throw new NullPointerException("Parameters passed to AsteriskChannelImpl() must not be null.");
147 }
148 this.name = name;
149 this.id = id;
150 this.dateOfCreation = dateOfCreation;
151 this.extensionHistory = new ArrayList<ExtensionHistoryEntry>();
152 this.stateHistory = new ArrayList<ChannelStateHistoryEntry>();
153 this.linkedChannelHistory = new ArrayList<LinkedChannelHistoryEntry>();
154 this.dialedChannelHistory = new ArrayList<DialedChannelHistoryEntry>();
155 }
156
157 public String getId()
158 {
159 return id;
160 }
161
162 String getTraceId()
163 {
164 return traceId;
165 }
166
167 void setTraceId(String traceId)
168 {
169 this.traceId = traceId;
170 }
171
172 public String getName()
173 {
174 return name;
175 }
176
177 /***
178 * Changes the name of this channel.
179 *
180 * @param date date of the name change.
181 * @param name the new name of this channel.
182 */
183 void nameChanged(Date date, String name)
184 {
185 final String oldName = this.name;
186
187 if (oldName != null && oldName.equals(name))
188 {
189 return;
190 }
191
192 this.name = name;
193 firePropertyChange(PROPERTY_NAME, oldName, name);
194 }
195
196 public CallerId getCallerId()
197 {
198 return callerId;
199 }
200
201 /***
202 * Sets the caller id of this channel.
203 *
204 * @param callerId the caller id of this channel.
205 */
206 void setCallerId(final CallerId callerId)
207 {
208 final CallerId oldCallerId = this.callerId;
209
210 this.callerId = callerId;
211 firePropertyChange(PROPERTY_CALLER_ID, oldCallerId, callerId);
212 }
213
214 public ChannelState getState()
215 {
216 return state;
217 }
218
219 public boolean wasInState(ChannelState state)
220 {
221 synchronized (stateHistory)
222 {
223 for (ChannelStateHistoryEntry historyEntry : stateHistory)
224 {
225 if (historyEntry.getState() == state)
226 {
227 return true;
228 }
229 }
230 }
231
232 return false;
233 }
234
235 public boolean wasBusy()
236 {
237 return wasInState(ChannelState.BUSY)
238 || hangupCause == HangupCause.AST_CAUSE_BUSY
239 || hangupCause == HangupCause.AST_CAUSE_USER_BUSY;
240 }
241
242 /***
243 * Changes the state of this channel.
244 *
245 * @param date when the state change occurred.
246 * @param state the new state of this channel.
247 */
248 synchronized void stateChanged(Date date, ChannelState state)
249 {
250 final ChannelStateHistoryEntry historyEntry;
251 final ChannelState oldState = this.state;
252
253 if (oldState == state)
254 {
255 return;
256 }
257
258
259
260 historyEntry = new ChannelStateHistoryEntry(date, state);
261 synchronized (stateHistory)
262 {
263 stateHistory.add(historyEntry);
264 }
265
266 this.state = state;
267 firePropertyChange(PROPERTY_STATE, oldState, state);
268 }
269
270 public String getAccount()
271 {
272 return account;
273 }
274
275 /***
276 * Sets the account code used to bill this channel.
277 *
278 * @param account the account code used to bill this channel.
279 */
280 void setAccount(String account)
281 {
282 final String oldAccount = this.account;
283
284 this.account = account;
285 firePropertyChange(PROPERTY_ACCOUNT, oldAccount, account);
286 }
287
288 public Extension getCurrentExtension()
289 {
290 final Extension extension;
291
292 synchronized (extensionHistory)
293 {
294 if (extensionHistory.isEmpty())
295 {
296 extension = null;
297 }
298 else
299 {
300 extension = extensionHistory.get(extensionHistory.size() - 1).getExtension();
301 }
302 }
303
304 return extension;
305 }
306
307 public Extension getFirstExtension()
308 {
309 final Extension extension;
310
311 synchronized (extensionHistory)
312 {
313 if (extensionHistory.isEmpty())
314 {
315 extension = null;
316 }
317 else
318 {
319 extension = extensionHistory.get(0).getExtension();
320 }
321 }
322
323 return extension;
324 }
325
326 public List<ExtensionHistoryEntry> getExtensionHistory()
327 {
328 final List<ExtensionHistoryEntry> copy;
329
330 synchronized (extensionHistory)
331 {
332 copy = new ArrayList<ExtensionHistoryEntry>(extensionHistory);
333 }
334
335 return copy;
336 }
337
338 /***
339 * Adds a visted dialplan entry to the history.
340 *
341 * @param date the date the extension has been visited.
342 * @param extension the visted dialplan entry to add.
343 */
344 void extensionVisited(Date date, Extension extension)
345 {
346 final Extension oldCurrentExtension = getCurrentExtension();
347 final ExtensionHistoryEntry historyEntry;
348
349 historyEntry = new ExtensionHistoryEntry(date, extension);
350
351 synchronized (extensionHistory)
352 {
353 extensionHistory.add(historyEntry);
354 }
355
356 firePropertyChange(PROPERTY_CURRENT_EXTENSION, oldCurrentExtension, extension);
357 }
358
359 public Date getDateOfCreation()
360 {
361 return dateOfCreation;
362 }
363
364 public Date getDateOfRemoval()
365 {
366 return dateOfRemoval;
367 }
368
369 public HangupCause getHangupCause()
370 {
371 return hangupCause;
372 }
373
374 public String getHangupCauseText()
375 {
376 return hangupCauseText;
377 }
378
379 public CallDetailRecord getCallDetailRecord()
380 {
381 return callDetailRecord;
382 }
383
384 void callDetailRecordReceived(Date date, CallDetailRecordImpl callDetailRecord)
385 {
386 final CallDetailRecordImpl oldCallDetailRecord = this.callDetailRecord;
387
388 this.callDetailRecord = callDetailRecord;
389 firePropertyChange(PROPERTY_CALL_DETAIL_RECORD, oldCallDetailRecord, callDetailRecord);
390 }
391
392 /***
393 * Sets dateOfRemoval, hangupCause and hangupCauseText and changes state to
394 * {@link ChannelState#HUNGUP}. Fires a PropertyChangeEvent for state.
395 *
396 * @param dateOfRemoval date the channel was hung up
397 * @param hangupCause cause for hangup
398 * @param hangupCauseText textual representation of hangup cause
399 */
400 synchronized void hungup(Date dateOfRemoval, HangupCause hangupCause, String hangupCauseText)
401 {
402 this.dateOfRemoval = dateOfRemoval;
403 this.hangupCause = hangupCause;
404 this.hangupCauseText = hangupCauseText;
405
406 stateChanged(dateOfRemoval, ChannelState.HUNGUP);
407 }
408
409
410
411 public AsteriskChannel getDialedChannel()
412 {
413 return dialedChannel;
414 }
415
416 public List<DialedChannelHistoryEntry> getDialedChannelHistory()
417 {
418 final List<DialedChannelHistoryEntry> copy;
419
420 synchronized (linkedChannelHistory)
421 {
422 copy = new ArrayList<DialedChannelHistoryEntry>(dialedChannelHistory);
423 }
424
425 return copy;
426 }
427
428 synchronized void channelDialed(Date date, AsteriskChannel dialedChannel)
429 {
430 final AsteriskChannel oldDialedChannel = this.dialedChannel;
431 final DialedChannelHistoryEntry historyEntry;
432
433 historyEntry = new DialedChannelHistoryEntry(date, dialedChannel);
434 synchronized (dialedChannelHistory)
435 {
436 dialedChannelHistory.add(historyEntry);
437 }
438 this.dialedChannel = dialedChannel;
439 firePropertyChange(PROPERTY_DIALED_CHANNEL, oldDialedChannel, dialedChannel);
440 }
441
442
443
444 public AsteriskChannel getDialingChannel()
445 {
446 return dialingChannel;
447 }
448
449 synchronized void channelDialing(Date date, AsteriskChannel dialingChannel)
450 {
451 final AsteriskChannel oldDialingChannel = this.dialingChannel;
452
453 this.dialingChannel = dialingChannel;
454 firePropertyChange(PROPERTY_DIALING_CHANNEL, oldDialingChannel, dialingChannel);
455 }
456
457
458
459 public AsteriskChannel getLinkedChannel()
460 {
461 return linkedChannel;
462 }
463
464 public List<LinkedChannelHistoryEntry> getLinkedChannelHistory()
465 {
466 final List<LinkedChannelHistoryEntry> copy;
467
468 synchronized (linkedChannelHistory)
469 {
470 copy = new ArrayList<LinkedChannelHistoryEntry>(linkedChannelHistory);
471 }
472
473 return copy;
474 }
475
476 public boolean wasLinked()
477 {
478 return wasLinked;
479 }
480
481 /***
482 * Sets the channel this channel is bridged with.
483 *
484 * @param linkedChannel the channel this channel is bridged with.
485 */
486 synchronized void channelLinked(Date date, AsteriskChannel linkedChannel)
487 {
488 final AsteriskChannel oldLinkedChannel = this.linkedChannel;
489 final LinkedChannelHistoryEntry historyEntry;
490
491 historyEntry = new LinkedChannelHistoryEntry(date, linkedChannel);
492 synchronized (linkedChannelHistory)
493 {
494 linkedChannelHistory.add(historyEntry);
495 }
496 this.linkedChannel = linkedChannel;
497 this.wasLinked = true;
498 firePropertyChange(PROPERTY_LINKED_CHANNEL, oldLinkedChannel, linkedChannel);
499 }
500
501 synchronized void channelUnlinked(Date date)
502 {
503 final AsteriskChannel oldLinkedChannel = this.linkedChannel;
504 final LinkedChannelHistoryEntry historyEntry;
505
506 synchronized (linkedChannelHistory)
507 {
508 if (linkedChannelHistory.isEmpty())
509 {
510 historyEntry = null;
511 }
512 else
513 {
514 historyEntry = linkedChannelHistory.get(linkedChannelHistory.size() - 1);
515 }
516 }
517
518 if (historyEntry != null)
519 {
520 historyEntry.setDateUnlinked(date);
521 }
522 this.linkedChannel = null;
523 firePropertyChange(PROPERTY_LINKED_CHANNEL, oldLinkedChannel, null);
524 }
525
526
527
528 public MeetMeUserImpl getMeetMeUser()
529 {
530 return meetMeUserImpl;
531 }
532
533 void setMeetMeUserImpl(MeetMeUserImpl meetMeUserImpl)
534 {
535 final MeetMeUserImpl oldMeetMeUserImpl = this.meetMeUserImpl;
536 this.meetMeUserImpl = meetMeUserImpl;
537 firePropertyChange(PROPERTY_MEET_ME_USER, oldMeetMeUserImpl, meetMeUserImpl);
538 }
539
540
541
542 public void hangup() throws ManagerCommunicationException, NoSuchChannelException
543 {
544 ManagerResponse response;
545
546 response = server.sendAction(new HangupAction(name));
547 if (response instanceof ManagerError)
548 {
549 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
550 }
551 }
552
553 public void hangup(HangupCause cause) throws ManagerCommunicationException, NoSuchChannelException
554 {
555 if (cause != null)
556 {
557 setVariable(CAUSE_VARIABLE_NAME, Integer.toString(cause.getCode()));
558 }
559 hangup();
560 }
561
562 public void setAbsoluteTimeout(int seconds) throws ManagerCommunicationException, NoSuchChannelException
563 {
564 ManagerResponse response;
565
566 response = server.sendAction(new AbsoluteTimeoutAction(name, seconds));
567 if (response instanceof ManagerError)
568 {
569 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
570 }
571 }
572
573 public void redirect(String context, String exten, int priority) throws ManagerCommunicationException,
574 NoSuchChannelException
575 {
576 ManagerResponse response;
577
578 response = server.sendAction(new RedirectAction(name, context, exten, priority));
579 if (response instanceof ManagerError)
580 {
581 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
582 }
583 }
584
585 public void redirectBothLegs(String context, String exten, int priority) throws ManagerCommunicationException,
586 NoSuchChannelException
587 {
588 ManagerResponse response;
589
590 if (linkedChannel == null)
591 {
592 response = server.sendAction(new RedirectAction(name, context, exten, priority));
593 }
594 else
595 {
596 response = server.sendAction(new RedirectAction(name, linkedChannel.getName(), context, exten, priority,
597 context, exten, priority));
598 }
599
600 if (response instanceof ManagerError)
601 {
602 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
603 }
604 }
605
606 public String getVariable(String variable) throws ManagerCommunicationException, NoSuchChannelException
607 {
608 ManagerResponse response;
609 String value;
610
611 response = server.sendAction(new GetVarAction(name, variable));
612 if (response instanceof ManagerError)
613 {
614 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
615 }
616 value = response.getAttribute("Value");
617 if (value == null)
618 {
619 value = response.getAttribute(variable);
620 }
621 return value;
622 }
623
624 public void setVariable(String variable, String value) throws ManagerCommunicationException, NoSuchChannelException
625 {
626 ManagerResponse response;
627
628 response = server.sendAction(new SetVarAction(name, variable, value));
629 if (response instanceof ManagerError)
630 {
631 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
632 }
633 }
634
635 public void playDtmf(String digit) throws ManagerCommunicationException, NoSuchChannelException,
636 IllegalArgumentException
637 {
638 ManagerResponse response;
639
640 if (digit == null)
641 {
642 throw new IllegalArgumentException("DTMF digit to send must not be null");
643 }
644
645 response = server.sendAction(new PlayDtmfAction(name, digit));
646 if (response instanceof ManagerError)
647 {
648 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
649 }
650 }
651
652 public void startMonitoring(String filename) throws ManagerCommunicationException, NoSuchChannelException
653 {
654 startMonitoring(filename, null, false);
655 }
656
657 public void startMonitoring(String filename, String format) throws ManagerCommunicationException, NoSuchChannelException
658 {
659 startMonitoring(filename, format, false);
660 }
661
662 public void startMonitoring(String filename, String format, boolean mix) throws ManagerCommunicationException,
663 NoSuchChannelException
664 {
665 ManagerResponse response;
666
667 response = server.sendAction(new MonitorAction(name, filename, format, mix));
668 if (response instanceof ManagerError)
669 {
670 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
671 }
672 }
673
674 public void changeMonitoring(String filename) throws ManagerCommunicationException, NoSuchChannelException,
675 IllegalArgumentException
676 {
677 ManagerResponse response;
678
679 if (filename == null)
680 {
681 throw new IllegalArgumentException("New filename must not be null");
682 }
683
684 response = server.sendAction(new ChangeMonitorAction(name, filename));
685 if (response instanceof ManagerError)
686 {
687 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
688 }
689 }
690
691 public void stopMonitoring() throws ManagerCommunicationException, NoSuchChannelException
692 {
693 ManagerResponse response;
694
695 response = server.sendAction(new StopMonitorAction(name));
696 if (response instanceof ManagerError)
697 {
698 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
699 }
700 }
701
702 public void pauseMonitoring() throws ManagerCommunicationException, NoSuchChannelException
703 {
704 ManagerResponse response;
705
706 response = server.sendAction(new PauseMonitorAction(name));
707 if (response instanceof ManagerError)
708 {
709 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
710 }
711 }
712
713 public void unpauseMonitoring() throws ManagerCommunicationException, NoSuchChannelException
714 {
715 ManagerResponse response;
716
717 response = server.sendAction(new UnpauseMonitorAction(name));
718 if (response instanceof ManagerError)
719 {
720 throw new NoSuchChannelException("Channel '" + name + "' is not available: " + response.getMessage());
721 }
722 }
723
724 @Override
725 public String toString()
726 {
727 final StringBuffer sb;
728 final AsteriskChannel dialedChannel;
729 final AsteriskChannel dialingChannel;
730 final AsteriskChannel linkedChannel;
731
732 sb = new StringBuffer("AsteriskChannel[");
733
734 synchronized (this)
735 {
736 sb.append("id='" + getId() + "',");
737 sb.append("name='" + getName() + "',");
738 sb.append("callerId='" + getCallerId() + "',");
739 sb.append("state='" + getState() + "',");
740 sb.append("account='" + getAccount() + "',");
741 sb.append("dateOfCreation=" + getDateOfCreation() + ",");
742 dialedChannel = this.dialedChannel;
743 dialingChannel = this.dialingChannel;
744 linkedChannel = this.linkedChannel;
745 }
746 if (dialedChannel == null)
747 {
748 sb.append("dialedChannel=null,");
749 }
750 else
751 {
752 sb.append("dialedChannel=AsteriskChannel[");
753 synchronized (dialedChannel)
754 {
755 sb.append("id='" + dialedChannel.getId() + "',");
756 sb.append("name='" + dialedChannel.getName() + "'],");
757 }
758 }
759 if (dialingChannel == null)
760 {
761 sb.append("dialingChannel=null,");
762 }
763 else
764 {
765 sb.append("dialingChannel=AsteriskChannel[");
766 synchronized (dialingChannel)
767 {
768 sb.append("id='" + dialingChannel.getId() + "',");
769 sb.append("name='" + dialingChannel.getName() + "'],");
770 }
771 }
772 if (linkedChannel == null)
773 {
774 sb.append("linkedChannel=null");
775 }
776 else
777 {
778 sb.append("linkedChannel=AsteriskChannel[");
779 synchronized (linkedChannel)
780 {
781 sb.append("id='" + linkedChannel.getId() + "',");
782 sb.append("name='" + linkedChannel.getName() + "']");
783 }
784 }
785 sb.append("]");
786
787 return sb.toString();
788 }
789 }