PageRenderTime 43ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/jain-sip-ha/jboss-4/src/test/java/org/mobicents/ha/javax/sip/SimpleB2BUAHandler.java

http://mobicents.googlecode.com/
Java | 515 lines | 371 code | 47 blank | 97 comment | 56 complexity | 7c8bed96cf620ec90464a5253d241e98 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.ha.javax.sip;
  23. import gov.nist.javax.sip.ListeningPointImpl;
  24. import gov.nist.javax.sip.header.Route;
  25. import gov.nist.javax.sip.header.RouteList;
  26. import gov.nist.javax.sip.header.Via;
  27. import gov.nist.javax.sip.header.ViaList;
  28. import gov.nist.javax.sip.message.SIPRequest;
  29. import java.text.ParseException;
  30. import java.util.Collections;
  31. import java.util.HashSet;
  32. import java.util.Iterator;
  33. import java.util.ListIterator;
  34. import java.util.Set;
  35. import java.util.concurrent.atomic.AtomicLong;
  36. import javax.sip.ClientTransaction;
  37. import javax.sip.Dialog;
  38. import javax.sip.InvalidArgumentException;
  39. import javax.sip.RequestEvent;
  40. import javax.sip.ResponseEvent;
  41. import javax.sip.ServerTransaction;
  42. import javax.sip.SipException;
  43. import javax.sip.SipProvider;
  44. import javax.sip.SipStack;
  45. import javax.sip.address.SipURI;
  46. import javax.sip.address.URI;
  47. import javax.sip.header.CSeqHeader;
  48. import javax.sip.header.CallIdHeader;
  49. import javax.sip.header.ContactHeader;
  50. import javax.sip.header.ContentLengthHeader;
  51. import javax.sip.header.ContentTypeHeader;
  52. import javax.sip.header.EventHeader;
  53. import javax.sip.header.ExpiresHeader;
  54. import javax.sip.header.FromHeader;
  55. import javax.sip.header.Header;
  56. import javax.sip.header.HeaderFactory;
  57. import javax.sip.header.RecordRouteHeader;
  58. import javax.sip.header.RouteHeader;
  59. import javax.sip.header.SubscriptionStateHeader;
  60. import javax.sip.header.ToHeader;
  61. import javax.sip.header.ViaHeader;
  62. import javax.sip.message.MessageFactory;
  63. import javax.sip.message.Request;
  64. import javax.sip.message.Response;
  65. import org.jboss.cache.CacheException;
  66. import org.jboss.cache.Fqn;
  67. import org.mobicents.ha.javax.sip.cache.JBossTreeSipCache;
  68. public class SimpleB2BUAHandler {
  69. private SipProvider sipProvider;
  70. private MessageFactory messageFactory;
  71. private HeaderFactory headerFactory;
  72. private ServerTransaction serverTransaction;
  73. private boolean createInviteOnAck = false;
  74. private SipStack sipStack;
  75. int myPort;
  76. public SimpleB2BUAHandler(SipProvider sipProvider, HeaderFactory headerFactory, MessageFactory messageFactory, int port) {
  77. // this.localTag = localTag;
  78. this.sipProvider = sipProvider;
  79. this.sipStack = sipProvider.getSipStack();
  80. this.messageFactory = messageFactory;
  81. this.headerFactory = headerFactory;
  82. myPort = port;
  83. }
  84. /**
  85. * @return the incomingDialog
  86. */
  87. public String getIncomingDialogId() {
  88. String incomingDialogId = null;
  89. try {
  90. incomingDialogId = (String) ((JBossTreeSipCache)((ClusteredSipStack)sipProvider.getSipStack()).getSipCache()).getCache().get(Fqn.fromString("DIALOG_IDS"), "incomingDialogId");
  91. } catch (CacheException e) {
  92. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  93. }
  94. return incomingDialogId;
  95. }
  96. /**
  97. * @return the outgoingDialog
  98. */
  99. public String getOutgoingDialogId() {
  100. String outgoingDialogId = null;
  101. try {
  102. outgoingDialogId = (String) ((JBossTreeSipCache)((ClusteredSipStack)sipProvider.getSipStack()).getSipCache()).getCache().get(Fqn.fromString("DIALOG_IDS"), "outgoingDialogId");
  103. } catch (CacheException e) {
  104. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  105. }
  106. return outgoingDialogId;
  107. }
  108. /**
  109. * @return the incomingDialog
  110. */
  111. public Dialog getIncomingDialog() {
  112. String incomingDialogId = null;
  113. try {
  114. incomingDialogId = (String) ((JBossTreeSipCache)((ClusteredSipStack)sipProvider.getSipStack()).getSipCache()).getCache().get(Fqn.fromString("DIALOG_IDS"), "incomingDialogId");
  115. } catch (CacheException e) {
  116. // TODO Auto-generated catch block
  117. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  118. }
  119. if(incomingDialogId == null) {
  120. return null;
  121. }
  122. return ((ClusteredSipStack)sipProvider.getSipStack()).getDialog(incomingDialogId);
  123. }
  124. /**
  125. * @return the outgoingDialog
  126. */
  127. public Dialog getOutgoingDialog() {
  128. String outgoingDialogId = null;
  129. try {
  130. outgoingDialogId = (String) ((JBossTreeSipCache)((ClusteredSipStack)sipProvider.getSipStack()).getSipCache()).getCache().get(Fqn.fromString("DIALOG_IDS"), "outgoingDialogId");
  131. } catch (CacheException e) {
  132. // TODO Auto-generated catch block
  133. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  134. }
  135. if(outgoingDialogId == null) {
  136. return null;
  137. }
  138. return ((ClusteredSipStack)sipProvider.getSipStack()).getDialog(outgoingDialogId);
  139. }
  140. private void storeOutgoingDialogId(String outgoingDialogId) {
  141. try {
  142. ((JBossTreeSipCache)((ClusteredSipStack)sipProvider.getSipStack()).getSipCache()).getCache().put(Fqn.fromString("DIALOG_IDS"), "outgoingDialogId", outgoingDialogId);
  143. } catch (CacheException e) {
  144. // TODO Auto-generated catch block
  145. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  146. }
  147. }
  148. private void storeIncomingDialogId(String incomingDialogId) {
  149. try {
  150. ((JBossTreeSipCache)((ClusteredSipStack)sipProvider.getSipStack()).getSipCache()).getCache().put(Fqn.fromString("DIALOG_IDS"), "incomingDialogId", incomingDialogId);
  151. } catch (CacheException e) {
  152. // TODO Auto-generated catch block
  153. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  154. }
  155. }
  156. public void processInvite(RequestEvent requestEvent) {
  157. //System.out.println("Got invite: "+requestEvent.getRequest());
  158. try {
  159. serverTransaction = requestEvent.getServerTransaction();
  160. if (serverTransaction == null) {
  161. try {
  162. serverTransaction = sipProvider.getNewServerTransaction(requestEvent.getRequest());
  163. }
  164. catch (Exception e) {
  165. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  166. return;
  167. }
  168. }
  169. //serverTransaction.sendResponse(messageFactory.createResponse(100, requestEvent.getRequest()));
  170. if(serverTransaction.getDialog() == null) {
  171. setupIncomingDialog();
  172. forwardInvite(5070);
  173. } else {
  174. Request request = getIncomingDialog().createRequest(Request.INVITE);
  175. final ClientTransaction ct = sipProvider.getNewClientTransaction(request);
  176. getIncomingDialog().sendRequest(ct);
  177. }
  178. // not used in basic reinvite
  179. if(((FromHeader)serverTransaction.getRequest().getHeader(FromHeader.NAME)).getAddress().getURI().toString().contains("ReInviteSubsNotify")) {
  180. createInviteOnAck = true;
  181. }
  182. } catch (Exception ex) {
  183. ex.printStackTrace();
  184. }
  185. }
  186. /**
  187. * @param serverTransaction2
  188. * @return
  189. * @throws SipException
  190. */
  191. private void setupIncomingDialog() throws SipException {
  192. Dialog incomingDialog = sipProvider.getNewDialog(serverTransaction);
  193. incomingDialog.setApplicationData(this);
  194. }
  195. /**
  196. * @param incomingDialog2
  197. * @return
  198. * @throws SipException
  199. */
  200. private void forwardInvite(int peerPort) throws SipException {
  201. Request request = createRequest(serverTransaction.getRequest(), peerPort);
  202. ClientTransaction ct = sipProvider.getNewClientTransaction(request);
  203. Dialog outgoingDialog = sipProvider.getNewDialog(ct);
  204. outgoingDialog.setApplicationData(this);
  205. ct.sendRequest();
  206. }
  207. @SuppressWarnings("unchecked")
  208. private Request createRequest(Request origRequest, int peerPort) throws SipException {
  209. final SIPRequest request = (SIPRequest) origRequest.clone();
  210. try {
  211. long l = new AtomicLong().incrementAndGet();
  212. request.getFromHeader().setTag(Long.toString(l));
  213. } catch (ParseException e1) {
  214. throw new SipException("failed to set local tag", e1);
  215. }
  216. final String transport = request.getTopmostViaHeader().getTransport();
  217. final ListeningPointImpl listeningPointImpl = (ListeningPointImpl) sipProvider.getListeningPoint(transport);
  218. final ViaList viaList = new ViaList();
  219. viaList.add((Via) listeningPointImpl.createViaHeader());
  220. request.setVia(viaList);
  221. try {
  222. request.setHeader(headerFactory.createMaxForwardsHeader(70));
  223. } catch (InvalidArgumentException e) {
  224. throw new SipException("Failed to create max forwards header",e);
  225. }
  226. request.setHeader((Header) sipProvider.getNewCallId());
  227. // note: cseq will be set by dialog when sending
  228. // set contact if the original response had it
  229. if (origRequest.getHeader(ContactHeader.NAME) != null) {
  230. request.setHeader(listeningPointImpl.createContactHeader());
  231. }
  232. /*
  233. * Route header fields of the upstream request MAY be copied in the
  234. * downstream request, except the topmost Route header if it is under
  235. * the responsibility of the B2BUA. Additional Route header fields MAY
  236. * also be added to the downstream request.
  237. */
  238. if (getOutgoingDialog() == null || getOutgoingDialog().getState() == null) {
  239. // first request, no route available
  240. final RouteList routeList = request.getRouteHeaders();
  241. if (routeList != null) {
  242. final RouteHeader topRoute = routeList.get(0);
  243. final URI topRouteURI = topRoute.getAddress().getURI();
  244. if (topRouteURI.isSipURI()) {
  245. final SipURI topRouteSipURI = (SipURI) topRouteURI;
  246. if (topRouteSipURI.getHost().equals(listeningPointImpl.getIPAddress())
  247. && topRouteSipURI.getPort() == listeningPointImpl.getPort()) {
  248. if (routeList.size() > 1) {
  249. routeList.remove(0);
  250. }
  251. else {
  252. request.removeHeader(RouteHeader.NAME);
  253. }
  254. }
  255. }
  256. }
  257. }
  258. else {
  259. // replace route in orig request with the one in dialog
  260. request.removeHeader(RouteHeader.NAME);
  261. final RouteList routeList = new RouteList();
  262. for (Iterator<Route> it = getOutgoingDialog().getRouteSet(); it.hasNext();) {
  263. Route route = it.next();
  264. routeList.add(route);
  265. }
  266. if (!routeList.isEmpty()) {
  267. request.addHeader(routeList);
  268. }
  269. }
  270. /*
  271. * Record-Route header fields of the upstream request are not copied in
  272. * the new downstream request, as Record-Route is only meaningful for
  273. * the upstream dialog.
  274. */
  275. request.removeHeader(RecordRouteHeader.NAME);
  276. ((SipURI)request.getRequestURI()).setPort(peerPort);
  277. return request;
  278. }
  279. public void processAck(RequestEvent requestEvent) {
  280. // ignore
  281. try {
  282. if(myPort == 5080 && getIncomingDialogId() == null) {
  283. storeIncomingDialogId(requestEvent.getDialog().getDialogId());
  284. }
  285. if(createInviteOnAck) {
  286. createInviteOnAck = false;
  287. Request request = getIncomingDialog().createRequest("INVITE");
  288. final ClientTransaction ct = sipProvider.getNewClientTransaction(request);
  289. getIncomingDialog().sendRequest(ct);
  290. }
  291. } catch (SipException e) {
  292. // TODO Auto-generated catch block
  293. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  294. }
  295. }
  296. public void processBye(RequestEvent requestEvent) {
  297. try {
  298. requestEvent.getServerTransaction().sendResponse(messageFactory.createResponse(200, requestEvent.getRequest()));
  299. Dialog dialog = getOutgoingDialog();
  300. Request request = dialog.createRequest(Request.BYE);
  301. final ClientTransaction ct = sipProvider.getNewClientTransaction(request);
  302. dialog.sendRequest(ct);
  303. }
  304. catch (Exception e) {
  305. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  306. }
  307. }
  308. public void processSubscribe(RequestEvent requestEvent) {
  309. try {
  310. Response response = messageFactory.createResponse(200, requestEvent.getRequest());
  311. response.addHeader(headerFactory.createHeader(ExpiresHeader.NAME, "3600"));
  312. requestEvent.getServerTransaction().sendResponse(response);
  313. Dialog dialog = getOutgoingDialog();
  314. Request request = dialog.createRequest(Request.SUBSCRIBE);
  315. ((SipURI)request.getRequestURI()).setPort(5070);
  316. final ClientTransaction ct = sipProvider.getNewClientTransaction(request);
  317. dialog.sendRequest(ct);
  318. }
  319. catch (Exception e) {
  320. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  321. }
  322. }
  323. public void processNotify(RequestEvent requestEvent) {
  324. try {
  325. requestEvent.getServerTransaction().sendResponse(messageFactory.createResponse(200, requestEvent.getRequest()));
  326. Dialog dialog = getIncomingDialog();
  327. Request request = dialog.createRequest(Request.NOTIFY);
  328. request.addHeader(headerFactory.createHeader(SubscriptionStateHeader.NAME, SubscriptionStateHeader.ACTIVE));
  329. request.addHeader(headerFactory.createHeader(EventHeader.NAME, "presence"));
  330. ((SipURI)request.getRequestURI()).setUser(null);
  331. // ((SipURI)request.getRequestURI()).setHost(IP_ADDRESS);
  332. ((SipURI)request.getRequestURI()).setPort(5060);
  333. final ClientTransaction ct = sipProvider.getNewClientTransaction(request);
  334. dialog.sendRequest(ct);
  335. }
  336. catch (Exception e) {
  337. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  338. }
  339. }
  340. public void process180(ResponseEvent responseEvent) {
  341. try {
  342. forwardResponse(responseEvent.getResponse());
  343. }
  344. catch (Exception e) {
  345. ((SipStackImpl)sipStack).getStackLogger().logError("unexpected exception", e);
  346. }
  347. }
  348. /**
  349. * @param responseEvent
  350. * @throws InvalidArgumentException
  351. */
  352. @SuppressWarnings("unchecked")
  353. private void forwardResponse(Response receivedResponse) throws SipException, InvalidArgumentException {
  354. if(receivedResponse.getStatusCode() == 202){
  355. return;
  356. }
  357. System.out.println("Forwarding " + receivedResponse);
  358. final ServerTransaction origServerTransaction = this.serverTransaction;
  359. Response forgedResponse = null;
  360. try {
  361. forgedResponse = messageFactory.createResponse(receivedResponse.getStatusCode(), origServerTransaction.getRequest());
  362. } catch (ParseException e) {
  363. throw new SipException("Failed to forge message", e);
  364. }
  365. // if ((getIncomingDialog() == null || getIncomingDialog().getState() == DialogState.EARLY) && localTag != null && getIncomingDialog() != null && getIncomingDialog().isServer()) {
  366. // // no tag set in the response, since the dialog creating transaction didn't had it
  367. // try {
  368. // ((ToHeader)forgedResponse.getHeader(ToHeader.NAME)).setTag(localTag);
  369. // } catch (ParseException e) {
  370. // throw new SipException("Failed to set local tag", e);
  371. // }
  372. // }
  373. // copy headers
  374. ListIterator<String> lit = receivedResponse.getHeaderNames();
  375. String headerName = null;
  376. ListIterator<Header> headersIterator = null;
  377. while (lit.hasNext()) {
  378. headerName = lit.next();
  379. if (SimpleB2BUAHandler.getHeadersToOmmitOnResponseCopy().contains(headerName)) {
  380. continue;
  381. } else {
  382. forgedResponse.removeHeader(headerName);
  383. headersIterator = receivedResponse.getHeaders(headerName);
  384. while (headersIterator.hasNext()) {
  385. forgedResponse.addLast((Header)headersIterator.next().clone());
  386. }
  387. }
  388. }
  389. // Copy content
  390. final byte[] rawOriginal = receivedResponse.getRawContent();
  391. if (rawOriginal != null && rawOriginal.length != 0) {
  392. final byte[] copy = new byte[rawOriginal.length];
  393. System.arraycopy(rawOriginal, 0, copy, 0, copy.length);
  394. try {
  395. forgedResponse.setContent(copy, (ContentTypeHeader) forgedResponse
  396. .getHeader(ContentTypeHeader.NAME));
  397. } catch (ParseException e) {
  398. throw new SipException("Failed to copy content.",e);
  399. }
  400. }
  401. // set contact if the received response had it
  402. if (receivedResponse.getHeader(ContactHeader.NAME) != null) {
  403. final String transport = ((ViaHeader) forgedResponse.getHeader(ViaHeader.NAME)).getTransport();
  404. forgedResponse.setHeader(((ListeningPointImpl)sipProvider.getListeningPoint(transport)).createContactHeader());
  405. }
  406. origServerTransaction.sendResponse(forgedResponse);
  407. }
  408. public void process200(ResponseEvent responseEvent) {
  409. try {
  410. final CSeqHeader cSeqHeader = (CSeqHeader) responseEvent.getResponse().getHeader(CSeqHeader.NAME);
  411. if (cSeqHeader.getMethod().equals(Request.INVITE)) {
  412. processInvite200(responseEvent,cSeqHeader);
  413. }
  414. else if (cSeqHeader.getMethod().equals(Request.BYE) || cSeqHeader.getMethod().equals(Request.SUBSCRIBE) || cSeqHeader.getMethod().equals(Request.NOTIFY)) {
  415. processBye200(responseEvent);
  416. }
  417. else {
  418. System.err.println("Unexpected response: "+responseEvent.getResponse());
  419. }
  420. } catch (Exception ex) {
  421. ex.printStackTrace();
  422. }
  423. }
  424. /**
  425. * @param responseEvent
  426. * @throws SipException
  427. * @throws InvalidArgumentException
  428. */
  429. private void processInvite200(ResponseEvent responseEvent,CSeqHeader cseq) throws InvalidArgumentException, SipException {
  430. // lets ack it ourselves to avoid UAS retransmissions due to
  431. // forwarding of this response and further UAC Ack
  432. // note that the app does not handles UAC ACKs
  433. System.out.println("Generating ACK to 200");
  434. final Request ack = responseEvent.getDialog().createAck(cseq.getSeqNumber());
  435. String outgoingDialogId = responseEvent.getDialog().getDialogId();
  436. if(myPort == 5080 && getOutgoingDialogId() == null) {
  437. storeOutgoingDialogId(outgoingDialogId);
  438. }
  439. responseEvent.getDialog().sendAck(ack);
  440. forwardResponse(responseEvent.getResponse());
  441. }
  442. /**
  443. * @param responseEvent
  444. */
  445. private void processBye200(ResponseEvent responseEvent) {
  446. // nothing to do
  447. }
  448. private static Set<String> HEADERS_TO_OMMIT_ON_RESPONSE_COPY;
  449. private static Set<String> getHeadersToOmmitOnResponseCopy() {
  450. if (HEADERS_TO_OMMIT_ON_RESPONSE_COPY == null) {
  451. final Set<String> set = new HashSet<String>();
  452. set.add(RouteHeader.NAME);
  453. set.add(RecordRouteHeader.NAME);
  454. set.add(ViaHeader.NAME);
  455. set.add(CallIdHeader.NAME);
  456. set.add(CSeqHeader.NAME);
  457. set.add(ContactHeader.NAME);
  458. set.add(FromHeader.NAME);
  459. set.add(ToHeader.NAME);
  460. set.add(ContentLengthHeader.NAME);
  461. HEADERS_TO_OMMIT_ON_RESPONSE_COPY = Collections.unmodifiableSet(set);
  462. }
  463. return HEADERS_TO_OMMIT_ON_RESPONSE_COPY;
  464. }
  465. public void checkState() {
  466. // TODO Auto-generated method stub
  467. }
  468. }