View Javadoc

1   /*
2    *  Copyright 2004-2006 Stefan Reuter
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   *
16   */
17  package org.asteriskjava.live.internal;
18  
19  import 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   * Default implementation of the AsteriskChannel interface.
50   *
51   * @author srt
52   * @version $Id$
53   */
54  class AsteriskChannelImpl extends AbstractLiveObject implements AsteriskChannel
55  {
56      private static final String CAUSE_VARIABLE_NAME = "PRI_CAUSE";
57  
58      /**
59       * Unique id of this channel.
60       */
61      private String id;
62  
63      /**
64       * The traceId is used to trace originated channels.
65       */
66      private String traceId;
67  
68      /**
69       * Date this channel has been created.
70       */
71      private final Date dateOfCreation;
72  
73      /**
74       * Date this channel has left the Asterisk server.
75       */
76      private Date dateOfRemoval;
77  
78      /**
79       * Name of this channel.
80       */
81      private String name;
82  
83      /**
84       * Caller*ID of this channel.
85       */
86      private CallerId callerId;
87  
88      /**
89       * State of this channel.
90       */
91      private ChannelState state;
92  
93      /**
94       * Account code used to bill this channel.
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      * If this channel is bridged to another channel, the linkedChannel contains
108      * the channel this channel is bridged with.
109      */
110     private AsteriskChannel linkedChannel;
111 
112     /**
113      * Indicates if this channel was linked to another channel at least once.
114      */
115     private boolean wasLinked;
116 
117     private HangupCause hangupCause;
118 
119     private String hangupCauseText;
120 
121     private CallDetailRecordImpl callDetailRecord;
122 
123     /**
124      * MeetMe room user associated with this channel if any, <code>null</code>
125      * otherwise.
126      */
127     private MeetMeUserImpl meetMeUserImpl;
128 
129     /**
130      * Queue entry associated with this channel if any, <code>null</code>
131      * otherwise.
132      */
133     private AsteriskQueueEntryImpl queueEntryImpl;
134 
135     /**
136      * Extension where the call is parked if it is parked, <code>null</code>
137      * otherwise.
138      */
139     private Extension parkedAt;
140 
141     /**
142      * Last dtmf digit recieved on this channel if any, <code>null</code> otherwise.
143      */
144     private Character dtmfReceived;
145 
146     /**
147      * Last dtmf digit sent on this channel if any, <code>null</code> otherwise.
148      */
149     private Character dtmfSent;
150 
151     private final Map<String, String> variables;
152 
153     /**
154      * Creates a new Channel.
155      *
156      * @param server         server this channel belongs to.
157      * @param name           name of this channel, for example "SIP/1310-20da".
158      * @param id             unique id of this channel, for example "1099015093.165".
159      * @param dateOfCreation date this channel has been created.
160      * @throws IllegalArgumentException if any of the parameters are null.
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      * Changes the id of this channel.
201      *
202      * @param date date of the name change.
203      * @param id   the new unique id of this channel.
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      * Changes the name of this channel.
235      *
236      * @param date date of the name change.
237      * @param name the new name of this channel.
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      * Sets the caller id of this channel.
259      *
260      * @param callerId the caller id of this channel.
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      * Changes the state of this channel.
300      *
301      * @param date  when the state change occurred.
302      * @param state the new state of this channel.
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         // System.err.println(id + " state change: " + oldState + " => " + state
315         // + " (" + name + ")");
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      * Sets the account code used to bill this channel.
333      *
334      * @param account the account code used to bill this channel.
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      * Adds a visted dialplan entry to the history.
396      *
397      * @param date      the date the extension has been visited.
398      * @param extension the visted dialplan entry to add.
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      * Sets dateOfRemoval, hangupCause and hangupCauseText and changes state to
450      * {@link ChannelState#HUNGUP}. Fires a PropertyChangeEvent for state.
451      *
452      * @param dateOfRemoval   date the channel was hung up
453      * @param hangupCause     cause for hangup
454      * @param hangupCauseText textual representation of hangup cause
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         // update state and fire PropertyChangeEvent
462         stateChanged(dateOfRemoval, ChannelState.HUNGUP);
463     }
464 
465     /* dialed channels */
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     /* dialed channels */
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     /* linked channels */
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      * Sets the channel this channel is bridged with.
539      *
540      * @param date          the date this channel was linked.
541      * @param linkedChannel the channel this channel is bridged with.
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     /* MeetMe user */
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     // action methods
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); // for Asterisk 1.0.x
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         // warning: the context of this extension will be null until we get the context property from
805         // the parked call event!
806         return parkedAt;
807     }
808 
809     void updateVariable(String name, String value)
810     {
811         synchronized (variables)
812         {
813             // final String oldValue = variables.get(name);
814             variables.put(name, value);
815             // TODO add notification for updated channel variables
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 }