[Asterisk-java-cvs] CVS: asterisk-java/src/java/net/sf/asterisk/manager DefaultAsteriskManager.java,
Brought to you by:
srt
Update of /cvsroot/asterisk-java/asterisk-java/src/java/net/sf/asterisk/manager In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11816/src/java/net/sf/asterisk/manager Added Files: DefaultAsteriskManager.java Channel.java ManagerConnection.java AuthenticationFailedException.java AsteriskServer.java EventBuilder.java Queue.java ManagerReader.java ChannelStateEnum.java ManagerConnectionFactory.java Util.java TimeoutException.java MultiAsterisksManager.java ManagerEventHandler.java AsteriskManager.java ManagerResponseHandler.java ActionBuilder.java DefaultManagerConnection.java Log Message: moved src and test to src/java and src/test first steps to introduce maven --- NEW FILE: DefaultAsteriskManager.java --- /* * (c) 2004 Stefan Reuter * * Created on Oct 28, 2004 */ package net.sf.asterisk.manager; import java.io.IOException; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import net.sf.asterisk.manager.action.OriginateAction; import net.sf.asterisk.manager.action.QueueStatusAction; import net.sf.asterisk.manager.action.StatusAction; import net.sf.asterisk.manager.event.ConnectEvent; import net.sf.asterisk.manager.event.DisconnectEvent; import net.sf.asterisk.manager.event.HangupEvent; import net.sf.asterisk.manager.event.LinkEvent; import net.sf.asterisk.manager.event.ManagerEvent; import net.sf.asterisk.manager.event.NewChannelEvent; import net.sf.asterisk.manager.event.NewExtenEvent; import net.sf.asterisk.manager.event.NewStateEvent; import net.sf.asterisk.manager.event.QueueEntryEvent; import net.sf.asterisk.manager.event.QueueMemberEvent; import net.sf.asterisk.manager.event.QueueParamsEvent; import net.sf.asterisk.manager.event.RenameEvent; import net.sf.asterisk.manager.event.StatusCompleteEvent; import net.sf.asterisk.manager.event.StatusEvent; import net.sf.asterisk.manager.event.UnlinkEvent; import net.sf.asterisk.manager.response.ManagerResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * @author srt * @version $Id: DefaultAsteriskManager.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public class DefaultAsteriskManager implements AsteriskManager, ManagerEventHandler { private final Log log = LogFactory.getLog(this.getClass()); private ManagerConnection connection; /** * A map of all active channel by their unique id. */ private Map channels; private Map queues; private List queuedEvents; private boolean channelsInitialized; private boolean queuesInitialized; private boolean initialized; public DefaultAsteriskManager() { this.channels = Collections.synchronizedMap(new HashMap()); this.queues = Collections.synchronizedMap(new HashMap()); this.queuedEvents = Collections.synchronizedList(new ArrayList()); this.channelsInitialized = false; //TODO fix detection of queuesInitialized this.queuesInitialized = true; this.initialized = false; } public void setManagerConnection(ManagerConnection connection) { this.connection = connection; } public void initialize() throws TimeoutException, IOException, AuthenticationFailedException { connection.addEventHandler(this); connection.login(); connection.sendAction(new StatusAction()); connection.sendAction(new QueueStatusAction()); } public String originateCall(OriginateAction originateAction) throws TimeoutException, IOException { ManagerResponse response; response = connection.sendAction(originateAction); return response == null ? null : response.getUniqueId(); } /** * Returns a map of all active channel by their unique id. */ public Map getChannels() { return channels; } public Map getQueues() { return queues; } /** * Handles all events received from the asterisk server.<br> * Events are queued until channels and queues are initialized and then * delegated to the dispatchEvent method. */ public void handleEvent(ManagerEvent event) { System.out.println("received: " + event); if (event instanceof ConnectEvent) { handleConnectEvent((ConnectEvent) event); } else if (event instanceof DisconnectEvent) { handleDisconnectEvent((DisconnectEvent) event); } // build initial state if not yet initialized, dispatch otherwise. if (!initialized) { if (event instanceof StatusEvent) { handleStatusEvent((StatusEvent) event); } else if (event instanceof StatusCompleteEvent) { handleStatusCompleteEvent((StatusCompleteEvent) event); } else if (event instanceof QueueParamsEvent) { handleQueueParamsEvent((QueueParamsEvent) event); } else if (event instanceof QueueMemberEvent) { handleQueueMemberEvent((QueueMemberEvent) event); } else if (event instanceof QueueEntryEvent) { handleQueueEntryEvent((QueueEntryEvent) event); } else { queuedEvents.add(event); } if (channelsInitialized && queuesInitialized) { Iterator i = queuedEvents.iterator(); while (i.hasNext()) { ManagerEvent queuedEvent = (ManagerEvent) i.next(); dispatchEvent(queuedEvent); i.remove(); } this.initialized = true; } } else { dispatchEvent(event); } } protected void dispatchEvent(ManagerEvent event) { if (event instanceof NewChannelEvent) { handleNewChannelEvent((NewChannelEvent) event); } else if (event instanceof NewExtenEvent) { handleNewExtenEvent((NewExtenEvent) event); } else if (event instanceof NewStateEvent) { handleNewStateEvent((NewStateEvent) event); } else if (event instanceof LinkEvent) { handleLinkEvent((LinkEvent) event); } else if (event instanceof UnlinkEvent) { handleUnlinkEvent((UnlinkEvent) event); } else if (event instanceof RenameEvent) { handleRenameEvent((RenameEvent) event); } else if (event instanceof HangupEvent) { handleHangupEvent((HangupEvent) event); } } protected void addChannel(Channel channel) { channels.put(channel.getId(), channel); } protected void removeChannel(Channel channel) { channels.remove(channel.getId()); } protected void addQueue(Queue queue) { queues.put(queue.getName(), queue); } protected void removeQueue(Queue queue) { queues.remove(queue.getName()); } protected void handleStatusEvent(StatusEvent event) { Channel channel; boolean isNew = false; channel = (Channel) channels.get(event.getUniqueId()); if (channel == null) { channel = new Channel(event.getChannel(), event.getUniqueId()); if (event.getSeconds() != null) { channel .setDateOfCreation(new Date(System.currentTimeMillis() - (event.getSeconds().intValue() * 1000))); } isNew = true; } synchronized (channel) { channel.setCallerId(event.getCallerId()); channel.setAccount(event.getAccount()); channel.setState(ChannelStateEnum.getEnum(event.getState())); channel.setContext(event.getContext()); channel.setExtension(event.getExtension()); channel.setPriority(event.getPriority()); if (event.getLink() != null) { Channel linkedChannel = getChannelByName(event.getLink()); if (linkedChannel != null) { channel.setLinkedChannel(linkedChannel); synchronized (linkedChannel) { linkedChannel.setLinkedChannel(channel); } } } } if (isNew) { log.info("Adding new channel " + channel.getName()); addChannel(channel); } } protected void handleStatusCompleteEvent(StatusCompleteEvent event) { log.info("Channels are now initialized"); this.channelsInitialized = true; } /** * Resets the internal state when the connection to the asterisk server is lost. */ protected void handleDisconnectEvent(DisconnectEvent disconnectEvent) { this.channels.clear(); this.queues.clear(); this.queuedEvents.clear(); this.channelsInitialized = false; //TODO fix detection of queuesInitialized this.queuesInitialized = true; this.initialized = false; } /** * Requests the current state from the asterisk server after the connection to the asterisk * server is restored. */ protected void handleConnectEvent(ConnectEvent connectEvent) { try { connection.sendAction(new StatusAction()); } catch (Exception e) { log.error("Unable to request channel status from asterisk server after reconnect.", e); } try { connection.sendAction(new QueueStatusAction()); } catch (Exception e) { log.error("Unable to request queue status from asterisk server after reconnect.", e); } } protected void handleQueueParamsEvent(QueueParamsEvent event) { Queue queue; boolean isNew = false; queue = (Queue) queues.get(event.getQueue()); if (queue == null) { queue = new Queue(event.getQueue()); isNew = true; } synchronized (queue) { queue.setMax(event.getMax()); } if (isNew) { log.info("Adding new queue " + queue.getName()); addQueue(queue); } } protected void handleQueueMemberEvent(QueueMemberEvent event) { } protected void handleQueueEntryEvent(QueueEntryEvent event) { Queue queue = (Queue) queues.get(event.getQueue()); Channel channel = getChannelByName(event.getChannel()); if (queue == null) { log.error("ignored QueueEntryEvent for unknown queue " + event.getQueue()); return; } if (channel == null) { log.error("ignored QueueEntryEvent for unknown channel " + event.getChannel()); return; } if (!queue.getEntries().contains(channel)) { queue.addEntry(channel); } } /** * Returns a channel by its name. * * @param name name of the channel to return * @return the channel with the given name */ private Channel getChannelByName(String name) { //TODO must get unique Channel channel = null; Iterator channelIterator = channels.values().iterator(); while (channelIterator.hasNext()) { Channel tmp = (Channel) channelIterator.next(); if (tmp.getName() != null && tmp.getName().equals(name)) { channel = tmp; } } return channel; } protected void handleNewChannelEvent(NewChannelEvent event) { Channel channel = new Channel(event.getChannel(), event.getUniqueId()); channel.setCallerId(event.getCallerId()); channel.setState(ChannelStateEnum.getEnum(event.getState())); log.info("Adding channel " + channel.getName()); addChannel(channel); } protected void handleNewExtenEvent(NewExtenEvent event) { Channel channel = (Channel) channels.get(event.getUniqueId()); if (channel == null) { log.error("Ignored NewExtenEvent for unknown channel " + event.getChannel()); return; } synchronized (channel) { channel.setContext(event.getContext()); channel.setExtension(event.getExtension()); channel.setPriority(event.getPriority()); channel.setApplication(event.getApplication()); channel.setAppData(event.getAppData()); } } protected void handleNewStateEvent(NewStateEvent event) { Channel channel = (Channel) channels.get(event.getUniqueId()); if (channel == null) { log.error("Ignored NewStateEvent for unknown channel " + event.getChannel()); return; } channel.setState(ChannelStateEnum.getEnum(event.getState())); } protected void handleHangupEvent(HangupEvent event) { Channel channel = (Channel) channels.get(event.getUniqueId()); if (channel == null) { log.error("Ignored HangupEvent for unknown channel " + event.getChannel()); return; } synchronized (channel) { channel.setState(ChannelStateEnum.HUNGUP); } log.info("Removing channel " + channel.getName() + " due to hangup"); removeChannel(channel); } protected void handleLinkEvent(LinkEvent event) { Channel channel1 = (Channel) channels.get(event.getUniqueId1()); Channel channel2 = (Channel) channels.get(event.getUniqueId2()); if (channel1 == null) { log.error("Ignored LinkEvent for unknown channel " + event.getChannel1()); return; } if (channel2 == null) { log.error("Ignored LinkEvent for unknown channel " + event.getChannel2()); return; } log.info("Linking channels " + channel1.getName() + " and " + channel2.getName()); synchronized (this) { channel1.setLinkedChannel(channel2); channel2.setLinkedChannel(channel1); } } protected void handleUnlinkEvent(UnlinkEvent event) { Channel channel1 = getChannelByName(event.getChannel1()); Channel channel2 = getChannelByName(event.getChannel2()); if (channel1 == null) { log.error("Ignored UnlinkEvent for unknown channel " + event.getChannel1()); return; } if (channel2 == null) { log.error("Ignored UnlinkEvent for unknown channel " + event.getChannel2()); return; } log.info("Unlinking channels " + channel1.getName() + " and " + channel2.getName()); synchronized (channel1) { channel1.setLinkedChannel(null); } synchronized (channel2) { channel2.setLinkedChannel(null); } } protected void handleRenameEvent(RenameEvent event) { Channel channel = (Channel) channels.get(event.getUniqueId()); log.info("Renaming channel '" + channel.getName() + "' to '" + event.getNewname() + "'"); channel.setName(event.getNewname()); } } --- NEW FILE: Channel.java --- /* * (c) 2004 Stefan Reuter * * Created on Oct 29, 2004 */ package net.sf.asterisk.manager; import java.io.Serializable; import java.util.Date; import org.apache.commons.lang.builder.ToStringBuilder; import org.apache.commons.lang.builder.ToStringStyle; /** * @author srt * @version $Id: Channel.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public class Channel implements Serializable { private static final long serialVersionUID = -6919877370396385380L; private AsteriskServer asteriskServer; private String id; private String name; private String callerId; private ChannelStateEnum state; private String account; private String context; private String extension; private Integer priority; private String application; private String appData; private Date dateOfCreation; private Channel linkedChannel; /** * Creates a new channel. * @param name name of this channel, for example "SIP/1310-20da" * @param id unique id of this channel, for example "1099015093.165" */ public Channel(String name, String id) { this.name = name; this.id = id; } public Channel(String name, String id, AsteriskServer server) { this.name = name; this.id = id; this.asteriskServer = server; } /** * @return Returns the asteriskServer. */ public AsteriskServer getAsteriskServer() { return asteriskServer; } /** * @param asteriskServer The asteriskServer to set. */ public void setAsteriskServer(AsteriskServer asteriskServer) { this.asteriskServer = asteriskServer; } /** * Returns the unique id of this channel, for example "1099015093.165". */ public String getId() { return id; } /** * Returns the name of this channel, for example "SIP/1310-20da". */ public String getName() { return name; } /** * Sets the name of this channel. */ public void setName(String name) { this.name = name; } /** * Returns the caller id of this channel. */ public String getCallerId() { return callerId; } /** * Sets the caller id of this channel. */ public void setCallerId(String callerId) { this.callerId = callerId; } /** * Returns the state of this channel. */ public ChannelStateEnum getState() { return state; } /** * Sets the state of this channel. */ public void setState(ChannelStateEnum state) { this.state = state; } public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public String getContext() { return context; } public void setContext(String context) { this.context = context; } public String getExtension() { return extension; } public void setExtension(String extension) { this.extension = extension; } public Integer getPriority() { return priority; } public void setPriority(Integer priority) { this.priority = priority; } public String getApplication() { return application; } public void setApplication(String application) { this.application = application; } public String getAppData() { return appData; } public void setAppData(String appData) { this.appData = appData; } public Date getDateOfCreation() { return dateOfCreation; } public void setDateOfCreation(Date dateOfCreation) { this.dateOfCreation = dateOfCreation; } public Channel getLinkedChannel() { return linkedChannel; } public void setLinkedChannel(Channel linkedChannel) { this.linkedChannel = linkedChannel; } public boolean equals(Object obj) { if (!(obj instanceof Channel)) { return false; } return id.equals(((Channel) obj).getId()); } public int hashCode() { return id.hashCode(); } public String toString() { return ToStringBuilder.reflectionToString(this, ToStringStyle.MULTI_LINE_STYLE); } } --- NEW FILE: ManagerConnection.java --- /* * (c) 2004 Stefan Reuter * * Created on Apr 23, 2004 */ package net.sf.asterisk.manager; import java.io.IOException; import net.sf.asterisk.manager.action.ManagerAction; import net.sf.asterisk.manager.response.ManagerResponse; /** * The main interface to talk to an asterisk server via the manager api. * A concrete implementation of this interface is obtained from a ManagerConnectionFactory. * * @see net.sf.asterisk.manager.ManagerConnectionFactory * * @author srt * @version $Id: ManagerConnection.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public interface ManagerConnection { /** * Logs in to the asterisk server with the username and password specified when this connection * was created. * * @throws IOException * @throws AuthenticationFailedException if the username and/or password are incorrect * or the ChallengeResponse could not be built. * @throws TimeoutException if no response is received within the default timeout period. * If the implementation uses challenge/response this can either be a * timeout of the ChallengeAction or the LoginAction; otherwise it * is always a timeout of the LoginAction. * * @see net.sf.asterisk.manager.action.LoginAction * @see net.sf.asterisk.manager.action.ChallengeAction */ void login() throws IOException, AuthenticationFailedException, TimeoutException; /** * Sends a LogoffAction to the asterisk server and disconnects. * * @throws IOException * @throws TimeoutException if no response to the logoff action is received within the default * timeout period. * * @see net.sf.asterisk.manager.action.LogoffAction */ void logoff() throws IOException, TimeoutException; /** * Returns the protocol identifier, i.e. a string like "Asterisk Call Manager/1.0". * * @return the protocol identifier of the asterisk manager interface in use if it has already * been received; <code>null</code> otherwise */ String getProtocolIdentifier(); /** * Sends a ManagerAction to the asterisk manager interface and waits for the * corresponding ManagerResponse. * * @param action the action to send to the asterisk server * * @return the corresponding response received from the asterisk server * * @throws IOException * @throws TimeoutException if no reponse is received within the default timeout period. * * @see #sendAction(ManagerAction, long) * @see #sendAction(ManagerAction, ManagerResponseHandler) */ ManagerResponse sendAction(ManagerAction action) throws IOException, TimeoutException; /** * Sends a ManagerAction to the asterisk manager interface and waits for the * corresponding {@link ManagerResponse}. * * @param action the action to send to the asterisk server * @param timeout milliseconds to wait for the reponse before throwing a TimeoutException * * @return the corresponding response received from the asterisk server * * @throws IOException * @throws TimeoutException if no reponse is received within the given timeout period. * * @see #sendAction(ManagerAction, ManagerResponseHandler) */ ManagerResponse sendAction(ManagerAction action, long timeout) throws IOException, TimeoutException; /** * Sends a ManagerAction to the asterisk manager interface and registers a callback handler * to be called when the corresponding ManagerResponse is received. * * @param action the action to send to the asterisk server * @param callbackHandler the callback handler to call when the response is received * * @throws IOException */ void sendAction(ManagerAction action, ManagerResponseHandler callbackHandler) throws IOException; /** * Registers an event handler to be called whenever an {@link net.sf.asterisk.manager.event.ManagerEvent} * is receiced from the asterisk server. * * @param eventHandler the handler to call whenever a manager event is received * * @see #removeEventHandler(ManagerEventHandler) */ void addEventHandler(ManagerEventHandler eventHandler); /** * Unregisters a previously registered event handler.<br> * Does nothing if the given event handler hasn't be been regiered before. * * @param eventHandler the event handle to unregister * * @see #addEventHandler(ManagerEventHandler) */ void removeEventHandler(ManagerEventHandler eventHandler); /** * Return Asterisk server of connection * */ AsteriskServer getAsteriskServer(); } --- NEW FILE: AuthenticationFailedException.java --- /* * (c) 2004 Stefan Reuter * * Created on Apr 22, 2004 */ package net.sf.asterisk.manager; /** * An AuthenticationFailedException is thrown when a login fails due to an * incorrect username and/or password. * * @author srt * @version $Id: AuthenticationFailedException.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public class AuthenticationFailedException extends Exception { static final long serialVersionUID = 7674248607624140309L; public AuthenticationFailedException(String message) { super(message); } public AuthenticationFailedException(String message, Throwable cause) { super(message, cause); } } --- NEW FILE: AsteriskServer.java --- /* * Created on 3 févr. 2005 by Pierre-Yves ROGER. * */ package net.sf.asterisk.manager; import java.io.Serializable; /** * Represents an asterisk server that is connected via the manager API. * * @author PY * @version $Id: AsteriskServer.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public class AsteriskServer implements Serializable { private static final long serialVersionUID = 3257284738393125176L; private String hostname = "localhost"; private int port = 5038; public AsteriskServer() { } public AsteriskServer(String hostname, int port) { this.hostname = hostname; this.port = port; } /** * @return Returns the hostname. */ public String getHostname() { return hostname; } /** * @param hostname The hostname to set. */ public void setHostname(String hostname) { this.hostname = hostname; } /** * @return Returns the port. */ public int getPort() { return port; } /** * @param port The port to set. */ public void setPort(int port) { this.port = port; } } --- NEW FILE: EventBuilder.java --- /* * (c) 2004 Stefan Reuter * * Created on Sep 24, 2004 */ package net.sf.asterisk.manager; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import net.sf.asterisk.manager.event.AgentCallbackLoginEvent; import net.sf.asterisk.manager.event.AgentCallbackLogoffEvent; import net.sf.asterisk.manager.event.AgentCalledEvent; import net.sf.asterisk.manager.event.AgentLoginEvent; import net.sf.asterisk.manager.event.AgentLogoffEvent; import net.sf.asterisk.manager.event.AlarmClearEvent; import net.sf.asterisk.manager.event.AlarmEvent; import net.sf.asterisk.manager.event.CdrEvent; import net.sf.asterisk.manager.event.ExtensionStatusEvent; import net.sf.asterisk.manager.event.HangupEvent; import net.sf.asterisk.manager.event.HoldedCallEvent; import net.sf.asterisk.manager.event.JoinEvent; import net.sf.asterisk.manager.event.LeaveEvent; import net.sf.asterisk.manager.event.LinkEvent; import net.sf.asterisk.manager.event.ManagerEvent; import net.sf.asterisk.manager.event.MeetMeJoinEvent; import net.sf.asterisk.manager.event.MeetMeLeaveEvent; import net.sf.asterisk.manager.event.MessageWaitingEvent; import net.sf.asterisk.manager.event.NewCallerIdEvent; import net.sf.asterisk.manager.event.NewChannelEvent; import net.sf.asterisk.manager.event.NewExtenEvent; import net.sf.asterisk.manager.event.NewStateEvent; import net.sf.asterisk.manager.event.OriginateFailureEvent; import net.sf.asterisk.manager.event.OriginateSuccessEvent; import net.sf.asterisk.manager.event.ParkedCallEvent; import net.sf.asterisk.manager.event.ParkedCallsCompleteEvent; import net.sf.asterisk.manager.event.PeerStatusEvent; import net.sf.asterisk.manager.event.QueueEntryEvent; import net.sf.asterisk.manager.event.QueueMemberEvent; import net.sf.asterisk.manager.event.QueueParamsEvent; import net.sf.asterisk.manager.event.RegistryEvent; import net.sf.asterisk.manager.event.ReloadEvent; import net.sf.asterisk.manager.event.RenameEvent; import net.sf.asterisk.manager.event.ResponseEvent; import net.sf.asterisk.manager.event.ShutdownEvent; import net.sf.asterisk.manager.event.StatusCompleteEvent; import net.sf.asterisk.manager.event.StatusEvent; import net.sf.asterisk.manager.event.UnlinkEvent; import net.sf.asterisk.manager.event.ZapShowChannelsCompleteEvent; import net.sf.asterisk.manager.event.ZapShowChannelsEvent; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * @author srt * @version $Id: EventBuilder.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public class EventBuilder { private final Log logger = LogFactory.getLog(getClass()); private Map registeredEventClasses; public EventBuilder() { this.registeredEventClasses = new HashMap(); registerBuiltinEventClasses(); } private void registerBuiltinEventClasses() { registerEventClass(AgentCallbackLoginEvent.class); registerEventClass(AgentCallbackLogoffEvent.class); registerEventClass(AgentCalledEvent.class); registerEventClass(AgentLoginEvent.class); registerEventClass(AgentLogoffEvent.class); registerEventClass(AlarmEvent.class); registerEventClass(AlarmClearEvent.class); registerEventClass(CdrEvent.class); registerEventClass(ExtensionStatusEvent.class); registerEventClass(HangupEvent.class); registerEventClass(HoldedCallEvent.class); registerEventClass(JoinEvent.class); registerEventClass(LeaveEvent.class); registerEventClass(LinkEvent.class); registerEventClass(MeetMeJoinEvent.class); registerEventClass(MeetMeLeaveEvent.class); registerEventClass(MessageWaitingEvent.class); registerEventClass(NewCallerIdEvent.class); registerEventClass(NewChannelEvent.class); registerEventClass(NewExtenEvent.class); registerEventClass(NewStateEvent.class); registerEventClass(OriginateFailureEvent.class); registerEventClass(OriginateSuccessEvent.class); registerEventClass(ParkedCallEvent.class); registerEventClass(ParkedCallsCompleteEvent.class); registerEventClass(PeerStatusEvent.class); registerEventClass(QueueEntryEvent.class); registerEventClass(QueueMemberEvent.class); registerEventClass(QueueParamsEvent.class); registerEventClass(RegistryEvent.class); registerEventClass(ReloadEvent.class); registerEventClass(RenameEvent.class); registerEventClass(ShutdownEvent.class); registerEventClass(StatusEvent.class); registerEventClass(StatusCompleteEvent.class); registerEventClass(UnlinkEvent.class); registerEventClass(ZapShowChannelsEvent.class); registerEventClass(ZapShowChannelsCompleteEvent.class); } /** * Registers a new event class. The event this class is registered for is simply derived * from the name of the class by stripping any package name (if present) and stripping * the sufffix "Event". * For example <code>net.sf.asterisk.manager.event.JoinEvent</code> is registered for the * event "Join". * @param clazz the event class to register, must extend net.sf.asterisk.manager.event.Event. */ public void registerEventClass(Class clazz) { String className; String eventType; className = clazz.getName(); eventType = className.substring(className.lastIndexOf('.') + 1).toLowerCase(); if (eventType.endsWith("event")) { eventType = eventType.substring(0, eventType.length() - "event".length()); } registerEventClass(eventType, clazz); } /** * Registers a new event class for the event given by eventType. * @param eventType the name of the event to register the class for. For example "Join". * @param clazz the event class to register, must extend net.sf.asterisk.manager.event.Event. */ public void registerEventClass(String eventType, Class clazz) { Constructor defaultConstructor; if (!ManagerEvent.class.isAssignableFrom(clazz)) { throw new IllegalArgumentException(clazz + " is not a ManagerEvent"); } if ((clazz.getModifiers() & Modifier.ABSTRACT) != 0) { throw new IllegalArgumentException(clazz + " is abstract"); } try { defaultConstructor = clazz.getConstructor(new Class[] { Object.class }); } catch (NoSuchMethodException ex) { throw new IllegalArgumentException(clazz + " has no usable constructor"); } if ((defaultConstructor.getModifiers() & Modifier.PUBLIC) == 0) { throw new IllegalArgumentException(clazz + " has no public default constructor"); } registeredEventClasses.put(eventType.toLowerCase(), clazz); logger.info("Registered event type '" + eventType + "' (" + clazz + ")"); } public ManagerEvent buildEvent(Object source, Map attributes) { ManagerEvent event; String eventType; Class eventClass; Constructor constructor; if (attributes.get("event") == null) { logger.error("No event event type in properties"); return null; } eventType = ((String) attributes.get("event")).toLowerCase(); eventClass = (Class) registeredEventClasses.get(eventType); if (eventClass == null) { logger.warn("No event class registered for event type '" + eventType + "', attributes: " + attributes); return null; } try { constructor = eventClass.getConstructor(new Class[] { Object.class }); } catch (NoSuchMethodException ex) { logger.error("Unable to get constructor of " + eventClass, ex); return null; } try { event = (ManagerEvent) constructor.newInstance(new Object[] { source }); } catch (InstantiationException ex) { logger.error("Unable to create new instance of " + eventClass, ex); return null; } catch (IllegalAccessException ex) { logger.error("Unable to create new instance of " + eventClass, ex); return null; } catch (InvocationTargetException ex) { logger.error("Unable to create new instance of " + eventClass, ex); return null; } setAttributes(event, attributes); if (event instanceof ResponseEvent) { ResponseEvent responseEvent; String actionId; responseEvent = (ResponseEvent) event; actionId = responseEvent.getActionId(); if (actionId != null) { responseEvent.setActionId(Util.stripInternalActionId(actionId)); } } return event; } private void setAttributes(ManagerEvent event, Map attributes) { Map setters; setters = getSetters(event.getClass()); Iterator i = attributes.keySet().iterator(); while (i.hasNext()) { String name; Object value; Class dataType; Method setter; name = (String) i.next(); if ("event".equals(name)) { continue; } /* * The source property needs special handling as it is already defined * in java.util.EventObject (the base class of ManagerEvent), so we * have to translate it. */ if ("source".equals(name)) { setter = (Method) setters.get("src"); } else { setter = (Method) setters.get(name); } if (setter == null) { logger.error("Unable to set property '" + name + "' on " + event.getClass() + ": no setter"); continue; } dataType = setter.getParameterTypes()[0]; if (dataType.isAssignableFrom(String.class)) { value = attributes.get(name); } else { try { Constructor constructor = dataType.getConstructor(new Class[] { String.class }); value = constructor.newInstance(new Object[] { attributes.get(name) }); } catch (Exception e) { logger.error("Unable to convert value '" + attributes.get(name) + "' of property '" + name + "' on " + event.getClass() + " to required type " + dataType, e); continue; } } try { setter.invoke(event, new Object[] { value }); } catch (Exception e) { logger.error("Unable to set property '" + name + "' on " + event.getClass(), e); continue; } } } private Map getSetters(Class clazz) { Map accessors = new HashMap(); Method[] methods = clazz.getMethods(); for (int i = 0; i < methods.length; i++) { String name; String methodName; Method method = methods[i]; methodName = method.getName(); if (!methodName.startsWith("set")) { continue; } // skip non public methods if ((method.getModifiers() & Modifier.PUBLIC) == 0) { continue; } // skip methods with != 1 parameters if (method.getParameterTypes().length != 1) { continue; } // ok seems to be an accessor name = methodName.substring("set".length()).toLowerCase(); accessors.put(name, method); } return accessors; } } --- NEW FILE: Queue.java --- /* * (c) 2004 Stefan Reuter * * Created on Oct 31, 2004 */ package net.sf.asterisk.manager; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * @author srt * @version $Id: Queue.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public class Queue implements Serializable { private static final long serialVersionUID = -6597536667933738312L; private String name; private Integer max; private List entries; public Queue(String name) { this.name = name; this.entries = Collections.synchronizedList(new ArrayList()); } public String getName() { return name; } public Integer getMax() { return max; } public void setMax(Integer max) { this.max = max; } public List getEntries() { return entries; } public void addEntry(Channel entry) { entries.add(entry); } public void removeEntry(Channel entry) { entries.remove(entry); } } --- NEW FILE: ManagerReader.java --- /* * (c) 2004 Stefan Reuter * * Created on Apr 22, 2004 */ package net.sf.asterisk.manager; import java.io.BufferedReader; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import net.sf.asterisk.manager.event.ManagerEvent; import net.sf.asterisk.manager.response.ChallengeResponse; import net.sf.asterisk.manager.response.CommandResponse; import net.sf.asterisk.manager.response.ExtensionStateResponse; import net.sf.asterisk.manager.response.MailboxCountResponse; import net.sf.asterisk.manager.response.MailboxStatusResponse; import net.sf.asterisk.manager.response.ManagerError; import net.sf.asterisk.manager.response.ManagerResponse; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * The ManagerReader is a Thread that reads events and responses from an asterisk * server, parses them to the corresponding ManagerEvent and ManagerResponse objects and * dispatches them via the associated DefaultManagerConnection. * <br> * This class is intended to be used only by the DefaultManagerConnection. * * @see net.sf.asterisk.manager.event.ManagerEvent * @see net.sf.asterisk.manager.response.ManagerResponse * @see net.sf.asterisk.manager.DefaultManagerConnection * * @author srt * @version $Id: ManagerReader.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ class ManagerReader extends Thread { private Log log = LogFactory.getLog(getClass()); private static int threadCount = 0; private EventBuilder eventBuilder; private boolean die = false; private BufferedReader reader; private DefaultManagerConnection connection; ManagerReader(DefaultManagerConnection connection, BufferedReader reader) { int i = threadCount++; setName("ManagerReader-" + i); this.connection = connection; this.reader = reader; this.eventBuilder = new EventBuilder(); } /** * Reads line by line from the asterisk server, sets the protocol identifier * as soon as it is received and dispatches the received * events and responses via the associated connection. * * @see DefaultManagerConnection#dispatchEvent(ManagerEvent) * @see DefaultManagerConnection#dispatchResponse(ManagerResponse) * @see DefaultManagerConnection#setProtocolIdentifier(String) */ public void run() { String line; List commandResult = new ArrayList(); boolean processingCommandResult = false; Map buffer = new HashMap(); try { while ((line = reader.readLine()) != null && !die) { // dirty hack for handling the CommandAction. Needs fix when manager protocol is enhanced. if (processingCommandResult) { if ("--END COMMAND--".equals(line)) { CommandResponse commandResponse = new CommandResponse(); if (!commandResult.isEmpty() && ((String) commandResult.get(0)).startsWith("ActionID")) { String tmp = (String) commandResult.get(0); int delimiterIndex = tmp.indexOf(":"); if (delimiterIndex > 0 && tmp.length() > delimiterIndex + 2) { commandResult.remove(0); commandResponse.setActionId(tmp.substring(delimiterIndex + 2)); } } commandResponse.setResponse("Follows"); commandResponse.setDateReceived(new Date()); commandResponse.setResult(commandResult); connection.dispatchResponse(commandResponse); commandResult = new ArrayList(); processingCommandResult = false; } else { commandResult.add(line); } continue; } if ("Response: Follows".equalsIgnoreCase(line)) { processingCommandResult = true; continue; } // maybe we will find a better way to identify the protocol identifier but for now // this works quite well. if (line.startsWith("Asterisk Call Manager/") && connection.getProtocolIdentifier() == null) { connection.setProtocolIdentifier(line); continue; } // an empty line indicates a normal response's or event's end so we build // the corresponding value object and dispatch it through the ManagerConnection. if (line.length() == 0) { if (buffer.containsKey("response")) { ManagerResponse response = buildResponse(buffer); if (response != null) { connection.dispatchResponse(response); } } else if (buffer.containsKey("event")) { ManagerEvent event = buildEvent(connection.getAsteriskServer(), buffer); if (event != null) { connection.dispatchEvent(event); } } buffer.clear(); } else { int delimiterIndex; delimiterIndex = line.indexOf(":"); if (delimiterIndex > 0 && line.length() > delimiterIndex + 2) { String name; String value; name = line.substring(0, delimiterIndex).toLowerCase(); value = line.substring(delimiterIndex + 2); buffer.put(name, value); } } } connection.handleDisconnection(); } catch (IOException e) { log.warn("IOException while reading from asterisk server, terminating thread.", e); } finally { log.info("Disconnected, closing reader"); try { reader.close(); } catch (IOException ex) { log.warn("Unable to close reader", ex); } } } public void die() { this.die = true; } private ManagerResponse buildResponse(Map buffer) { ManagerResponse response; String responseType = (String) buffer.get("response"); // determine type if ("error".equals(responseType)) { response = new ManagerError(); } else if (buffer.containsKey("challenge")) { ChallengeResponse challengeResponse = new ChallengeResponse(); challengeResponse.setChallenge((String) buffer.get("challenge")); response = challengeResponse; } else if (buffer.containsKey("mailbox") && buffer.containsKey("waiting")) { MailboxStatusResponse mailboxStatusResponse = new MailboxStatusResponse(); mailboxStatusResponse.setMailbox((String) buffer.get("mailbox")); mailboxStatusResponse.setWaiting(new Integer((String) buffer.get("mailbox"))); response = mailboxStatusResponse; } else if (buffer.containsKey("mailbox") && buffer.containsKey("newmessages") && buffer.containsKey("oldmessages")) { MailboxCountResponse mailboxCountResponse = new MailboxCountResponse(); mailboxCountResponse.setMailbox((String) buffer.get("mailbox")); mailboxCountResponse.setNewMessages(new Integer((String) buffer.get("newmessages"))); mailboxCountResponse.setOldMessages(new Integer((String) buffer.get("oldmessages"))); response = mailboxCountResponse; } else if (buffer.containsKey("exten") && buffer.containsKey("context") && buffer.containsKey("hint") && buffer.containsKey("status")) { ExtensionStateResponse extensionStateResponse = new ExtensionStateResponse(); extensionStateResponse.setExten((String) buffer.get("exten")); extensionStateResponse.setContext((String) buffer.get("context")); extensionStateResponse.setHint((String) buffer.get("hint")); extensionStateResponse.setStatus(new Integer((String) buffer.get("status"))); response = extensionStateResponse; } else { response = new ManagerResponse(); } // fill known attributes response.setResponse(responseType); if (buffer.containsKey("actionid")) { response.setActionId((String) buffer.get("actionid")); } if (buffer.containsKey("message")) { response.setMessage((String) buffer.get("message")); } if (buffer.containsKey("uniqueid")) { response.setUniqueId((String) buffer.get("uniqueid")); } response.setDateReceived(new Date()); return response; } private ManagerEvent buildEvent(Object source, Map buffer) { ManagerEvent event; event = this.eventBuilder.buildEvent(source, buffer); if (event != null) { event.setDateReceived(new Date()); } return event; } } --- NEW FILE: ChannelStateEnum.java --- /* * (c) 2004 Stefan Reuter * * Created on Oct 29, 2004 */ package net.sf.asterisk.manager; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.lang.enums.Enum; /** * Enumeration that represents the state of a channel. * * @author srt * @version $Id: ChannelStateEnum.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public class ChannelStateEnum extends Enum { private static final long serialVersionUID = 6436381500165485011L; public static final ChannelStateEnum DOWN = new ChannelStateEnum("Down"); public static final ChannelStateEnum OFF_HOOK = new ChannelStateEnum("OffHook"); public static final ChannelStateEnum DIALING = new ChannelStateEnum("Dialing"); public static final ChannelStateEnum RING = new ChannelStateEnum("Ring"); public static final ChannelStateEnum RINGING = new ChannelStateEnum("Ringing"); public static final ChannelStateEnum UP = new ChannelStateEnum("Up"); public static final ChannelStateEnum BUSY = new ChannelStateEnum("Busy"); public static final ChannelStateEnum RSRVD = new ChannelStateEnum("Rsrvd"); public static final ChannelStateEnum HUNGUP = new ChannelStateEnum("Hungup"); private ChannelStateEnum(String state) { super(state); } public static ChannelStateEnum getEnum(String state) { return (ChannelStateEnum) getEnum(ChannelStateEnum.class, state); } public static Map getEnumMap() { return getEnumMap(ChannelStateEnum.class); } public static List getEnumList() { return getEnumList(ChannelStateEnum.class); } public static Iterator iterator() { return iterator(ChannelStateEnum.class); } } --- NEW FILE: ManagerConnectionFactory.java --- /* * (c) 2004 Stefan Reuter * * Created on Apr 23, 2004 */ package net.sf.asterisk.manager; import java.io.IOException; /** * This factory is used to obtain new ManagerConnections. * * @see net.sf.asterisk.manager.ManagerConnection * * @author srt * @version $Id: ManagerConnectionFactory.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public class ManagerConnectionFactory { private static final String DEFAULT_HOSTNAME = "localhost"; private static final int DEFAULT_PORT = 5038; /** * Creates a new ManagerConnectionFactory. */ public ManagerConnectionFactory() { } /** * Returns a new ManagerConnection to an asterisk server running on localhost with * the call manager interface listening on its default port 5038. * * @param username the username as specified in asterisk's <code>manager.conf</code> * @param password the password as specified in asterisk's <code>manager.conf</code> * * @return the created connection to the asterisk call manager * * @throws IOException if the connection cannot be established. */ public ManagerConnection getManagerConnection(String username, String password) throws IOException { return new DefaultManagerConnection(DEFAULT_HOSTNAME, DEFAULT_PORT, username, password); } /** * Returns a new ManagerConnection to an asterisk server running on given host with * the call manager interface listening on its default port 5038. * * @param hostname the name of the host the asterisk server is running on * @param username the username as specified in asterisk's <code>manager.conf</code> * @param password the password as specified in asterisk's <code>manager.conf</code> * * @return the created connection to the asterisk call manager * * @throws IOException if the connection cannot be established. */ public ManagerConnection getManagerConnection(String hostname, String username, String password) throws IOException { return new DefaultManagerConnection(hostname, DEFAULT_PORT, username, password); } /** * Returns a new ManagerConnection to an asterisk server running on given host with * the call manager interface listening on the given port. * * @param hostname the name of the host the asterisk server is running on * @param port the port the call manager interface is listening on * @param username the username as specified in asterisk's <code>manager.conf</code> * @param password the password as specified in asterisk's <code>manager.conf</code> * * @return the created connection to the asterisk call manager * * @throws IOException if the connection cannot be established. */ public ManagerConnection getManagerConnection(String hostname, int port, String username, String password) throws IOException { return new DefaultManagerConnection(hostname, port, username, password); } } --- NEW FILE: Util.java --- /* * (c) 2004 Stefan Reuter * * Created on Apr 23, 2004 */ package net.sf.asterisk.manager; /** * Utilitiy class with some static helper methods that are used in multiple * contexts. * * @author srt * @version $Id: Util.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public class Util { /** * The hex digits used to build a hex string representation of a byte array. */ private static final char[] hexChar = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * Converts a byte array to a hex string representing it. The hex digits are lower case. * * @param b the byte array to convert * * @return the hex representation of b */ public static String toHexString(byte[] b) { StringBuffer sb = new StringBuffer(b.length * 2); for (int i = 0; i < b.length; i++) { sb.append(hexChar[(b[i] & 0xf0) >>> 4]); sb.append(hexChar[b[i] & 0x0f]); } return sb.toString(); } public static String getInternalActionId(String actionId) { int delimiterIndex; delimiterIndex = actionId.indexOf('-'); if (delimiterIndex > 0) { return actionId.substring(0, delimiterIndex); } else { return null; } } public static String stripInternalActionId(String actionId) { int delimiterIndex; delimiterIndex = actionId.indexOf('-'); if (delimiterIndex > 0) { if (actionId.length() > delimiterIndex + 1) { return actionId.substring(delimiterIndex + 1); } else { return null; } } else { return null; } } } --- NEW FILE: TimeoutException.java --- /* * (c) 2004 Stefan Reuter * * Created on Apr 22, 2004 */ package net.sf.asterisk.manager; /** * A TimeoutException is thrown if a ManagerResponse is not * received within the expected time period. * * @author srt * @version $Id: TimeoutException.java,v 1.1 2005/02/23 12:45:20 srt Exp $ */ public class TimeoutException extends Exception { static final long serialVersionUID = 7674248607624140309L; public TimeoutException(String message) { super(message); } } --- NEW FILE: MultiAsterisksManager.java --- /* * Created on 1 févr. 2005 by Pierre-Yves ROGER. * */ package net.sf.asterisk.manager; import java.io.IOException; import java.util.*; import net.sf.asterisk.manager.action.*; import net.sf.asterisk.manager.event.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; /** * @author PY */ public class MultiAsterisksManager implements AsteriskManager, ManagerEventHandler { private final Log log = LogFactory.getLog(this.getClass()); /** * A map of all Asterisk servers connections by their unique hostname. */ private Map managerConnections; /** * A map of all active channel by theirs hostname of connection and unique id. * keyMap -> "hostname:uniqueId" */ private Map channels; private Map queues; private List queuedEvents; /** * A map of all "Initiallize flag" per connection. * keyMap -> "hostname" */ private Map initialized; public MultiAsterisksManager() { this.channels = Collections.synchronizedMap(new HashMap()); this.queues = Collections.synchronizedMap(new HashMap()); this.queuedEvents = Collections.synchronizedList(new ArrayList()); this.managerConnections = Collections.synchronizedMap(new HashMap()); this.initialized = Collections.synchronizedMap(new HashMap()); } public void addManagerConnection(ManagerConnection connection) { managerConnections.put(connection.getAsteriskServer(), connection); Hashtable initializedBoolean = new Hashtable(3, 1); initializedBoolean.put("channelsInitialized", new Boolean(false)); // TODO fix detection of queuesInitialized initializedBoolean.put("queuesInitialized", new Boolean(true)); initializedBoolean.put("initialized", new Boolean(false)); initialized.put(connection.getAsteriskServer(), initializedBoolean); } public void initialize() throws TimeoutException, IOException, AuthenticationFailedException { Iterator iterConnect = managerConnections.values().iterator(); while (iterConnect.hasNext()) { ManagerConnection connection = (ManagerConnection) iterConnect.next(); connection.addEventHandler(this); connection.login(); connection.sendAction(new StatusAction()); connection.sendAction(new QueueStatusAction()); } } /** * Returns a map of all active channel by their unique id: "hostname:ChannelId". */ public Map getChannels() { return channels; } public Map getQueues() { return queues; } /** * Handles all events received from the asterisk server... [truncated message content] |