Package org.mobicents.slee.examples.callcontrol.voicemail

Source Code of org.mobicents.slee.examples.callcontrol.voicemail.VoiceMailSbb

/***************************************************
*                                                 *
*  Mobicents: The Open Source VoIP Platform       *
*                                                 *
*  Distributable under LGPL license.              *
*  See terms of license at gnu.org.               *
*                                                 *
***************************************************/
package org.mobicents.slee.examples.callcontrol.voicemail;

import java.net.URL;
import java.text.ParseException;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.RequestEvent;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipProvider;
import javax.sip.Transaction;
import javax.sip.TransactionUnavailableException;
import javax.sip.address.SipURI;
import javax.sip.address.URI;
import javax.sip.header.ContactHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.ToHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import javax.slee.ActivityContextInterface;
import javax.slee.Address;
import javax.slee.AddressPlan;
import javax.slee.FactoryException;
import javax.slee.SLEEException;
import javax.slee.SbbContext;
import javax.slee.SbbLocalObject;
import javax.slee.TransactionRequiredLocalException;
import javax.slee.UnrecognizedActivityException;
import javax.slee.facilities.TimerEvent;
import javax.slee.facilities.TimerFacility;
import javax.slee.facilities.TimerID;

import org.mobicents.mscontrol.MsConnection;
import org.mobicents.mscontrol.MsConnectionEvent;
import org.mobicents.mscontrol.MsNotifyEvent;
import org.mobicents.mscontrol.MsProvider;
import org.mobicents.mscontrol.MsSession;
import org.mobicents.mscontrol.MsSignalDetector;
import org.mobicents.mscontrol.MsSignalGenerator;
import org.mobicents.mscontrol.signal.AU;
import org.mobicents.mscontrol.signal.Announcement;
import org.mobicents.mscontrol.signal.Basic;
import org.mobicents.slee.examples.callcontrol.common.SubscriptionProfileSbb;
import org.mobicents.slee.examples.callcontrol.profile.CallControlProfileCMP;
import org.mobicents.slee.resource.media.ratype.MediaRaActivityContextInterfaceFactory;
import org.mobicents.slee.resource.sip.SipActivityContextInterfaceFactory;

