PageRenderTime 64ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/servers/jain-slee/examples/call-controller2/jar/src/main/java/org/mobicents/slee/examples/callcontrol/voicemail/VoiceMailSbb.java

http://mobicents.googlecode.com/
Java | 1178 lines | 779 code | 220 blank | 179 comment | 71 complexity | b1da5808435ebbafcfb2cf8fe3485e29 MD5 | raw file
Possible License(s): LGPL-3.0, GPL-3.0, LGPL-2.1, GPL-2.0, CC-BY-SA-3.0, CC0-1.0, Apache-2.0, BSD-3-Clause
  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.mobicents.slee.examples.callcontrol.voicemail;
  23. import jain.protocol.ip.mgcp.JainMgcpEvent;
  24. import jain.protocol.ip.mgcp.message.CreateConnection;
  25. import jain.protocol.ip.mgcp.message.CreateConnectionResponse;
  26. import jain.protocol.ip.mgcp.message.DeleteConnection;
  27. import jain.protocol.ip.mgcp.message.NotificationRequest;
  28. import jain.protocol.ip.mgcp.message.NotificationRequestResponse;
  29. import jain.protocol.ip.mgcp.message.Notify;
  30. import jain.protocol.ip.mgcp.message.NotifyResponse;
  31. import jain.protocol.ip.mgcp.message.parms.CallIdentifier;
  32. import jain.protocol.ip.mgcp.message.parms.ConflictingParameterException;
  33. import jain.protocol.ip.mgcp.message.parms.ConnectionDescriptor;
  34. import jain.protocol.ip.mgcp.message.parms.ConnectionMode;
  35. import jain.protocol.ip.mgcp.message.parms.EndpointIdentifier;
  36. import jain.protocol.ip.mgcp.message.parms.EventName;
  37. import jain.protocol.ip.mgcp.message.parms.NotifiedEntity;
  38. import jain.protocol.ip.mgcp.message.parms.RequestedAction;
  39. import jain.protocol.ip.mgcp.message.parms.RequestedEvent;
  40. import jain.protocol.ip.mgcp.message.parms.ReturnCode;
  41. import jain.protocol.ip.mgcp.pkg.MgcpEvent;
  42. import jain.protocol.ip.mgcp.pkg.PackageName;
  43. import java.io.File;
  44. import java.net.URL;
  45. import java.text.ParseException;
  46. import java.util.ArrayList;
  47. import java.util.regex.Pattern;
  48. import javax.naming.Context;
  49. import javax.naming.InitialContext;
  50. import javax.naming.NamingException;
  51. import javax.sip.ClientTransaction;
  52. import javax.sip.Dialog;
  53. import javax.sip.InvalidArgumentException;
  54. import javax.sip.RequestEvent;
  55. import javax.sip.ServerTransaction;
  56. import javax.sip.SipException;
  57. import javax.sip.SipProvider;
  58. import javax.sip.Transaction;
  59. import javax.sip.TransactionUnavailableException;
  60. import javax.sip.address.SipURI;
  61. import javax.sip.address.URI;
  62. import javax.sip.header.ContactHeader;
  63. import javax.sip.header.ContentTypeHeader;
  64. import javax.sip.header.FromHeader;
  65. import javax.sip.header.HeaderFactory;
  66. import javax.sip.header.ToHeader;
  67. import javax.sip.message.Request;
  68. import javax.sip.message.Response;
  69. import javax.slee.ActivityContextInterface;
  70. import javax.slee.ActivityEndEvent;
  71. import javax.slee.Address;
  72. import javax.slee.AddressPlan;
  73. import javax.slee.FactoryException;
  74. import javax.slee.SLEEException;
  75. import javax.slee.SbbContext;
  76. import javax.slee.SbbLocalObject;
  77. import javax.slee.TransactionRequiredLocalException;
  78. import javax.slee.UnrecognizedActivityException;
  79. import javax.slee.facilities.TimerEvent;
  80. import javax.slee.facilities.Tracer;
  81. import net.java.slee.resource.mgcp.JainMgcpProvider;
  82. import net.java.slee.resource.mgcp.MgcpActivityContextInterfaceFactory;
  83. import net.java.slee.resource.mgcp.MgcpConnectionActivity;
  84. import net.java.slee.resource.mgcp.MgcpEndpointActivity;
  85. import net.java.slee.resource.sip.DialogActivity;
  86. import net.java.slee.resource.sip.SipActivityContextInterfaceFactory;
  87. import org.mobicents.protocols.mgcp.jain.pkg.AUPackage;
  88. import org.mobicents.slee.examples.callcontrol.common.SubscriptionProfileSbb;
  89. import org.mobicents.slee.examples.callcontrol.profile.CallControlProfileCMP;
  90. /**
  91. * Voice Mail service logic using SIP RA with dialog support and Media RA.
  92. *
  93. * @author torosvi
  94. * @author baranowb
  95. * @author iivanov
  96. *
  97. */
  98. public abstract class VoiceMailSbb extends SubscriptionProfileSbb implements
  99. javax.slee.Sbb {
  100. public void onInvite(javax.sip.RequestEvent event,
  101. VoiceMailSbbActivityContextInterface localAci) {
  102. Response response;
  103. log.info("########## VOICE MAIL SBB: INVITE ##########");
  104. // Request
  105. Request request = event.getRequest();
  106. // Setting Request
  107. this.setInviteRequest(request);
  108. // Server Transaction
  109. ServerTransaction st = event.getServerTransaction();
  110. try {
  111. if (localAci.getFilteredByAncestor()) {
  112. log
  113. .info("########## VOICE MAIL SBB: FILTERED BY ANCESTOR ##########");
  114. return;
  115. }
  116. // if we are calling to vmail this means we want to check our mail
  117. // box
  118. // sameUser = true
  119. boolean sameUser = sameUser(event);
  120. URI uri;
  121. if (sameUser) {
  122. // The user is the caller
  123. FromHeader fromHeader = (FromHeader) request
  124. .getHeader(FromHeader.NAME);
  125. uri = fromHeader.getAddress().getURI();
  126. } else {
  127. // The user is the callee - we are calling someone else
  128. ToHeader toHeader = (ToHeader) request.getHeader(ToHeader.NAME);
  129. uri = toHeader.getAddress().getURI();
  130. }
  131. // In the Profile Table the port is not used
  132. ((SipURI) uri).removePort();
  133. // Responding to the user
  134. // To know whether the user has the Voice mail service enabled
  135. boolean isSubscriber = isSubscriber(uri.toString());
  136. if (isSubscriber) {
  137. // Formalities of sip, so we dont get retrans
  138. // Attaching to SIP Dialog activity
  139. Dialog dial = getSipFactoryProvider().getNewDialog(
  140. (Transaction) st);
  141. ActivityContextInterface dialogAci = sipACIF
  142. .getActivityContextInterface((DialogActivity) dial);
  143. // attach this SBB object to the Dialog activity to receive
  144. // subsequent events on this Dialog
  145. dialogAci.attach(this.getSbbLocalObject());
  146. // Notify caller that we're TRYING to reach voice mail. Just a
  147. // formality, we know we can go further than TRYING at this
  148. // point
  149. response = getMessageFactory().createResponse(Response.TRYING,
  150. request);
  151. st.sendResponse(response);
  152. // RINGING. Another formality of the SIP protocol.
  153. response = getMessageFactory().createResponse(Response.RINGING,
  154. request);
  155. st.sendResponse(response);
  156. String sdp = new String(event.getRequest().getRawContent());
  157. CallIdentifier callID = this.mgcpProvider
  158. .getUniqueCallIdentifier();
  159. // this is not requiered, but to be good MGCP citizen we will
  160. // obey mgcp call id rule.
  161. setCallIdentifier(callID);
  162. EndpointIdentifier endpointID = new EndpointIdentifier(
  163. PRE_ENDPOINT_NAME, mmsBindAddress + ":"
  164. + MGCP_PEER_PORT);
  165. CreateConnection createConnection = new CreateConnection(this,
  166. callID, endpointID, ConnectionMode.Confrnce);
  167. try {
  168. createConnection
  169. .setRemoteConnectionDescriptor(new ConnectionDescriptor(
  170. sdp));
  171. } catch (ConflictingParameterException e) {
  172. // TODO Auto-generated catch block
  173. e.printStackTrace();
  174. }
  175. int txID = mgcpProvider.getUniqueTransactionHandler();
  176. createConnection.setTransactionHandle(txID);
  177. MgcpConnectionActivity connectionActivity = null;
  178. try {
  179. connectionActivity = mgcpProvider.getConnectionActivity(
  180. txID, endpointID);
  181. ActivityContextInterface epnAci = mgcpActivityContestInterfaceFactory
  182. .getActivityContextInterface(connectionActivity);
  183. epnAci.attach(getSbbContext().getSbbLocalObject());
  184. } catch (FactoryException ex) {
  185. ex.printStackTrace();
  186. } catch (NullPointerException ex) {
  187. ex.printStackTrace();
  188. } catch (UnrecognizedActivityException ex) {
  189. ex.printStackTrace();
  190. }
  191. mgcpProvider
  192. .sendMgcpEvents(new JainMgcpEvent[] { createConnection });
  193. log
  194. .info("########## VOICE MAIL AVAILABLE FOR USER: sent PR CRCX request ##########");
  195. } else {
  196. // Voice Mail service disabled
  197. response = getMessageFactory().createResponse(
  198. Response.TEMPORARILY_UNAVAILABLE, request);
  199. log.info("########## NO VOICE MAIL AVAILABLE FOR USER: "
  200. + uri.toString());
  201. st.sendResponse(response);
  202. }
  203. } catch (TransactionRequiredLocalException e) {
  204. log.severe(e.getMessage(), e);
  205. } catch (SLEEException e) {
  206. log.severe(e.getMessage(), e);
  207. } catch (ParseException e) {
  208. log.severe(e.getMessage(), e);
  209. } catch (SipException e) {
  210. log.severe(e.getMessage(), e);
  211. } catch (InvalidArgumentException e) {
  212. log.severe(e.getMessage(), e);
  213. } catch (NullPointerException e) {
  214. log.severe(e.getMessage(), e);
  215. }
  216. }
  217. /**
  218. * At any time a SIP Client can send a BYE Request. If the Voice Mail is
  219. * being used it will be the VoicemailSbb the one that will send OK
  220. * Response.
  221. *
  222. * @param event
  223. * @param aci
  224. */
  225. public void onByeEvent(RequestEvent event, ActivityContextInterface aci) {
  226. log.info("########## VOICE MAIL SBB: BYE ##########");
  227. try {
  228. releaseState();
  229. // Sending the OK Response to the BYE Request received.
  230. byeRequestOkResponse(event);
  231. } catch (FactoryException e) {
  232. log.severe(e.getMessage(), e);
  233. } catch (NullPointerException e) {
  234. log.severe(e.getMessage(), e);
  235. }
  236. }
  237. private void sendServerError(String message, int errorCode) {
  238. try {
  239. Response response = getMessageFactory()
  240. .createResponse(
  241. Response.SERVER_INTERNAL_ERROR,
  242. this.getInviteRequest(),
  243. getHeaderFactory().createContentTypeHeader("text",
  244. "plain"), message.getBytes());
  245. this.getServerTransaction().sendResponse(response);
  246. } catch (ParseException e) {
  247. log.severe(e.getMessage(), e);
  248. } catch (SipException e) {
  249. log.severe(e.getMessage(), e);
  250. } catch (InvalidArgumentException e) {
  251. log.severe(e.getMessage(), e);
  252. }
  253. releaseState();
  254. }
  255. public void onCreateConnectionResponse(CreateConnectionResponse event,
  256. ActivityContextInterface aci) {
  257. log.info("Receive CRCX response: " + event);
  258. ReturnCode status = event.getReturnCode();
  259. switch (status.getValue()) {
  260. case ReturnCode.TRANSACTION_EXECUTED_NORMALLY:
  261. log.info("Connection created properly.");
  262. break;
  263. default:
  264. ReturnCode rc = event.getReturnCode();
  265. log.severe("CRCX failed. Value = " + rc.getValue() + " Comment = "
  266. + rc.getComment());
  267. sendServerError("Failed to create connection, code: "
  268. + event.getReturnCode(), Response.SERVER_INTERNAL_ERROR);
  269. return;
  270. }
  271. boolean startMailMedia = false;
  272. if (event.getSecondEndpointIdentifier() == null) {
  273. // this is response for PR creation
  274. // we have one connection activity, lets send another crcx
  275. // send OK with sdp
  276. DialogActivity da = getDialogActivity();
  277. ServerTransaction txn = getServerTransaction();
  278. if (txn == null) {
  279. log.severe("SIP activity lost, close RTP connection");
  280. releaseState();
  281. return;
  282. }
  283. Request request = txn.getRequest();
  284. ContentTypeHeader contentType = null;
  285. try {
  286. contentType = getHeaderFactory().createContentTypeHeader("application", "sdp");
  287. } catch (ParseException ex) {
  288. }
  289. String localAddress = getSipFactoryProvider().getListeningPoints()[0].getIPAddress();
  290. int localPort = getSipFactoryProvider().getListeningPoints()[0].getPort();
  291. javax.sip.address.Address contactAddress = null;
  292. try {
  293. contactAddress = getAddressFactory().createAddress("sip:" + localAddress + ":" + localPort);
  294. } catch (ParseException ex) {
  295. log.severe(ex.getMessage(), ex);
  296. }
  297. ContactHeader contact = getHeaderFactory().createContactHeader(contactAddress);
  298. Response response = null;
  299. try {
  300. response = getMessageFactory().createResponse(Response.OK, request, contentType, event.getLocalConnectionDescriptor().toString().getBytes());
  301. } catch (ParseException ex) {
  302. }
  303. response.setHeader(contact);
  304. try {
  305. txn.sendResponse(response);
  306. } catch (InvalidArgumentException ex) {
  307. log.severe(ex.getMessage(), ex);
  308. } catch (SipException ex) {
  309. log.severe(ex.getMessage(), ex);
  310. }
  311. EndpointIdentifier endpointID = new EndpointIdentifier(
  312. IVR_ENDPOINT_NAME, mmsBindAddress + ":" + MGCP_PEER_PORT);
  313. CreateConnection createConnection = new CreateConnection(this,
  314. getCallIdentifier(), event .getSpecificEndpointIdentifier(), ConnectionMode.Confrnce);
  315. int txID = mgcpProvider.getUniqueTransactionHandler();
  316. createConnection.setTransactionHandle(txID);
  317. // now set other end
  318. try {
  319. createConnection.setSecondEndpointIdentifier(endpointID);
  320. } catch (ConflictingParameterException e) {
  321. // TODO Auto-generated catch block
  322. e.printStackTrace();
  323. }
  324. MgcpConnectionActivity connectionActivity = null;
  325. try {
  326. connectionActivity = mgcpProvider.getConnectionActivity(txID,
  327. endpointID);
  328. ActivityContextInterface epnAci = mgcpActivityContestInterfaceFactory
  329. .getActivityContextInterface(connectionActivity);
  330. epnAci.attach(getSbbContext().getSbbLocalObject());
  331. // epnAci.attach(getParentCmp());
  332. } catch (FactoryException ex) {
  333. ex.printStackTrace();
  334. } catch (NullPointerException ex) {
  335. ex.printStackTrace();
  336. } catch (UnrecognizedActivityException ex) {
  337. ex.printStackTrace();
  338. }
  339. mgcpProvider
  340. .sendMgcpEvents(new JainMgcpEvent[] { createConnection });
  341. } else {
  342. // this is last
  343. startMailMedia = true;
  344. }
  345. EndpointIdentifier eid = null;
  346. if(startMailMedia)
  347. {
  348. eid = event.getSecondEndpointIdentifier();
  349. }else
  350. {
  351. eid = event.getSpecificEndpointIdentifier();
  352. }
  353. log.info("Creating endpoint activity on: " + eid);
  354. MgcpEndpointActivity eActivity = mgcpProvider.getEndpointActivity(eid);
  355. ActivityContextInterface eAci = mgcpActivityContestInterfaceFactory
  356. .getActivityContextInterface(eActivity);
  357. eAci.attach(this.getSbbContext().getSbbLocalObject());
  358. if (startMailMedia) {
  359. startMailMedia();
  360. }
  361. }
  362. public void onTimerEvent(TimerEvent event, ActivityContextInterface aci) {
  363. }
  364. private void releaseState() {
  365. ActivityContextInterface[] activities = getSbbContext().getActivities();
  366. SbbLocalObject sbbLocalObject = getSbbContext().getSbbLocalObject();
  367. MgcpEndpointActivity mea = getEndpointActivity("ivr");
  368. if(mea!=null)
  369. {
  370. //empty RQNT, this is requiered to flush data.
  371. sendRQNT(null,false,false);
  372. }
  373. for (ActivityContextInterface attachedAci : activities) {
  374. if (attachedAci.getActivity() instanceof Dialog) {
  375. attachedAci.detach(sbbLocalObject);
  376. }
  377. if (attachedAci.getActivity() instanceof MgcpConnectionActivity) {
  378. attachedAci.detach(sbbLocalObject);
  379. }
  380. if ( (attachedAci.getActivity() instanceof MgcpEndpointActivity)) {
  381. attachedAci.detach(sbbLocalObject);
  382. MgcpEndpointActivity mgcpEndpoint = (MgcpEndpointActivity) attachedAci
  383. .getActivity();
  384. DeleteConnection deleteConnection = new DeleteConnection(this,
  385. mgcpEndpoint.getEndpointIdentifier());
  386. deleteConnection.setCallIdentifier(this.getCallIdentifier());
  387. deleteConnection.setTransactionHandle(mgcpProvider.getUniqueTransactionHandler());
  388. mgcpProvider.sendMgcpEvents(
  389. new JainMgcpEvent[] { deleteConnection });
  390. log.info("Delete connections: \n"+deleteConnection);
  391. }
  392. }
  393. this.setCallIdentifier(null);
  394. }
  395. public void onNotificationRequestResponse(NotificationRequestResponse event, ActivityContextInterface aci) {
  396. ReturnCode status = event.getReturnCode();
  397. switch (status.getValue()) {
  398. case ReturnCode.TRANSACTION_EXECUTED_NORMALLY:
  399. log.info("########## VOICE MAIL SBB: RQNT executed properly. TXID: "+event.getTransactionHandle()+" ##########");
  400. break;
  401. default:
  402. ReturnCode rc = event.getReturnCode();
  403. log.info("########## VOICE MAIL SBB: RQNT failed, terminating call. TXID: "+event.getTransactionHandle()+" ##########");
  404. sendByeRequest();
  405. break;
  406. }
  407. }
  408. public void onNotifyRequest(Notify event, ActivityContextInterface aci) {
  409. NotifyResponse response = new NotifyResponse(event.getSource(),
  410. ReturnCode.Transaction_Executed_Normally);
  411. response.setTransactionHandle(event.getTransactionHandle());
  412. log.info("########## VOICE MAIL SBB: Sending Notify response["+response+"] to ["+event+"]["+event.getTransactionHandle()+"] ["+response.getTransactionHandle()+"]##########");
  413. mgcpProvider.sendMgcpEvents(new JainMgcpEvent[] { response });
  414. EventName[] observedEvents = event.getObservedEvents();
  415. for (EventName observedEvent : observedEvents) {
  416. switch (observedEvent.getEventIdentifier().intValue()) {
  417. case MgcpEvent.REPORT_ON_COMPLETION:
  418. log.info("########## VOICE MAIL SBB: Signal completed, event identifier["+observedEvent.getEventIdentifier()+"] ##########");
  419. if(observedEvent.getEventIdentifier().getName().toString().equals("oc"))
  420. {
  421. onAnnouncementComplete();
  422. }
  423. break;
  424. case MgcpEvent.REPORT_FAILURE:
  425. log.info("########## VOICE MAIL SBB: Signal failed, event identifier["+observedEvent.getEventIdentifier()+"] ##########");
  426. //releaseState();
  427. sendByeRequest();
  428. break;
  429. default:
  430. //MGCP RI expects D/dtmfX, but correct is D/X ... hence it fails to match on
  431. //MgcpEvent.DTMF_X .... Thus event ID is wrong....
  432. if(observedEvent.getPackageName().toString().equals("D"))
  433. {
  434. int decodedId = decodeDTMF(observedEvent);
  435. processDTMF(decodedId);
  436. }else
  437. {
  438. log.info("########## VOICE MAIL SBB: Notify on unknown event, event identifier["+observedEvent.getEventIdentifier()+"] identifier["+observedEvent.getEventIdentifier().intValue()+"] ##########");
  439. }
  440. }
  441. }
  442. }
  443. private int decodeDTMF(EventName observed)
  444. {
  445. String eventName = observed.getEventIdentifier().getName();
  446. if(Pattern.matches("\\d", eventName))
  447. {
  448. //digit
  449. int i = Integer.parseInt(eventName);
  450. return MgcpEvent.DTMF_0+i;
  451. } else if(Pattern.matches("[A-D]#*", eventName))
  452. {
  453. switch(eventName.charAt(0))
  454. {
  455. case 'A':
  456. return MgcpEvent.DTMF_A;
  457. case 'B':
  458. return MgcpEvent.DTMF_B;
  459. case 'C':
  460. return MgcpEvent.DTMF_C;
  461. case 'D':
  462. return MgcpEvent.DTMF_D;
  463. case '#':
  464. return MgcpEvent.DTMF_HASH;
  465. case '*':
  466. return MgcpEvent.DTMF_STAR;
  467. default:
  468. return -1;
  469. }
  470. } else
  471. {
  472. return -1;
  473. }
  474. }
  475. private void onAnnouncementComplete() {
  476. log.info("########## VOICE MAIL SBB: onAnnouncementComplete ##########");
  477. boolean record = false;
  478. boolean detectDtmf = true;
  479. if (this.getSameUser()) {
  480. sendRQNT(null, record, detectDtmf);
  481. } else {
  482. ServerTransaction txn = getServerTransaction();
  483. Request request = txn.getRequest();
  484. ToHeader toHeader = (ToHeader) request.getHeader(ToHeader.NAME);
  485. String userName = ((SipURI) toHeader.getAddress().getURI())
  486. .getUser();
  487. String recordFilePath = getAudioFileURL(getAudioFileString(userName));
  488. // if (route != null) {
  489. // recordFilePath = route+File.separator+fileName;
  490. // } else {
  491. // recordFilePath = _DEFAULT_FILE_ROUTE_+File.separator+fileName;
  492. // }
  493. record = true;
  494. detectDtmf = false;
  495. sendRQNT(recordFilePath, record, detectDtmf);
  496. }
  497. }
  498. public void onActivityEndEvent(ActivityEndEvent end,ActivityContextInterface aci)
  499. {
  500. log.info("########## VOICE MAIL SBB: onActivityEndEvent["+aci.getActivity()+"] ##########");
  501. }
  502. private ServerTransaction getServerTransaction() {
  503. ActivityContextInterface[] activities = this.getSbbContext()
  504. .getActivities();
  505. for (int i = 0; i < activities.length; i++) {
  506. if (activities[i].getActivity() instanceof ServerTransaction) {
  507. return (ServerTransaction) activities[i].getActivity();
  508. }
  509. }
  510. return null;
  511. }
  512. private Dialog getDialog() {
  513. ActivityContextInterface[] activities = this.getSbbContext()
  514. .getActivities();
  515. for (int i = 0; i < activities.length; i++) {
  516. if (activities[i].getActivity() instanceof Dialog) {
  517. return (Dialog) activities[i].getActivity();
  518. }
  519. }
  520. return null;
  521. }
  522. /**
  523. * Voice Mail will hang up on caller sending a BYE Request.
  524. *
  525. */
  526. private void sendByeRequest() {
  527. log.info("########## VOICE MAIL SBB: sendByRequest ##########");
  528. try {
  529. SipProvider sipProvider = getSipFactoryProvider();
  530. Dialog dialog = this.getDialog();
  531. if(dialog == null)
  532. {
  533. return;
  534. }
  535. Request request = dialog.createRequest(Request.BYE);
  536. ClientTransaction ct = sipProvider.getNewClientTransaction(request);
  537. dialog.sendRequest(ct);
  538. releaseState();
  539. } catch (TransactionUnavailableException e) {
  540. log.severe(e.getMessage(), e);
  541. } catch (SipException e) {
  542. log.severe(e.getMessage(), e);
  543. }
  544. }
  545. /**
  546. * After receiving a BYE Request, an OK Respose has to be sent.
  547. *
  548. * @param byeEvent
  549. */
  550. private void byeRequestOkResponse(RequestEvent byeEvent) {
  551. log.info("########## VOICE MAIL SBB: byeRequestOkResponse ##########");
  552. Request request = byeEvent.getRequest();
  553. ServerTransaction tx = byeEvent.getServerTransaction();
  554. try {
  555. Response response = getMessageFactory().createResponse(Response.OK,
  556. request);
  557. tx.sendResponse(response);
  558. } catch (Exception e) {
  559. log.severe(e.getMessage(), e);
  560. }
  561. }
  562. /*
  563. * Methods below are required for two reasons. Java seems to be loose with URL and File objects:
  564. *
  565. * File f1= new File("file:///e:/tmp");
  566. * File f2= new File("file://e:/tmp");
  567. * File f3= new File("file:/e:/tmp");
  568. * File f4= new File("file:///e:/tmp");
  569. *
  570. * ONLY f.exists() will return true.
  571. * To make it worst: Class.getResource(String) returns URLs... which have look as follows:
  572. * 'file:/e:/tmp' which is seems to be a violation of 'file:///'.=.
  573. *
  574. * Its better to specify proper one, record signals dont work with 'file:/', announcement
  575. * depends on javax, which seems to be less restrictive
  576. *
  577. */
  578. /**
  579. * Fectch audio file string for passed user name. Used to get file string when called user is not available.
  580. * @param userName
  581. * @return
  582. */
  583. private String getAudioFileString(String userName)
  584. {
  585. String fileName = userName + WAV_EXT;
  586. String recordFilePath = System.getenv(_DEFAULT_RECORDINGS_HOME_)
  587. + File.separator;
  588. if (route != null) {
  589. recordFilePath = recordFilePath+route +File.separator+ fileName;
  590. } else {
  591. //recordFilePath = recordFilePath + fileName;
  592. recordFilePath = recordFilePath+_DEFAULT_FILE_ROUTE_ +File.separator+ fileName;
  593. }
  594. //recordFilePath = new File(recordFilePath).toURI().toString();
  595. log.info("The File to be played = " + recordFilePath);
  596. return recordFilePath;
  597. }
  598. /**
  599. * Fetch audio file string for callee, used to check for recorded message.
  600. * @return
  601. */
  602. private String getAudioFileString() {
  603. FromHeader fromHeader = (FromHeader) this.getInviteRequest().getHeader(
  604. FromHeader.NAME);
  605. return getAudioFileString(((SipURI) fromHeader.getAddress().getURI()).getUser());
  606. }
  607. private String getAudioFileURL(String audioFileString)
  608. {
  609. if(audioFileString.startsWith("file:///"))
  610. return audioFileString;
  611. if(audioFileString.startsWith("file://"))
  612. {
  613. return audioFileString.replace("file://", "file:///");
  614. }
  615. if(audioFileString.startsWith("file:/"))
  616. {
  617. return audioFileString.replace("file:/", "file:///");
  618. }
  619. if(audioFileString.startsWith("/"))
  620. {
  621. return "file://"+audioFileString;
  622. }else
  623. {
  624. return "file:///"+audioFileString;
  625. }
  626. }
  627. private boolean processDTMF(int id) {
  628. String audioFileURL = null;
  629. boolean bye = false;
  630. switch(id)
  631. {
  632. case MgcpEvent.DTMF_1:
  633. String filePath = getAudioFileString();
  634. File f = new File(filePath);
  635. boolean exists = f.exists();
  636. //String audioFileString = "file:/" + filePath;
  637. //linux stuff?
  638. try {
  639. // Just to check if file exist
  640. //File file = new File(filePath);
  641. if (exists) {
  642. audioFileURL = getAudioFileURL(filePath);
  643. } else {
  644. audioFileURL = getAudioFileURL(getClass().getResource(novoicemessage).toString());
  645. }
  646. } catch (NullPointerException npe) {
  647. log.severe(
  648. "Ignore. NullPointerException. The file does not exist "
  649. + filePath, npe);
  650. audioFileURL = getAudioFileURL(getClass().getResource(dtmf1).toString());
  651. }
  652. break;
  653. case MgcpEvent.DTMF_7:
  654. audioFileURL = getAudioFileURL(getClass().getResource(dtmf7).toString());
  655. filePath = getAudioFileString();
  656. File fileToBeDeleted = new File(filePath);
  657. boolean deleted = fileToBeDeleted.delete();
  658. log.info("Deletion of file " + filePath + " is successful = "
  659. + deleted);
  660. break;
  661. case MgcpEvent.DTMF_9:
  662. // audioFileURL = getClass().getResource(dtmf9);
  663. this.sendByeRequest();
  664. bye = true;
  665. break;
  666. default:
  667. log.info("########## VOICE MAIL SBB: Notify on not handled DTMF! ##########");
  668. audioFileURL = getAudioFileURL(getClass().getResource(tryAgain).toString());
  669. }
  670. if (!bye) {
  671. boolean record = false;
  672. boolean detectDtmf = false;
  673. sendRQNT(audioFileURL.toString(), record, detectDtmf);
  674. }
  675. return bye;
  676. }
  677. /**
  678. * To know whether or not the called user has the Voice Mail service
  679. * enabled.
  680. *
  681. * @param sipAddress
  682. * : Called user address.
  683. * @return boolean: TRUE -> Voice Mail enabled. FALSE -> Voice Mail disabled
  684. * for the given user identified by sip address.
  685. */
  686. private boolean isSubscriber(String sipAddress) {
  687. boolean state = false;
  688. CallControlProfileCMP profile = lookup(new Address(AddressPlan.SIP,
  689. sipAddress));
  690. log.info("Retrieved CallControllProfile["+(profile!=null)+"] for user: "+sipAddress);
  691. if (profile != null) {
  692. state = profile.getVoicemailState();
  693. }
  694. return state;
  695. }
  696. /**
  697. * This method is used to know if the it is going to be used the voice mail
  698. * of the same user or the voice mail of a different user.
  699. *
  700. * @param event
  701. * @return TRUE: If the called user is sip:vmail@nist.gov
  702. */
  703. private boolean sameUser(javax.sip.RequestEvent event) {
  704. boolean sameUser = false;
  705. Request inviteRequest = event.getRequest();
  706. // Checking if the called user and the caller are the same
  707. ToHeader toHeader = (ToHeader) inviteRequest.getHeader(ToHeader.NAME);
  708. SipURI toURI = (SipURI) toHeader.getAddress().getURI();
  709. if ((toURI.getUser().equals(USER) && toURI.getHost().equals(HOST))) {
  710. sameUser = true;
  711. }
  712. // Setting Same User value
  713. this.setSameUser(sameUser);
  714. return sameUser;
  715. }
  716. public void setSbbContext(SbbContext context) {
  717. super.setSbbContext(context);
  718. this.log = getSbbContext().getTracer("VoiceMailSbb");
  719. // To create Header objects from a particular implementation of JAIN SIP
  720. headerFactory = getSipFactoryProvider().getHeaderFactory();
  721. try {
  722. Context myEnv = (Context) new InitialContext()
  723. .lookup("java:comp/env");
  724. // Getting Media Resource Adaptor interfaces
  725. mgcpProvider = (JainMgcpProvider) myEnv
  726. .lookup("slee/resources/jainmgcp/2.0/provider");
  727. mgcpActivityContestInterfaceFactory = (MgcpActivityContextInterfaceFactory) myEnv
  728. .lookup("slee/resources/jainmgcp/2.0/acifactory");
  729. // Getting Sip Resource Adaptor interface
  730. sipACIF = (SipActivityContextInterfaceFactory) myEnv
  731. .lookup("slee/resources/jainsip/1.2/acifactory");
  732. // Getting Timer Facility interface
  733. route = (String) myEnv.lookup("filesRoute");
  734. log.info("=== Files Route: "+route+" ===");
  735. mmsBindAddress = (String) myEnv.lookup("server.address");
  736. } catch (NamingException e) {
  737. log.severe(e.getMessage(), e);
  738. }
  739. }
  740. public void sbbPostCreate() throws javax.slee.CreateException {
  741. }
  742. /*
  743. public abstract org.mobicents.slee.examples.callcontrol.profile.CallControlProfileCMP getCallControlProfileCMP(
  744. javax.slee.profile.ProfileID profileID)
  745. throws javax.slee.profile.UnrecognizedProfileNameException,
  746. javax.slee.profile.UnrecognizedProfileTableNameException;
  747. */
  748. public abstract org.mobicents.slee.examples.callcontrol.voicemail.VoiceMailSbbActivityContextInterface asSbbActivityContextInterface(
  749. ActivityContextInterface aci);
  750. private final HeaderFactory getHeaderFactory() {
  751. return headerFactory;
  752. }
  753. private Tracer log;
  754. // Interfaces
  755. private HeaderFactory headerFactory;
  756. private SipActivityContextInterfaceFactory sipACIF;
  757. private MgcpActivityContextInterfaceFactory mgcpActivityContestInterfaceFactory;
  758. private JainMgcpProvider mgcpProvider;
  759. //private TimerFacility timerFacility;
  760. private final String recordAfterTone = "audiofiles/RecordAfterTone.wav";
  761. private final String waitingDTMF = "audiofiles/WaitingDTMF.wav";
  762. private final String dtmf1 = "audiofiles/DTMF1.wav";
  763. private final String dtmf7 = "audiofiles/DTMF7.wav";
  764. private final String dtmf9 = "audiofiles/DTMF9.wav";
  765. private final String tryAgain = "audiofiles/TryAgain.wav";
  766. private final String novoicemessage = "audiofiles/NoVoiceMessage.wav";
  767. private final String USER = "vmail";
  768. private final String HOST = System.getProperty("jboss.bind.address",
  769. "127.0.0.1");
  770. private final String WAV_EXT = ".wav";
  771. public final static String IVR_ENDPOINT_NAME = "mobicents/ivr/$";
  772. // Pre is required since it has capability to transcode
  773. public final static String PRE_ENDPOINT_NAME = "mobicents/relay/$";
  774. public final static String _DEFAULT_FILE_ROUTE_ = "call-controll2";
  775. public final static String _DEFAULT_RECORDINGS_HOME_="MOBICENTS_SLEE_EXAMPLE_CC2_RECORDINGS_HOME";
  776. private String route = null;
  777. protected String mmsBindAddress;
  778. public final static String JBOSS_BIND_ADDRESS = System.getProperty(
  779. "jboss.bind.address", "127.0.0.1");
  780. public static final int MGCP_PEER_PORT = 2427;
  781. public static final int MGCP_PORT = 2727;
  782. /**
  783. * ***************************************** ************** CMP Fields
  784. * *************** *****************************************
  785. */
  786. // 'inviteRequest' CMP field setter
  787. public abstract void setInviteRequest(Request value);
  788. // 'inviteRequest' CMP field getter
  789. public abstract Request getInviteRequest();
  790. // 'sameUser' CMP field setter
  791. public abstract void setSameUser(boolean value);
  792. // 'sameUser' CMP field getter
  793. public abstract boolean getSameUser();
  794. public abstract void setCallIdentifier(CallIdentifier cid);
  795. public abstract CallIdentifier getCallIdentifier();
  796. // //////////////////
  797. // Helper Methods //
  798. // //////////////////
  799. private MgcpEndpointActivity getEndpointActivity(String ePartialID) {
  800. for (ActivityContextInterface aci : getSbbContext().getActivities()) {
  801. if (aci.getActivity() instanceof MgcpEndpointActivity) {
  802. MgcpEndpointActivity activity = (MgcpEndpointActivity) aci
  803. .getActivity();
  804. if (activity.getEndpointIdentifier().toString().toLowerCase().contains(ePartialID.toLowerCase())) {
  805. return activity;
  806. }
  807. }
  808. }
  809. return null;
  810. }
  811. private DialogActivity getDialogActivity() {
  812. for (ActivityContextInterface aci : getSbbContext().getActivities()) {
  813. if (aci.getActivity() instanceof DialogActivity) {
  814. return (DialogActivity) aci.getActivity();
  815. }
  816. }
  817. return null;
  818. }
  819. private void startMailMedia() {
  820. URL audioFileURL = null;
  821. boolean waitDtmf = false;
  822. boolean record = false;
  823. if (this.getSameUser()) {
  824. log.info("same user, lets play the voice mail");
  825. String audioFile = getAudioFileString();
  826. File file = null;
  827. boolean fileExist = false;
  828. try {
  829. file = new File(audioFile);
  830. fileExist = file.exists();
  831. } catch (NullPointerException npe) {
  832. // Ignore
  833. }
  834. if (fileExist) {
  835. audioFileURL = getClass().getResource(waitingDTMF);
  836. } else {
  837. log.info("Mail media file does not exist: "+file);
  838. audioFileURL = getClass().getResource(novoicemessage);
  839. }
  840. } else {
  841. log.info("not the same user, start recording after announcement");
  842. audioFileURL = getClass().getResource(recordAfterTone);
  843. }
  844. MgcpEndpointActivity mea = getEndpointActivity("ivr");
  845. log.info("########## VOICE MAIL SBB: Execute on ["
  846. + mea
  847. + "] ##########");
  848. String stringURL = getAudioFileURL(audioFileURL.toString());
  849. sendRQNT(stringURL, record, waitDtmf);
  850. }
  851. private MgcpConnectionActivity getConnectionActivity(EndpointIdentifier eid) {
  852. for (ActivityContextInterface aci : getSbbContext().getActivities()) {
  853. if (aci.getActivity() instanceof MgcpConnectionActivity) {
  854. MgcpConnectionActivity activity = (MgcpConnectionActivity) aci
  855. .getActivity();
  856. if (activity.getEndpointIdentifier().equals(eid)) {
  857. return activity;
  858. }
  859. }
  860. }
  861. return null;
  862. }
  863. public void sendRQNT(String audioFileUrl, boolean record, boolean detectDtmf) {
  864. MgcpEndpointActivity endpointActivity = getEndpointActivity("ivr");
  865. if (endpointActivity == null) {
  866. // bad practice
  867. throw new RuntimeException("There is no IVR endpoint activity");
  868. }
  869. // MgcpConnectionActivity connectionActivity = getConnectionActivity(endpointActivity
  870. // .getEndpointIdentifier());
  871. // if (connectionActivity == null) {
  872. // // bad practice
  873. // throw new RuntimeException(
  874. // "There is no IVR connection activity");
  875. // }
  876. EndpointIdentifier endpointID = endpointActivity
  877. .getEndpointIdentifier();
  878. // ConnectionIdentifier connectionID = new ConnectionIdentifier(
  879. // connectionActivity.getConnectionIdentifier());
  880. NotificationRequest notificationRequest = new NotificationRequest(this,
  881. endpointID, mgcpProvider.getUniqueRequestIdentifier());
  882. RequestedAction[] actions = new RequestedAction[] { RequestedAction.NotifyImmediately };
  883. if (audioFileUrl != null) {
  884. EventName[] signalRequests = null;
  885. if (!record) {
  886. signalRequests = new EventName[] { new EventName(
  887. AUPackage.AU, MgcpEvent.factory("pa").withParm("an="+audioFileUrl)/*,connectionID*/) };
  888. RequestedEvent[] requestedEvents = {
  889. new RequestedEvent(new EventName(AUPackage.AU, MgcpEvent.oc/*,connectionID*/), actions),
  890. new RequestedEvent(new EventName(AUPackage.AU, MgcpEvent.of/*,connectionID*/), actions), };
  891. notificationRequest.setRequestedEvents(requestedEvents);
  892. } else {
  893. signalRequests = new EventName[] { new EventName(AUPackage.AU, MgcpEvent.factory("pr")
  894. .withParm("ri="+audioFileUrl+" oa=true")/*,connectionID*/) };
  895. RequestedEvent[] requestedEvents = {
  896. new RequestedEvent(new EventName(AUPackage.AU, MgcpEvent.oc/*,connectionID*/), actions),
  897. new RequestedEvent(new EventName(AUPackage.AU, MgcpEvent.of/*,connectionID*/), actions), };
  898. notificationRequest.setRequestedEvents(requestedEvents);
  899. }
  900. notificationRequest.setSignalRequests(signalRequests);
  901. //add notification, in case dtmf part is not included
  902. }
  903. if (detectDtmf) {
  904. RequestedEvent[] requestedEvents = null;
  905. // This has to be present, since MGCP states that new RQNT erases
  906. // previous set.
  907. RequestedEvent[] requestedDtmfEvents = {
  908. new RequestedEvent(new EventName(AUPackage.AU, MgcpEvent.oc/* , connectionID */), actions),
  909. new RequestedEvent(new EventName(AUPackage.AU, MgcpEvent.of/* , connectionID */), actions),
  910. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("0")/* , connectionID */), actions),
  911. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("1")/* , connectionID */), actions),
  912. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("2")/* , connectionID */), actions),
  913. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("3")/* , connectionID */), actions),
  914. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("4")/* , connectionID */), actions),
  915. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("5")/* , connectionID */), actions),
  916. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("6")/* , connectionID */), actions),
  917. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("7")/* , connectionID */), actions),
  918. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("8")/* , connectionID */), actions),
  919. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("9")/* , connectionID */), actions),
  920. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("A")/* , connectionID */), actions),
  921. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("B")/* , connectionID */), actions),
  922. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("C")/* , connectionID */), actions),
  923. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("D")/* , connectionID */), actions),
  924. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("*")/* , connectionID */), actions),
  925. new RequestedEvent(new EventName(PackageName.Dtmf, MgcpEvent.factory("#")/* , connectionID */), actions) };
  926. if(notificationRequest.getRequestedEvents()!=null)
  927. {
  928. //we have something
  929. ArrayList<RequestedEvent> listOfEvents = new ArrayList<RequestedEvent>();
  930. //add old ones - tahts A or AU
  931. for(RequestedEvent re:notificationRequest.getRequestedEvents())
  932. {
  933. listOfEvents.add(re);
  934. }
  935. //now dtmfs;
  936. for(RequestedEvent re:requestedDtmfEvents)
  937. {
  938. listOfEvents.add(re);
  939. }
  940. requestedEvents = new RequestedEvent[listOfEvents.size()];
  941. requestedEvents = listOfEvents.toArray(requestedEvents);
  942. }else
  943. {
  944. requestedEvents = requestedDtmfEvents;
  945. }
  946. notificationRequest.setRequestedEvents(requestedEvents);
  947. }
  948. notificationRequest.setTransactionHandle(mgcpProvider
  949. .getUniqueTransactionHandler());
  950. NotifiedEntity notifiedEntity = new NotifiedEntity(JBOSS_BIND_ADDRESS,
  951. JBOSS_BIND_ADDRESS, MGCP_PORT);
  952. notificationRequest.setNotifiedEntity(notifiedEntity);
  953. // we can send empty RQNT, that is clean all req.
  954. mgcpProvider
  955. .sendMgcpEvents(new JainMgcpEvent[] { notificationRequest });
  956. log.info(" NotificationRequest sent: \n" + notificationRequest);
  957. }
  958. }