/******************************************************************************* * This file is part of OpenNMS(R). * * Copyright (C) 2009-2011 The OpenNMS Group, Inc. * OpenNMS(R) is Copyright (C) 1999-2011 The OpenNMS Group, Inc. * * OpenNMS(R) is a registered trademark of The OpenNMS Group, Inc. * * OpenNMS(R) is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published * by the Free Software Foundation, either version 3 of the License, * or (at your option) any later version. * * OpenNMS(R) is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with OpenNMS(R). If not, see: * http://www.gnu.org/licenses/ * * For more information contact: * OpenNMS(R) Licensing <license@opennms.org> * http://www.opennms.org/ * http://www.opennms.com/ *******************************************************************************/ package org.opennms.netmgt.asterisk.utils; import java.io.IOException; import java.net.InetAddress; import java.net.UnknownHostException; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.Properties; import org.asteriskjava.manager.AuthenticationFailedException; import org.asteriskjava.manager.DefaultManagerConnection; import org.asteriskjava.manager.ManagerConnectionFactory; import org.asteriskjava.manager.TimeoutException; import org.asteriskjava.manager.action.OriginateAction; import org.asteriskjava.manager.response.ManagerResponse; import org.exolab.castor.xml.MarshalException; import org.exolab.castor.xml.ValidationException; import org.opennms.core.utils.InetAddressUtils; import org.opennms.core.utils.PropertiesUtils; import org.opennms.core.utils.ThreadCategory; import org.opennms.netmgt.config.AmiPeerFactory; import org.opennms.protocols.ami.AmiAgentConfig; /** * Originates a call using the Asterisk Manager API * * @author <A HREF="mailto:jeffg@opennms.org">Jeff Gehlbach</A> * @version $Id: $ */ public class AsteriskOriginator { private static final String DEFAULT_AMI_HOST = "127.0.0.1"; private static final boolean DEFAULT_ORIGINATOR_DEBUG = true; private static final long DEFAULT_RESPONSE_TIMEOUT = 10000; private static final String DEFAULT_LEGA_CALLER_ID = "OpenNMS<9195551212>"; private static final long DEFAULT_LEGA_TIMEOUT = 30000; private static final String DEFAULT_LEGA_CHANNEL_PATTERN = "Local/${exten}@default"; private static final String DEFAULT_LEGB_CONTEXT = "default"; private static final String DEFAULT_LEGB_EXTENSION = "noc"; private static final int DEFAULT_LEGB_PRIORITY = 1; // private static final String DEFAULT_LEGB_APP = "Playback"; // private static final String DEFAULT_LEGB_APP_DATA = "tt-monkeysintro"; private DefaultManagerConnection m_managerConnection = null; private OriginateAction m_originateAction = null; private ManagerResponse m_managerResponse = null; /* * properties from configuration */ private Properties m_amiProps; /* * fields from properties used for deterministic behavior of the originator */ private boolean m_debug; private long m_responseTimeout; private InetAddress m_amiHost; private String m_legAChannelPattern; private String m_callerId; private long m_dialTimeout; private String m_legBContext; private int m_legBPriority; private String m_legBExtension; private boolean m_legBIsApp; private String m_legBAppPattern; private String m_legBAppDataPattern; private String m_legBApp; private String m_legBAppData; /* * Basic call fields */ private String m_legAExtension; private String m_legAChannel; private String m_subject; private String m_messageText; /* * A Map for setting channel variables */ private Map<String,String> m_channelVars; /** * <p>Constructor for AsteriskOriginator.</p> * * @param amiProps a {@link java.util.Properties} object. * @throws org.opennms.netmgt.asterisk.utils.AsteriskOriginatorException if any. */ public AsteriskOriginator(Properties amiProps) throws AsteriskOriginatorException { try { configureProperties(amiProps); } catch (IOException e) { throw new AsteriskOriginatorException("Failed to construct originator", e); } // Get the details for this AMI peer from the AmiPeerFactory try { AmiPeerFactory.init(); } catch (MarshalException me) { throw new AsteriskOriginatorException("Failed to unmarshal AMI peer factory configuration", me); } catch (ValidationException ve) { throw new AsteriskOriginatorException("Failed to validate AMI peer factory configuration", ve); } catch (IOException ioe) { throw new AsteriskOriginatorException("I/O error initializing AMI peer factory", ioe); } AmiAgentConfig agentConfig = AmiPeerFactory.getInstance().getAgentConfig(m_amiHost); // Now create and configure the manager connection ManagerConnectionFactory mcf = new ManagerConnectionFactory(InetAddressUtils.str(m_amiHost), agentConfig.getPort(), agentConfig.getUsername(), agentConfig.getPassword()); if (agentConfig.getUseTls()) { m_managerConnection = (DefaultManagerConnection)mcf.createSecureManagerConnection(); } else { m_managerConnection = (DefaultManagerConnection)mcf.createManagerConnection(); } m_managerConnection.setDefaultResponseTimeout(m_responseTimeout); m_channelVars = new HashMap<String,String>(); } /** * Default constructor. Default properties from asterisk-properties are set into session. * * @throws org.opennms.netmgt.asterisk.utils.AsteriskOriginatorException if any. */ public AsteriskOriginator() throws AsteriskOriginatorException { this(new Properties()); } /** * This method uses a properties file reader to pull in opennms styled AMI properties and sets * the actual AMi properties. This is here to preserve the backwards compatibility but configuration * will probably change soon. * * @throws IOException */ private void configureProperties(Properties amiProps) throws IOException { //this loads the OpenNMS-defined properties m_amiProps = AsteriskConfig.getProperties(); //this sets any Asterisk-Java defined properties sent in to the constructor m_amiProps.putAll(amiProps); /* * fields from properties used for deterministic behavior of the originator */ m_debug = PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.debug", DEFAULT_ORIGINATOR_DEBUG); m_responseTimeout = PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.responsetimeout", DEFAULT_RESPONSE_TIMEOUT); m_amiHost = InetAddressUtils.addr(PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.amiHost", DEFAULT_AMI_HOST)); m_legAChannelPattern = PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.legachannel", DEFAULT_LEGA_CHANNEL_PATTERN); m_callerId = PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.legacallerid", DEFAULT_LEGA_CALLER_ID); m_dialTimeout = PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.legadialtimeout", DEFAULT_LEGA_TIMEOUT); m_legBContext = PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.legbcontext", DEFAULT_LEGB_CONTEXT); m_legBExtension = PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.legbextension", DEFAULT_LEGB_EXTENSION); String legBPriorityStr = PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.legbpriority", new Integer(DEFAULT_LEGB_PRIORITY).toString()); m_legBPriority = Integer.parseInt(legBPriorityStr); m_legBAppPattern = PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.legbapp", null); m_legBAppDataPattern = PropertiesUtils.getProperty(m_amiProps, "org.opennms.asterisk.originate.legbappdata", null); m_legBIsApp = (m_legBAppPattern != null && ! "".equals(m_legBAppPattern)); } /** * Originates a call based on properties set on this bean. * * @throws org.opennms.netmgt.asterisk.utils.AsteriskOriginatorException if any. */ public void originateCall() throws AsteriskOriginatorException { m_originateAction = buildOriginateAction(); log().info("Logging in Asterisk manager connection"); try { m_managerConnection.login(); } catch (IllegalStateException ise) { throw new AsteriskOriginatorException("Illegal state logging in Asterisk manager connection", ise); } catch (IOException ioe) { throw new AsteriskOriginatorException("I/O exception logging in Asterisk manager connection", ioe); } catch (AuthenticationFailedException afe) { throw new AsteriskOriginatorException("Authentication failure logging in Asterisk manager connection", afe); } catch (TimeoutException toe) { throw new AsteriskOriginatorException("Timed out logging in Asterisk manager connection", toe); } log().info("Successfully logged in Asterisk manager connection"); log().info("Originating a call to extension " + m_legAExtension); if (log().isDebugEnabled()) { log().debug(createCallLogMsg()); log().debug("Originate action:\n\n" + m_originateAction.toString()); } try { m_managerResponse = m_managerConnection.sendAction(m_originateAction); } catch (IllegalArgumentException iae) { m_managerConnection.logoff(); throw new AsteriskOriginatorException("Illegal argument sending originate action", iae); } catch (IllegalStateException ise) { m_managerConnection.logoff(); throw new AsteriskOriginatorException("Illegal state sending originate action", ise); } catch (IOException ioe) { m_managerConnection.logoff(); throw new AsteriskOriginatorException("I/O exception sending originate action", ioe); } catch (TimeoutException toe) { m_managerConnection.logoff(); throw new AsteriskOriginatorException("Timed out sending originate action", toe); } log().info("Asterisk manager responded: " + m_managerResponse.getResponse()); log().info("Asterisk manager message: " + m_managerResponse.getMessage()); if (m_managerResponse.getResponse().toLowerCase().startsWith("error")) { m_managerConnection.logoff(); throw new AsteriskOriginatorException("Got error response sending originate event. Response: " + m_managerResponse.getResponse() + "; Message: " + m_managerResponse.getMessage()); } log().info("Logging off Asterisk manager connection"); m_managerConnection.logoff(); log().info("Successfully logged off Asterisk manager connection"); } /** * Build a complete OriginateAction ready for dispatching. * * @return completed OriginateAction, ready to be passed to ManagerConnection.sendAction * @throws org.opennms.netmgt.asterisk.utils.AsteriskOriginatorException if any of the underlying operations fail */ public OriginateAction buildOriginateAction() throws AsteriskOriginatorException { OriginateAction action = new OriginateAction(); action.setCallerId(m_callerId); setLegAChannel(expandPattern(m_legAChannelPattern)); action.setChannel(getLegAChannel()); action.setTimeout(m_dialTimeout); action.setCallerId(m_callerId); if (m_legBIsApp) { action.setApplication(expandPattern(m_legBAppPattern)); action.setData(expandPattern(m_legBAppDataPattern)); } else { action.setContext(m_legBContext); action.setExten(m_legBExtension); action.setPriority(m_legBPriority); } action.setVariables(m_channelVars); return action; } private String expandPattern(String pattern) { log().debug("Expanding pattern " + pattern); String expanded = AsteriskUtils.expandPattern(pattern); // Further expand AsteriskOriginator-specific tokens Properties props = new Properties(); props.put("exten", getLegAExtension()); expanded = PropertiesUtils.substitute(expanded, props); log().debug("Expanded pattern is: " + expanded); return expanded; } /** * @return */ private String createCallLogMsg() { StringBuffer sb = new StringBuffer(); sb.append("\n\tChannel: "); sb.append(getLegAChannel()); sb.append("\n\tFrom Caller-ID: "); sb.append(getCallerId()); if (m_legBIsApp) { sb.append("\n\tConnect to application: "); sb.append(m_legBApp); sb.append("\n\tApplication Data: "); sb.append(m_legBAppData); } else { sb.append("\n\tConnect to extension: "); sb.append(m_legBExtension); sb.append("\n\tIn context: "); sb.append(m_legBContext); sb.append("\n\tStarting at priority: "); sb.append(m_legBPriority); } sb.append("\n\tSubject is: "); sb.append(getSubject()); sb.append("\n\n"); sb.append(getMessageText()); sb.append("\n"); return sb.toString(); } /** * <p>getCallerId</p> * * @return Returns the Caller ID */ public String getCallerId() { return m_callerId; } /** * <p>setCallerId</p> * * @param cid The from address to set. */ public void setCallerId(String cid) { m_callerId = cid; } /** * <p>getAmiHost</p> * * @return Returns the AMI host. */ public String getAmiHost() { return InetAddressUtils.str(m_amiHost); } /** * <p>setAmiHost</p> * * @param amiHost Sets the mail host. * @throws java.net.UnknownHostException if any. */ public void setAmiHost(String amiHost) throws UnknownHostException { m_amiHost = InetAddressUtils.addr(amiHost); } /** * <p>getMessageText</p> * * @return Returns the message text. */ public String getMessageText() { return m_messageText; } /** * <p>setMessageText</p> * * @param messageText * Sets the message text. */ public void setMessageText(String messageText) { m_messageText = messageText; } /** * <p>getSubject</p> * * @return Returns the message Subject. */ public String getSubject() { return m_subject; } /** * <p>setSubject</p> * * @param subject * Sets the message Subject. */ public void setSubject(String subject) { m_subject = subject; } /** * <p>getLegAExtension</p> * * @return Returns the extension for Leg A */ public String getLegAExtension() { return m_legAExtension; } /** * <p>setLegAExtension</p> * * @param exten Sets the extension for Leg A */ public void setLegAExtension(String exten) { m_legAExtension = exten; } /** * <p>getLegAChannel</p> * * @return Returns the channel for Leg A */ public String getLegAChannel() { return m_legAChannel; } /** * <p>setLegAChannel</p> * * @param chan Sets the channelfor Leg A */ public void setLegAChannel(String chan) { m_legAChannel = chan; } /** * <p>isDebug</p> * * @return a boolean. */ public boolean isDebug() { return m_debug; } /** * <p>setDebug</p> * * @param debug a boolean. */ public void setDebug(boolean debug) { m_debug = debug; } /** * @return log4j Category */ private ThreadCategory log() { return ThreadCategory.getInstance(getClass()); } /** * This returns the properties configured in the asterisk-configuration.properties file. * * @return a {@link java.util.Properties} object. */ public Properties getAmiProps() { return m_amiProps; } /** * Sets a variable on the channel used for the originated call * * @param name Name of variable to set * @param value Value to set for variable */ public void setChannelVariable(String name, String value) { m_channelVars.put(name, value); } /** * Retrieves a Map of channel variables for the originated call * * @return A Map of channel variable names and values */ public Map<String,String> getChannelVariables() { return Collections.unmodifiableMap(m_channelVars); } /** * Retrieves a named channel variable for the originated call * * @param name Name of variable to retrieve * @return Value of named variable */ public String getChannelVariable(String name) { if (name == null || "".equals(name)) { return null; } return m_channelVars.get(name); } }