package net.sourceforge.gjtapi.raw.mjsip;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Semaphore;
import java.util.logging.Logger;
import javax.telephony.InvalidArgumentException;
import javax.telephony.InvalidPartyException;
import javax.telephony.MethodNotSupportedException;
import javax.telephony.PrivilegeViolationException;
import javax.telephony.ProviderUnavailableException;
import javax.telephony.ResourceUnavailableException;
import javax.telephony.media.MediaResourceException;
import javax.telephony.media.PlayerConstants;
import javax.telephony.media.RTC;
import javax.telephony.media.RecorderConstants;
import javax.telephony.media.Symbol;
import net.sourceforge.gjtapi.CallId;
import net.sourceforge.gjtapi.RawSigDetectEvent;
import net.sourceforge.gjtapi.RawStateException;
import net.sourceforge.gjtapi.TelephonyListener;
import net.sourceforge.gjtapi.TermData;
import net.sourceforge.gjtapi.raw.MediaTpi;
import net.sourceforge.gjtapi.raw.mjsip.ua.UA;
/**
* This is a provider that hooks into the MjSip to provide
* Session Initiation Protocol support for GJTAPI
*
* If possible disable ReINVITEs in you registrar. There has been some problems
* between MjSIP UserAgent and X-Lite during call hangup, causing the UserAgent
* to keep his online status. (On Asterisk use canreinvite=no on sip.conf)
*
*/
public class MjSipProvider implements MediaTpi {
/** Logger instance. */
private static final Logger LOGGER =
Logger.getLogger(MjSipProvider.class.getName());
private final HashMap<String, UA> loadedUAs = new HashMap<String, UA>();
private Collection<TelephonyListener> listener;
private final Semaphore playSemaphore;
private final Semaphore recSemaphore;
public MjSipProvider() {
playSemaphore = new Semaphore(1, true);
recSemaphore = new Semaphore(1, true);
}
// ******************* Core GJTAPI Functions ******************
/**
* {@inheritDoc}
*/
public void initialize(Map props) throws
ProviderUnavailableException {
String strPhone = (String) props.get("gjtapi.mjsip.ua");
if (strPhone == null) {
String resource = System.getProperty("gjtapi.sip.properties",
"/MjSip.props");
InputStream in = null;
try {
in = MjSipProvider.class.getResourceAsStream(
resource);
if (in == null) {
throw new ProviderUnavailableException("resource '"
+ resource + "' not found in CLASSPATH!");
}
Properties properties = System.getProperties();
properties.load(in);
strPhone = properties.getProperty("gjtapi.mjsip.ua");
} catch (IOException e) {
throw new ProviderUnavailableException(e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException ignore) {
}
}
}
}
if (strPhone != null) {
String[] phones = strPhone.split(",");
for (int i=0; i<phones.length; i++) {
try {
String phone = phones[i];
UA ua = new UA(phone, this);
loadedUAs.put(ua.getAddress(), ua);
LOGGER.info("MjSipProvider initialize UA: "
+ ua.getAddress());
} catch (Exception e) {
throw new ProviderUnavailableException(e.getMessage());
}
}
}
}
/**
* {@inheritDoc}
*/
public Properties getCapabilities() {
return null;
}
/**
* {@inheritDoc}
*/
public String[] getAddresses() throws ResourceUnavailableException {
String[] ret = new String[loadedUAs.size()];
int i = 0;
for (String address : loadedUAs.keySet()) {
ret[i] = address;
i++;
}
return ret;
}
/**
* {@inheritDoc}
*/
public String[] getAddresses(String terminal) throws
InvalidArgumentException {
UA ua = loadedUAs.get(terminal);
if (ua == null) {
return new String[] {};
} else {
return new String[] {terminal};
}
}
/**
* {@inheritDoc}
*/
public TermData[] getTerminals() throws ResourceUnavailableException {
TermData[] ret = new TermData[loadedUAs.size()];
int i = 0;
for (String address : loadedUAs.keySet()) {
ret[i] = new TermData(address, true);
i++;
}
return ret;
}
/**
* {@inheritDoc}
*/
public TermData[] getTerminals(String address) throws
InvalidArgumentException {
UA ua = loadedUAs.get(address);
if (ua == null) {
return new TermData[] {};
} else {
return new TermData[] {
new TermData(address, true)
};
}
}
/**
* {@inheritDoc}
*/
public void releaseCallId(CallId id) {
}
/** Add a listener for RawEvnts
* Creation date: (2007-10-02 16:30:54)
* @author:
* @return
* @param to Listener to add
* @throws
*
*/
public void addListener(TelephonyListener ro) {
if (listener == null) {
listener = new java.util.ArrayList<TelephonyListener>();
}
synchronized (listener) {
listener.add(ro);
}
}
/**
* {@inheritDoc}
*/
public void removeListener(TelephonyListener ro) {
if (ro == listener) {
listener = null;
} else {
System.err.println("Request to remove a TelephonyListener from "
+ this.getClass().getName() +
", but it wasn't registered");
}
}
/**
* {@inheritDoc}
*/
public void shutdown() {
for (UA ua : loadedUAs.values()) {
ua.hangup();
ua.closeMediaApplication();
}
loadedUAs.clear();
}
// ******************* Basic Call Control ******************
/**
* {@inheritDoc}
*/
public void answerCall(CallId call, String address, String terminal) throws
PrivilegeViolationException, ResourceUnavailableException,
MethodNotSupportedException, RawStateException {
//Get UA
final UA ua = loadedUAs.get(address);
if (ua == null) {
throw new ResourceUnavailableException(ResourceUnavailableException.
ORIGINATOR_UNAVAILABLE, "Address not found: " + address);
}
final CallId uaCallId = ua.getCallId();
if (!uaCallId.equals(call)) {
throw new ResourceUnavailableException(
ResourceUnavailableException.UNSPECIFIED_LIMIT_EXCEEDED);
}
ua.accept();
}
/**
* {@inheritDoc}
*/
public CallId reserveCallId(String address) throws InvalidArgumentException {
return new MjSipCallId();
}
/**
* {@inheritDoc}
*/
public CallId createCall(CallId id, String address, String term,
String dest) throws ResourceUnavailableException,
PrivilegeViolationException, InvalidPartyException,
InvalidArgumentException, RawStateException,
MethodNotSupportedException {
//Get UA
UA ua = loadedUAs.get(address);
if (ua == null) {
throw new ResourceUnavailableException(ResourceUnavailableException.
ORIGINATOR_UNAVAILABLE, "Address not found: " + address);
}
ua.setCallId(id);
ua.call(dest);
return id;
}
/**
* {@inheritDoc}
*/
public void release(String address, CallId call) throws
PrivilegeViolationException, ResourceUnavailableException,
MethodNotSupportedException, RawStateException {
UA ua = loadedUAs.get(address);
if (ua == null) {
throw new ResourceUnavailableException(ResourceUnavailableException.
ORIGINATOR_UNAVAILABLE, "Address not found: " + address);
}
ua.hangup();
}
// ******************* TelephonyListener ******************
/**
* {@inheritDoc}
*/
public void callActive(CallId id, int cause) {
if (listener != null) {
synchronized (listener) {
for (TelephonyListener current : listener) {
current.callActive(id, cause);
}
}
}
}
/**
* {@inheritDoc}
*/
public void connectionInProgress(CallId id, String address, int cause) {
if (listener != null) {
synchronized (listener) {
for (TelephonyListener current : listener) {
current.connectionInProgress(id, address, cause);
}
}
}
}
/**
* {@inheritDoc}
*/
public void connectionAlerting(CallId id, String address, int cause) {
if (listener != null) {
synchronized (listener) {
for (TelephonyListener current : listener) {
current.connectionAlerting(id, address, cause);
}
}
}
}
/**
* {@inheritDoc}
*/
public void connectionConnected(CallId id, String address, int cause) {
if (listener != null) {
synchronized (listener) {
for (TelephonyListener current : listener) {
current.connectionConnected(id, address, cause);
}
}
}
}
/**
* {@inheritDoc}
*/
public void connectionDisconnected(CallId id, String address, int cause) {
//Get UA
UA ua = loadedUAs.get(address);
if (ua != null) {
ua.stopPlay();
ua.stopRecord();
}
if (listener != null) {
synchronized (listener) {
for (TelephonyListener current : listener) {
current.connectionDisconnected(id, address, cause);
}
}
}
}
/**
* {@inheritDoc}
*/
public void terminalConnectionCreated(CallId id, String address,
String terminal, int cause) {
if (listener != null) {
synchronized (listener) {
for (TelephonyListener current : listener) {
current.terminalConnectionCreated(id, address, terminal,
cause);
}
}
}
}
/**
* {@inheritDoc}
*/
public void terminalConnectionRinging(CallId id, String address,
String terminal, int cause) {
if (listener != null) {
synchronized (listener) {
for (TelephonyListener current : listener) {
current.terminalConnectionRinging(id, address, terminal,
cause);
}
}
}
}
// *************************** Media *************************
/**
* {@inheritDoc}
*/
public boolean allocateMedia(String terminal, int type,
Dictionary resourceArgs) {
return true;
}
/**
* {@inheritDoc}
*/
public boolean freeMedia(String terminal, int type) {
//Get UA
UA ua = loadedUAs.get(terminal);
if (ua == null) {
return false;
} else {
ua.closeMediaApplication();
return true;
}
}
/**
* {@inheritDoc}
*/
public void play(String terminal, String[] streamIds, int offset,
RTC[] rtcs, Dictionary optArgs) throws
MediaResourceException {
try {
do {
try {
playSemaphore.acquire(1);
break;
} catch (InterruptedException ex) {
LOGGER.warning("wait for play sem interrupted: "
+ ex.getMessage());
return;
}
} while (true);
//Get UA
UA ua = loadedUAs.get(terminal);
if (ua == null) {
playSemaphore.release(1);
throw new MediaResourceException("Terminal not found: " +
terminal);
}
for (String streamID : streamIds) {
URI uri = new URI(streamID);
InputStream in = null;
if (uri.getScheme().equals("file")) {
in = new FileInputStream(uri.getPath());
} else {
URL url = new URL(streamID);
URLConnection c = url.openConnection();
c.connect();
in = c.getInputStream();
}
try {
ua.play(in);
} finally {
if (in != null) {
in.close();
}
}
}
} catch (IllegalMonitorStateException e) {
LOGGER.warning(e.getMessage());
throw new MediaResourceException(e.getMessage());
} catch (Exception e) {
LOGGER.warning(e.getMessage());
throw new MediaResourceException(e.getMessage());
} finally {
playSemaphore.release(1);
}
}
public void record(String terminal, String streamId, RTC[] rtcs,
Dictionary optArgs) throws MediaResourceException {
do {
try {
recSemaphore.acquire(1);
break;
} catch (InterruptedException ex) {
return;
}
} while (true);
//Get UA
UA ua = loadedUAs.get(terminal);
if (ua == null) {
recSemaphore.release(1);
throw new MediaResourceException("Terminal not found: " + terminal);
}
try {
URI uri = new URI(streamId);
if (uri.getScheme().equals("file")) {
FileOutputStream fos = new FileOutputStream(uri.getPath());
ua.record(fos);
} else {
URL url = new URL(streamId);
URLConnection c = url.openConnection();
c.connect();
OutputStream os = c.getOutputStream();
ua.record(os);
os.close();
}
} catch (Exception e) {
LOGGER.warning(e.getMessage());
throw new MediaResourceException(e.getMessage());
} finally {
recSemaphore.release(1);
}
}
public RawSigDetectEvent retrieveSignals(String terminal, int num,
Symbol[] patterns, RTC[] rtcs,
Dictionary optArgs) throws
MediaResourceException {
return null;
}
public void sendSignals(String terminal, Symbol[] syms, RTC[] rtcs,
Dictionary optArgs) throws MediaResourceException {
}
public void stop(String terminal) {
//Get UA
UA ua = loadedUAs.get(terminal);
if (ua == null) {
return;
}
ua.stop();
}
public void triggerRTC(String terminal, Symbol action) {
//Get UA
UA ua = loadedUAs.get(terminal);
if (ua == null) {
return;
}
if (action.equals(PlayerConstants.rtca_Stop)) {
ua.stopPlay();
} else if (action.equals(RecorderConstants.rtca_Stop)) {
ua.stopRecord();
}
}
public boolean isMediaTerminal(String terminal) {
return true;
}
}