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.Collection;
21 import java.util.Date;
22 import java.util.HashMap;
23 import java.util.Iterator;
24 import java.util.Locale;
25 import java.util.Map;
26
27 import org.asteriskjava.live.AsteriskChannel;
28 import org.asteriskjava.live.CallerId;
29 import org.asteriskjava.live.ChannelState;
30 import org.asteriskjava.live.Extension;
31 import org.asteriskjava.live.HangupCause;
32 import org.asteriskjava.live.ManagerCommunicationException;
33 import org.asteriskjava.manager.ResponseEvents;
34 import org.asteriskjava.manager.action.StatusAction;
35 import org.asteriskjava.manager.event.CdrEvent;
36 import org.asteriskjava.manager.event.DialEvent;
37 import org.asteriskjava.manager.event.HangupEvent;
38 import org.asteriskjava.manager.event.LinkEvent;
39 import org.asteriskjava.manager.event.ManagerEvent;
40 import org.asteriskjava.manager.event.NewCallerIdEvent;
41 import org.asteriskjava.manager.event.NewChannelEvent;
42 import org.asteriskjava.manager.event.NewExtenEvent;
43 import org.asteriskjava.manager.event.NewStateEvent;
44 import org.asteriskjava.manager.event.RenameEvent;
45 import org.asteriskjava.manager.event.StatusEvent;
46 import org.asteriskjava.manager.event.UnlinkEvent;
47 import org.asteriskjava.util.DateUtil;
48 import org.asteriskjava.util.Log;
49 import org.asteriskjava.util.LogFactory;
50
51 /***
52 * Manages channel events on behalf of an AsteriskServer.
53 *
54 * @author srt
55 * @version $Id: ChannelManager.java 886 2007-09-05 15:56:21Z srt $
56 */
57 class ChannelManager
58 {
59 private final Log logger = LogFactory.getLog(getClass());
60
61 /***
62 * How long we wait before we remove hung up channels from memory (in milliseconds).
63 */
64 private static final long REMOVAL_THRESHOLD = 15 * 60 * 1000L;
65
66 private final AsteriskServerImpl server;
67
68 /***
69 * A map of all active channel by their unique id.
70 */
71 private final Map<String, AsteriskChannelImpl> channels;
72
73 /***
74 * Creates a new instance.
75 *
76 * @param server the server this channel manager belongs to.
77 */
78 ChannelManager(AsteriskServerImpl server)
79 {
80 this.server = server;
81 this.channels = new HashMap<String, AsteriskChannelImpl>();
82 }
83
84 void initialize() throws ManagerCommunicationException
85 {
86 ResponseEvents re;
87
88 disconnected();
89 re = server.sendEventGeneratingAction(new StatusAction());
90 for (ManagerEvent event : re.getEvents())
91 {
92 if (event instanceof StatusEvent)
93 {
94 handleStatusEvent((StatusEvent) event);
95 }
96 }
97 }
98
99 void disconnected()
100 {
101 synchronized (channels)
102 {
103 channels.clear();
104 }
105 }
106
107 /***
108 * Returns a collection of all active AsteriskChannels.
109 *
110 * @return a collection of all active AsteriskChannels.
111 */
112 Collection<AsteriskChannel> getChannels()
113 {
114 Collection<AsteriskChannel> copy;
115
116 synchronized (channels)
117 {
118 copy = new ArrayList<AsteriskChannel>(channels.size() + 2);
119 for (AsteriskChannel channel : channels.values())
120 {
121 if (channel.getState() != ChannelState.HUNGUP)
122 {
123 copy.add(channel);
124 }
125 }
126 }
127 return copy;
128 }
129
130 private void addChannel(AsteriskChannelImpl channel)
131 {
132 synchronized (channels)
133 {
134 channels.put(channel.getId(), channel);
135 }
136 }
137
138 /***
139 * Removes channels that have been hung more than {@link #REMOVAL_THRESHOLD} milliseconds.
140 */
141 private void removeOldChannels()
142 {
143 Iterator<AsteriskChannelImpl> i;
144
145 synchronized (channels)
146 {
147 i = channels.values().iterator();
148 while (i.hasNext())
149 {
150 final AsteriskChannel channel = i.next();
151 final Date dateOfRemoval = channel.getDateOfRemoval();
152 if (channel.getState() == ChannelState.HUNGUP && dateOfRemoval != null)
153 {
154 final long diff = DateUtil.getDate().getTime() - dateOfRemoval.getTime();
155 if (diff >= REMOVAL_THRESHOLD)
156 {
157 i.remove();
158 }
159 }
160 }
161 }
162 }
163
164 private AsteriskChannelImpl addNewChannel(String uniqueId, String name,
165 Date dateOfCreation, String callerIdNumber, String callerIdName,
166 ChannelState state)
167 {
168 final AsteriskChannelImpl channel;
169 final String traceId;
170
171 channel = new AsteriskChannelImpl(server, name, uniqueId, dateOfCreation);
172 channel.setCallerId(new CallerId(callerIdName, callerIdNumber));
173 channel.stateChanged(dateOfCreation, state);
174 logger.info("Adding channel " + channel.getName() + "(" + channel.getId() + ")");
175
176 traceId = getTraceId(channel);
177 channel.setTraceId(traceId);
178
179 addChannel(channel);
180
181 if (traceId != null && (! name.toLowerCase(Locale.ENGLISH).startsWith("local/") || name.endsWith(",1")))
182 {
183 final OriginateCallbackData callbackData;
184 callbackData = server.getOriginateCallbackDataByTraceId(traceId);
185 if (callbackData != null && callbackData.getChannel() == null)
186 {
187 callbackData.setChannel(channel);
188 try
189 {
190 callbackData.getCallback().onDialing(channel);
191 }
192 catch (Throwable t)
193 {
194 logger.warn("Exception dispatching originate progress.", t);
195 }
196 }
197 }
198 server.fireNewAsteriskChannel(channel);
199 return channel;
200 }
201
202 private ChannelState string2ChannelState(String state)
203 {
204 if (state == null)
205 {
206 return null;
207 }
208
209 return ChannelState.valueOf(state.toUpperCase());
210 }
211
212 void handleStatusEvent(StatusEvent event)
213 {
214 AsteriskChannelImpl channel;
215 final Extension extension;
216 boolean isNew = false;
217
218 channel = getChannelImplById(event.getUniqueId());
219 if (channel == null)
220 {
221 Date dateOfCreation;
222
223 if (event.getSeconds() != null)
224 {
225 dateOfCreation = new Date(DateUtil.getDate().getTime() - (event.getSeconds() * 1000L));
226 }
227 else
228 {
229 dateOfCreation = DateUtil.getDate();
230 }
231 channel = new AsteriskChannelImpl(server, event.getChannel(), event.getUniqueId(), dateOfCreation);
232 isNew = true;
233 }
234
235 if (event.getContext() == null && event.getExtension() == null
236 && event.getPriority() == null)
237 {
238 extension = null;
239 }
240 else
241 {
242 extension = new Extension(event.getContext(), event.getExtension(), event.getPriority());
243 }
244
245 synchronized (channel)
246 {
247 channel.setCallerId(new CallerId(event.getCallerIdName(), event.getCallerIdNum()));
248 channel.setAccount(event.getAccount());
249 if (event.getState() != null)
250 {
251 channel.stateChanged(event.getDateReceived(), ChannelState.valueOf(event.getState().toUpperCase()));
252 }
253 channel.extensionVisited(event.getDateReceived(), extension);
254
255 if (event.getLink() != null)
256 {
257 final AsteriskChannelImpl linkedChannel = getChannelImplByName(event.getLink());
258 if (linkedChannel != null)
259 {
260
261 channel.channelLinked(event.getDateReceived(), linkedChannel);
262 synchronized (linkedChannel)
263 {
264 linkedChannel.channelLinked(event.getDateReceived(), channel);
265 }
266 }
267 }
268 }
269
270 if (isNew)
271 {
272 logger.info("Adding new channel " + channel.getName());
273 addChannel(channel);
274 server.fireNewAsteriskChannel(channel);
275 }
276 }
277
278 AsteriskChannelImpl getChannelImplByName(String name)
279 {
280 Date dateOfCreation = null;
281 AsteriskChannelImpl channel = null;
282
283 if (name == null)
284 {
285 return null;
286 }
287
288 synchronized (channels)
289 {
290 for (AsteriskChannelImpl tmp : channels.values())
291 {
292 if (tmp.getName() != null && tmp.getName().equals(name))
293 {
294
295 if (dateOfCreation == null || tmp.getDateOfCreation().after(dateOfCreation))
296 {
297 channel = tmp;
298 dateOfCreation = channel.getDateOfCreation();
299 }
300 }
301 }
302 }
303 return channel;
304 }
305
306 AsteriskChannelImpl getChannelImplById(String id)
307 {
308 AsteriskChannelImpl channel = null;
309
310 if (id == null)
311 {
312 return null;
313 }
314
315 synchronized (channels)
316 {
317 channel = channels.get(id);
318 }
319 return channel;
320 }
321
322 /***
323 * Returns the other side of a local channel.
324 * <p>
325 * Local channels consist of two sides, like
326 * "Local/1234@from-local-60b5,1" and "Local/1234@from-local-60b5,2"
327 * this method returns the other side.
328 *
329 * @param localChannel one side
330 * @return the other side, or <code>null</code> if not available or if the given channel
331 * is not a local channel.
332 */
333 AsteriskChannelImpl getOtherSideOfLocalChannel(AsteriskChannel localChannel)
334 {
335 final String name;
336 final char num;
337
338 if (localChannel == null)
339 {
340 return null;
341 }
342
343 name = localChannel.getName();
344 if (name == null || !name.startsWith("Local/") || name.charAt(name.length() - 2) != ',')
345 {
346 return null;
347 }
348
349 num = name.charAt(name.length() - 1);
350
351 if (num == '1')
352 {
353 return getChannelImplByName(name.substring(0, name.length() - 1) + "2");
354 }
355 else if (num == '2')
356 {
357 return getChannelImplByName(name.substring(0, name.length() - 1) + "1");
358 }
359 else
360 {
361 return null;
362 }
363 }
364
365 void handleNewChannelEvent(NewChannelEvent event)
366 {
367 AsteriskChannelImpl channel;
368
369 channel = getChannelImplById(event.getUniqueId());
370 if (channel == null)
371 {
372 channel = addNewChannel(
373 event.getUniqueId(), event.getChannel(), event.getDateReceived(),
374 event.getCallerIdNum(), event.getCallerIdName(),
375 string2ChannelState(event.getState()));
376 }
377 else
378 {
379
380 synchronized (channel)
381 {
382 channel.nameChanged(event.getDateReceived(), event.getChannel());
383 channel.setCallerId(new CallerId(event.getCallerIdName(), event.getCallerIdNum()));
384 channel.stateChanged(event.getDateReceived(), string2ChannelState(event.getState()));
385 }
386 }
387 }
388
389 void handleNewExtenEvent(NewExtenEvent event)
390 {
391 AsteriskChannelImpl channel;
392 Extension extension;
393
394 channel = getChannelImplById(event.getUniqueId());
395 if (channel == null)
396 {
397 logger.error("Ignored NewExtenEvent for unknown channel "
398 + event.getChannel());
399 return;
400 }
401
402 extension = new Extension(
403 event.getContext(), event.getExtension(), event.getPriority(),
404 event.getApplication(), event.getAppData());
405
406 synchronized (channel)
407 {
408 channel.extensionVisited(event.getDateReceived(), extension);
409 }
410 }
411
412 void handleNewStateEvent(NewStateEvent event)
413 {
414 AsteriskChannelImpl channel = getChannelImplById(event.getUniqueId());
415
416 if (channel == null)
417 {
418
419 channel = addNewChannel(
420 event.getUniqueId(), event.getChannel(), event.getDateReceived(),
421 event.getCallerIdNum(), event.getCallerIdName(), string2ChannelState(event.getState()));
422 }
423 if (event.getState() != null)
424 {
425 synchronized (channel)
426 {
427 channel.stateChanged(event.getDateReceived(), ChannelState.valueOf(event.getState().toUpperCase()));
428 }
429 }
430 }
431
432 void handleNewCallerIdEvent(NewCallerIdEvent event)
433 {
434 AsteriskChannelImpl channel = getChannelImplById(event.getUniqueId());
435
436 if (channel == null)
437 {
438
439 channel = addNewChannel(
440 event.getUniqueId(), event.getChannel(), event.getDateReceived(),
441 event.getCallerIdNum(), event.getCallerIdName(), ChannelState.DOWN);
442 }
443 else
444 {
445 synchronized (channel)
446 {
447 channel.setCallerId(new CallerId(event.getCallerIdName(), event.getCallerIdNum()));
448 }
449 }
450 }
451
452 void handleHangupEvent(HangupEvent event)
453 {
454 HangupCause cause = null;
455 AsteriskChannelImpl channel = getChannelImplById(event.getUniqueId());
456
457 if (channel == null)
458 {
459 logger.error("Ignored HangupEvent for unknown channel "
460 + event.getChannel());
461 return;
462 }
463
464 if (event.getCause() != null)
465 {
466 cause = HangupCause.getByCode(event.getCause());
467 }
468
469 synchronized (channel)
470 {
471 channel.hungup(event.getDateReceived(), cause, event.getCauseTxt());
472 }
473
474 logger.info("Removing channel " + channel.getName() + " due to hangup (" + cause + ")");
475 removeOldChannels();
476 }
477
478 void handleDialEvent(DialEvent event)
479 {
480 AsteriskChannelImpl sourceChannel = getChannelImplById(event.getSrcUniqueId());
481 AsteriskChannelImpl destinationChannel = getChannelImplById(event.getDestUniqueId());
482
483 if (sourceChannel == null)
484 {
485 logger.error("Ignored LinkEvent for unknown source channel "
486 + event.getSrc());
487 return;
488 }
489 if (destinationChannel == null)
490 {
491 logger.error("Ignored DialEvent for unknown destination channel "
492 + event.getDestination());
493 return;
494 }
495
496 logger.info(sourceChannel.getName() + " dialed " + destinationChannel.getName());
497 getTraceId(sourceChannel);
498 getTraceId(destinationChannel);
499 synchronized (sourceChannel)
500 {
501 sourceChannel.channelDialed(event.getDateReceived(), destinationChannel);
502 }
503 synchronized (destinationChannel)
504 {
505 destinationChannel.channelDialing(event.getDateReceived(), sourceChannel);
506 }
507 }
508
509 void handleLinkEvent(LinkEvent event)
510 {
511 AsteriskChannelImpl channel1 = getChannelImplById(event.getUniqueId1());
512 AsteriskChannelImpl channel2 = getChannelImplById(event.getUniqueId2());
513
514 if (channel1 == null)
515 {
516 logger.error("Ignored LinkEvent for unknown channel "
517 + event.getChannel1());
518 return;
519 }
520 if (channel2 == null)
521 {
522 logger.error("Ignored LinkEvent for unknown channel "
523 + event.getChannel2());
524 return;
525 }
526
527 logger.info("Linking channels " + channel1.getName() + " and "
528 + channel2.getName());
529 synchronized (channel1)
530 {
531 channel1.channelLinked(event.getDateReceived(), channel2);
532 }
533
534 synchronized (channel2)
535 {
536 channel2.channelLinked(event.getDateReceived(), channel1);
537 }
538 }
539
540 void handleUnlinkEvent(UnlinkEvent event)
541 {
542 AsteriskChannelImpl channel1 = getChannelImplByName(event.getChannel1());
543 AsteriskChannelImpl channel2 = getChannelImplByName(event.getChannel2());
544
545 if (channel1 == null)
546 {
547 logger.error("Ignored UnlinkEvent for unknown channel "
548 + event.getChannel1());
549 return;
550 }
551 if (channel2 == null)
552 {
553 logger.error("Ignored UnlinkEvent for unknown channel "
554 + event.getChannel2());
555 return;
556 }
557
558 logger.info("Unlinking channels " + channel1.getName() + " and "
559 + channel2.getName());
560 synchronized (channel1)
561 {
562 channel1.channelUnlinked(event.getDateReceived());
563 }
564
565 synchronized (channel2)
566 {
567 channel2.channelUnlinked(event.getDateReceived());
568 }
569 }
570
571 void handleRenameEvent(RenameEvent event)
572 {
573 AsteriskChannelImpl channel = getChannelImplById(event.getUniqueId());
574
575 if (channel == null)
576 {
577 logger.error("Ignored RenameEvent for unknown channel with uniqueId "
578 + event.getUniqueId());
579 return;
580 }
581
582 logger.info("Renaming channel '" + channel.getName() + "' to '"
583 + event.getNewname() + "'");
584 synchronized (channel)
585 {
586 channel.nameChanged(event.getDateReceived(), event.getNewname());
587 }
588 }
589
590 void handleCdrEvent(CdrEvent event)
591 {
592 AsteriskChannelImpl channel = getChannelImplById(event.getUniqueId());
593 AsteriskChannelImpl destinationChannel = getChannelImplByName(event.getDestinationChannel());
594 CallDetailRecordImpl cdr;
595
596 if (channel == null)
597 {
598 logger.info("Ignored CdrEvent for unknown channel with uniqueId "
599 + event.getUniqueId());
600 return;
601 }
602
603 cdr = new CallDetailRecordImpl(channel, destinationChannel, event);
604
605 synchronized (channel)
606 {
607 channel.callDetailRecordReceived(event.getDateReceived(), cdr);
608 }
609 }
610
611 private String getTraceId(AsteriskChannel channel)
612 {
613 String traceId;
614
615 try
616 {
617 traceId = channel.getVariable(Constants.VARIABLE_TRACE_ID);
618 }
619 catch (Exception e)
620 {
621 traceId = null;
622 }
623
624 return traceId;
625 }
626 }