/**
* Voice Mail service logic using SIP RA with dialog support and Media RA.
*
* @author torosvi
* @author baranowb
* @author iivanov
*
*/
public abstract class VoiceMailSbb extends SubscriptionProfileSbb implements
    javax.slee.Sbb {

  public void onInvite(javax.sip.RequestEvent event,
      VoiceMailSbbActivityContextInterface localAci) {
    Response response;
    log.info("########## VOICE MAIL SBB: INVITE ##########");
    // Request
    Request request = event.getRequest();
    // Setting Request
    this.setInviteRequest(request);

    // Server Transaction
    ServerTransaction st = event.getServerTransaction();
    // Setting Server Transaction
    this.setServerTransaction(st);

    try {
      localAci.detach(this.getSbbLocalObject());

      if (localAci.getFilteredByAncestor()) {
        log
            .info("########## VOICE MAIL SBB: FILTERED BY ANCESTOR ##########");
        return;
      }

      // if we are calling to vmail this means we want to check our mail
      // box
      // sameUser = true
      boolean sameUser = sameUser(event);
      URI uri;

      if (sameUser) {
        // The user is the caller
        FromHeader fromHeader = (FromHeader) request
            .getHeader(FromHeader.NAME);
        uri = fromHeader.getAddress().getURI();
      } else {
        // The user is the callee - we are calling someone else
        ToHeader toHeader = (ToHeader) request.getHeader(ToHeader.NAME);
        uri = toHeader.getAddress().getURI();
      }
      // In the Profile Table the port is not used
      ((SipURI) uri).removePort();

      // Responding to the user
      // To know whether the user has the Voice mail service enabled
      boolean isSubscriber = isSubscriber(uri.toString());

      if (isSubscriber) {
        // Voice Mail service enabled
        String fileRoute = null;

        // Looking for the audio file to transmit
        Context initCtx = new InitialContext();
        Context myEnv = (Context) initCtx.lookup("java:comp/env");

        // check if the user is calling their own number to check voice
        // mail
        if (sameUser) {

        }
        // or someone else is calling the user to leave a voice message
        else {

          ToHeader toHeader = (ToHeader) request
              .getHeader(ToHeader.NAME);
          String fileName = ((SipURI) toHeader.getAddress().getURI())
              .getUser()
              + WAV_EXT;

          // Setting File Route where recording the voice message
          String route = (String) myEnv.lookup("filesRoute");
          fileRoute = route + fileName;
        }

        // SDP Description from the request
        String sdp = new String(request.getRawContent());

        // Creating Media Session
        MsSession mediaSession = msProvider.createSession();
        // Setting Media Session
        this.setMediaSession(mediaSession);
        MsConnection msConnection = mediaSession
            .createNetworkConnection(ENDPOINT_NAME);

        // Attaching session AC
        ActivityContextInterface msAci = null;
        try {
          msAci = msActivityFactory
              .getActivityContextInterface(msConnection);
          msAci.attach(this.getSbbLocalObject());
        } catch (Exception ex) {
          log.error("Internal server error", ex);
          getMessageFactory().createResponse(
              Response.SERVER_INTERNAL_ERROR, request);
          return;
        }

        // Attaching to SIP Dialog activity
        Dialog dial = getSipFactoryProvider().getSipProvider()
            .getNewDialog((Transaction) st);
        ActivityContextInterface dialogAci = sipACIF
            .getActivityContextInterface(dial);

        // attach this SBB object to the Dialog activity to receive
        // subsequent events on this Dialog
        dialogAci.attach(this.getSbbLocalObject());

        // Notify caller that we're TRYING to reach voice mail. Just a
        // formality, we know we can go further than TRYING at this
        // point
        response = getMessageFactory().createResponse(Response.TRYING,
            request);
        st.sendResponse(response);

        // RINGING. Another formality of the SIP protocol.
        response = getMessageFactory().createResponse(Response.RINGING,
            request);
        st.sendResponse(response);

        log.info("Creating RTP connection [" + ENDPOINT_NAME + "]");
        msConnection.modify("$", sdp);

      }
      // Voice Mail service disabled
      else {
        response = getMessageFactory().createResponse(
            Response.TEMPORARILY_UNAVAILABLE, request);
        log.info("########## NO VOICE MAIL AVAILABLE FOR USER: "
            + uri.toString());
        st.sendResponse(response);
      }

    } catch (TransactionRequiredLocalException e) {
      log.error(e.getMessage(), e);
    } catch (SLEEException e) {
      log.error(e.getMessage(), e);
    } catch (ParseException e) {
      log.error(e.getMessage(), e);
    } catch (SipException e) {
      log.error(e.getMessage(), e);
    } catch (InvalidArgumentException e) {
      log.error(e.getMessage(), e);
    } catch (NullPointerException e) {
      log.error(e.getMessage(), e);
    } catch (NamingException e) {
      log.error(e.getMessage(), e);
    } catch (UnrecognizedActivityException e) {
      log.error(e.getMessage(), e);
    }
  }

  /**
   * At any time a SIP Client can send a BYE Request. If the Voice Mail is
   * being used it will be the VoicemailSbb the one that will send OK
   * Response.
   *
   * @param event
   * @param aci
   */
  public void onByeEvent(RequestEvent event, ActivityContextInterface aci) {
    log.info("########## VOICE MAIL SBB: BYE ##########");
    try {
      TimerID timerID = this.getTimerID();

      // If there is a Timer set we have to cancel it.
      if (timerID != null) {
        timerFacility.cancelTimer(timerID);
      }

      releaseMediaConnectionAndDialog();

      // Sending the OK Response to the BYE Request received.
      byeRequestOkResponse(event);

    } catch (FactoryException e) {
      log.error(e.getMessage(), e);
    } catch (NullPointerException e) {
      log.error(e.getMessage(), e);
    }
  }

  public void onTimerEvent(TimerEvent event, ActivityContextInterface aci) {
    // Timer Event is fired in 2 cases after waiting a number of secs:
    // * After being waiting to receive DTMF digit and do not receive any.
    // * After being recording audio from an UA without receiving a BYE
    // Request.
    // mediaSession = this.getMediaSession();
    //
    // if (this.getSameUser()) {
    // // In this case we were waiting DTMF digit.
    // mediaSession.stopSession();
    // // We have not received any DTMF digit, so we try it again.
    // URL audioFileURL = getClass().getResource(tryAgain);
    // mediaSession.createTransmitterReceiver(mediaSession
    // .getSdpDescription(), audioFileURL, null, true);
    // } else {
    // // In this case we were waiting a BYE Request.
    // mediaSession.stopSession();
    // // We have not received the BYE, so we send the Request to the UA.
    // sendByeRequest();
    // aci.detach(this.getSbbLocalObject());
    // }
  }

  public void onConnectionCreated(MsConnectionEvent evt,
      ActivityContextInterface aci) {
    MsConnection connection = evt.getConnection();
    log.info("Created RTP connection [" + connection.getEndpoint() + "]");

    MsConnection msConnection = evt.getConnection();
    String sdp = msConnection.getLocalDescriptor();

    ServerTransaction txn = getServerTransaction();
    if (txn == null) {
      log.error("SIP activity lost, close RTP connection");
      msConnection.release();
      return;
    }

    Request request = txn.getRequest();

    ContentTypeHeader contentType = null;
    try {
      contentType = getHeaderFactory().createContentTypeHeader(
          "application", "sdp");
    } catch (ParseException ex) {
    }

    String localAddress = getSipFactoryProvider().getSipProvider()
        .getListeningPoints()[0].getIPAddress();
    int localPort = getSipFactoryProvider().getSipProvider()
        .getListeningPoints()[0].getPort();

    javax.sip.address.Address contactAddress = null;
    try {
      contactAddress = getAddressFactory().createAddress(
          "sip:" + localAddress + ":" + localPort);
    } catch (ParseException ex) {
      log.error(ex.getMessage(), ex);
    }
    ContactHeader contact = getHeaderFactory().createContactHeader(
        contactAddress);

    Response response = null;
    try {
      response = getMessageFactory().createResponse(Response.OK, request,
          contentType, sdp.getBytes());
    } catch (ParseException ex) {
    }

    response.setHeader(contact);
    try {
      txn.sendResponse(response);
    } catch (InvalidArgumentException ex) {
      log.error(ex.getMessage(), ex);
    } catch (SipException ex) {
      log.error(ex.getMessage(), ex);
    }

    String endpointName = evt.getConnection().getEndpoint();
    this.setUserEndpoint(endpointName);

    if (getSameUser()) {

      log.debug("same user, lets play the voice mail");
      System.out.println("same user, lets play the voice mail");

      MsSignalGenerator generator = msProvider
          .getSignalGenerator(endpointName);

      try {
        ActivityContextInterface generatorActivity = msActivityFactory
            .getActivityContextInterface(generator);
        generatorActivity.attach(this.getSbbLocalObject());

        URL audioFileURL = getClass().getResource(waitingDTMF);

        generator.apply(Announcement.PLAY, new String[] { audioFileURL
            .toString() });

        this.initDtmfDetector(evt.getConnection(), endpointName);

      } catch (UnrecognizedActivityException e) {
        e.printStackTrace();
      }

    } else {
      log.debug("not the same user, start recording after announcement");
      System.out
          .println("not the same user, start recording after announcement");

      URL audioFileURL = getClass().getResource(recordAfterTone);

      ToHeader toHeader = (ToHeader) request.getHeader(ToHeader.NAME);
      String fileName = ((SipURI) toHeader.getAddress().getURI())
          .getUser()
          + WAV_EXT;
      String route = null;
      String recordFilePath = null;

      try {
        Context initCtx = new InitialContext();
        Context myEnv = (Context) initCtx.lookup("java:comp/env");

        route = (String) myEnv.lookup("filesRoute");
      } catch (NamingException nEx) {
        log.warn("Lookup of filesRoute env Variable failed", nEx);
      }

      if (route != null) {
        recordFilePath = route + fileName;
      } else {
        recordFilePath = fileName;
      }

      String[] params = new String[2];
      params[0] = audioFileURL.toString();
      params[1] = recordFilePath;

      MsSignalGenerator signalGenerator = msProvider
          .getSignalGenerator(endpointName);

      try {
        ActivityContextInterface dtmfAci = msActivityFactory
            .getActivityContextInterface(signalGenerator);
        dtmfAci.attach(this.getSbbLocalObject());
        signalGenerator.apply(AU.PLAY_RECORD, params);
      } catch (UnrecognizedActivityException e) {
        log.error(e.getMessage(), e);
      }
    }

  }

  private void initDtmfDetector(MsConnection connection, String userEndPoint) {
    MsSignalDetector dtmfDetector = msProvider
        .getSignalDetector(userEndPoint);
    try {
      ActivityContextInterface dtmfAci = msActivityFactory
          .getActivityContextInterface(dtmfDetector);
      dtmfAci.attach(this.getSbbLocalObject());
      dtmfDetector.receive(Basic.DTMF, connection, new String[] {});
    } catch (UnrecognizedActivityException e) {
      log.error(e.getMessage(), e);
    }
  }

  private void releaseMediaConnectionAndDialog() {
    ActivityContextInterface[] activities = getSbbContext().getActivities();
    SbbLocalObject sbbLocalObject = getSbbContext().getSbbLocalObject();
    MsConnection msConnection = null;
    for (ActivityContextInterface attachedAci : activities) {
      if (attachedAci.getActivity() instanceof Dialog) {
        attachedAci.detach(sbbLocalObject);
      }
      if (attachedAci.getActivity() instanceof MsConnection) {
        attachedAci.detach(sbbLocalObject);
        msConnection = (MsConnection) attachedAci.getActivity();
      }
    }
    if (msConnection != null) {
      msConnection.release();
    }
  }

  private void sendServerInternalError() {
    try {
      Response response = getMessageFactory().createResponse(
          Response.SERVER_INTERNAL_ERROR, this.getInviteRequest());
      this.getServerTransaction().sendResponse(response);

    } catch (ParseException e) {
      log.error(e.getMessage(), e);
    } catch (SipException e) {
      log.error(e.getMessage(), e);
    } catch (InvalidArgumentException e) {
      log.error(e.getMessage(), e);
    }
  }

  public void onDtmf(MsNotifyEvent evt, ActivityContextInterface aci) {
    log.info("########## VOICE MAIL SBB: onDTMFEvent ##########");
    int cause = evt.getCause();
    checkDtmfDigit(cause);
    this.initDtmfDetector(this.getConnection(), this.getUserEndpoint());

  }

  private MsConnection getConnection() {
    ActivityContextInterface[] activities = this.getSbbContext()
        .getActivities();
    for (int i = 0; i < activities.length; i++) {
      if (activities[i].getActivity() instanceof MsConnection) {
        return (MsConnection) activities[i].getActivity();
      }
    }
    return null;
  }

  /**
   * Voice Mail will hang up on caller sending a BYE Request.
   *
   */
  private void sendByeRequest() {
    log.info("########## VOICE MAIL SBB: sendByRequest ##########");
    try {
      SipProvider sipProvider = getSipFactoryProvider().getSipProvider();
      Dialog dialog = this.getServerTransaction().getDialog();
      Request request = dialog.createRequest(Request.BYE);
      ClientTransaction ct = sipProvider.getNewClientTransaction(request);

      dialog.sendRequest(ct);

    } catch (TransactionUnavailableException e) {
      log.error(e.getMessage(), e);
    } catch (SipException e) {
      log.error(e.getMessage(), e);
    }
  }

  /**
   * After receiving a BYE Request, an OK Respose has to be sent.
   *
   * @param byeEvent
   */
  private void byeRequestOkResponse(RequestEvent byeEvent) {
    log.info("########## VOICE MAIL SBB: byeRequestOkResponse ##########");
    Request request = byeEvent.getRequest();
    ServerTransaction tx = byeEvent.getServerTransaction();
    try {
      Response response = getMessageFactory().createResponse(Response.OK,
          request);
      tx.sendResponse(response);
    } catch (Exception e) {
      log.error(e.getMessage(), e);
    }
  }

  private void checkDtmfDigit(int dtmf) {
    URL audioFileURL;

    /**
     * TODO: The Feature of listening the next message and deleting the last
     * message is not implemented yet. After pressing 1 or 7 you will only
     * listen a message saying which number you have pressed
     */

    // Press 1 if you want to listen the next message
    if (dtmf == 1) {
      audioFileURL = getClass().getResource(dtmf1);
    }
    // Press 7 if you want to delete the last message
    else if (dtmf == 7) {
      audioFileURL = getClass().getResource(dtmf7);
    }
    // Press 9 if you want to hang up
    else if (dtmf == 9) {
      audioFileURL = getClass().getResource(dtmf9);

    } else {
      audioFileURL = getClass().getResource(tryAgain);
    }

    MsSignalGenerator generator = msProvider.getSignalGenerator(this
        .getUserEndpoint());

    try {
      ActivityContextInterface generatorActivity = msActivityFactory
          .getActivityContextInterface(generator);
      generatorActivity.attach(getSbbContext().getSbbLocalObject());

      generator.apply(Announcement.PLAY, new String[] { audioFileURL
          .toString() });

      // this.initDtmfDetector(getConnection(), this.getEndpointName());
    } catch (UnrecognizedActivityException e) {
      e.printStackTrace();
    }

  }

  /**
   * To know whether or not the called user has the Voice Mail service
   * enabled.
   *
   * @param sipAddress:
   *            Called user address.
   * @return boolean: TRUE -> Voice Mail enabled. FALSE -> Voice Mail disabled
   *         for the given user identified by sip address.
   */
  private boolean isSubscriber(String sipAddress) {
    boolean state = false;
    CallControlProfileCMP profile = lookup(new Address(AddressPlan.SIP,
        sipAddress));

    if (profile != null) {
      state = profile.getVoicemailState();
    }

    return state;
  }

  /**
   * This method is used to know if the it is going to be used the voice mail
   * of the same user or the voice mail of a different user.
   *
   * @param event
   * @return TRUE: If the called user is sip:vmail@nist.gov
   */
  private boolean sameUser(javax.sip.RequestEvent event) {
    boolean sameUser = false;
    Request inviteRequest = event.getRequest();

    // Checking if the called user and the caller are the same
    ToHeader toHeader = (ToHeader) inviteRequest.getHeader(ToHeader.NAME);
    SipURI toURI = (SipURI) toHeader.getAddress().getURI();

    if ((toURI.getUser().equals(USER) && toURI.getHost().equals(HOST))) {
      sameUser = true;
    }

    // Setting Same User value
    this.setSameUser(sameUser);

    return sameUser;
  }

  // TODO: Perform further operations if required in these methods.
  public void setSbbContext(SbbContext context) {
    super.setSbbContext(context);

    // To create Header objects from a particular implementation of JAIN SIP
    headerFactory = getSipFactoryProvider().getHeaderFactory();

    try {
      Context myEnv = (Context) new InitialContext()
          .lookup("java:comp/env");
      // Getting Media Resource Adaptor interfaces
      msProvider = (MsProvider) myEnv
          .lookup("slee/resources/media/1.0/provider");
      msActivityFactory = (MediaRaActivityContextInterfaceFactory) myEnv
          .lookup("slee/resources/media/1.0/acifactory");
      // Getting Sip Resource Adaptor interface
      sipACIF = (SipActivityContextInterfaceFactory) myEnv
          .lookup("slee/resources/jainsip/1.2/acifactory");
      // Getting Timer Facility interface
      timerFacility = (TimerFacility) myEnv
          .lookup("slee/facilities/timer");

    } catch (NamingException e) {
      log.error(e.getMessage(), e);
    }
  }

  public void sbbPostCreate() throws javax.slee.CreateException {
    // Setting DTMF
    this.setDtmf(NON_DIGIT);
  }

  public abstract org.mobicents.slee.examples.callcontrol.profile.CallControlProfileCMP getCallControlProfileCMP(
      javax.slee.profile.ProfileID profileID)
      throws javax.slee.profile.UnrecognizedProfileNameException,
      javax.slee.profile.UnrecognizedProfileTableNameException;

  public abstract org.mobicents.slee.examples.callcontrol.voicemail.VoiceMailSbbActivityContextInterface asSbbActivityContextInterface(
      ActivityContextInterface aci);

  private final HeaderFactory getHeaderFactory() {
    return headerFactory;
  }

  // Interfaces
  private HeaderFactory headerFactory;

  private SipActivityContextInterfaceFactory sipACIF;
  private TimerFacility timerFacility;

  private final String recordAfterTone = "audiofiles/RecordAfterTone.wav";
  private final String waitingDTMF = "audiofiles/WaitingDTMF.wav";
  private final String dtmf1 = "audiofiles/DTMF1.wav";
  private final String dtmf7 = "audiofiles/DTMF7.wav";
  private final String dtmf9 = "audiofiles/DTMF9.wav";
  private final String tryAgain = "audiofiles/TryAgain.wav";

  private final String USER = "vmail";
  private final String HOST = "nist.gov";
  private final String NON_DIGIT = "NULL";
  private final String WAV_EXT = ".wav";

  private MsProvider msProvider;
  private MediaRaActivityContextInterfaceFactory msActivityFactory;

  public final static String ENDPOINT_NAME = "media/trunk/IVR/$";

  /**
   * ***************************************** ************** CMP Fields
   * *************** *****************************************
   */

  // 'mediaSession' CMP field setter
  public abstract void setMediaSession(MsSession value);

  // 'mediaSession' CMP field getter
  public abstract MsSession getMediaSession();

  // 'inviteRequest' CMP field setter
  public abstract void setInviteRequest(Request value);

  // 'inviteRequest' CMP field getter
  public abstract Request getInviteRequest();

  // 'serverTransaction' CMP field setter
  public abstract void setServerTransaction(ServerTransaction value);

  // 'serverTransaction' CMP field getter
  public abstract ServerTransaction getServerTransaction();

  // 'ok' CMP field setter
  public abstract void setOk(boolean value);

  // 'ok' CMP field getter
  public abstract boolean getOk();

  // 'sameUser' CMP field setter
  public abstract void setSameUser(boolean value);

  // 'sameUser' CMP field getter
  public abstract boolean getSameUser();

  // 'timerID' CMP field setter
  public abstract void setTimerID(TimerID value);

  // 'timerID' CMP field getter
  public abstract TimerID getTimerID();

  // 'dtmf' CMP field setter
  public abstract void setDtmf(String value);

  // 'dtmf' CMP field getter
  public abstract String getDtmf();

  public abstract String getUserEndpoint();

  public abstract void setUserEndpoint(String endpointName);

}
TOP

Related Classes of org.mobicents.slee.examples.callcontrol.voicemail.VoiceMailSbb

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.