PageRenderTime 43ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/servers/sip-presence/sip-event/server/publication/sbb/src/main/java/org/mobicents/slee/sipevent/server/publication/SipPublicationControlSbb.java

http://mobicents.googlecode.com/
Java | 522 lines | 323 code | 59 blank | 140 comment | 79 complexity | 51ddff624c0e1995508076eb460fd60c 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.sipevent.server.publication;
  23. import gov.nist.javax.sip.header.ims.PChargingFunctionAddressesHeader;
  24. import gov.nist.javax.sip.header.ims.PChargingVectorHeader;
  25. import javax.naming.Context;
  26. import javax.naming.InitialContext;
  27. import javax.sip.RequestEvent;
  28. import javax.sip.ServerTransaction;
  29. import javax.sip.address.URI;
  30. import javax.sip.header.ContentTypeHeader;
  31. import javax.sip.header.EventHeader;
  32. import javax.sip.header.ExpiresHeader;
  33. import javax.sip.header.HeaderFactory;
  34. import javax.sip.header.SIPIfMatchHeader;
  35. import javax.sip.message.MessageFactory;
  36. import javax.sip.message.Request;
  37. import javax.sip.message.Response;
  38. import javax.slee.ActivityContextInterface;
  39. import javax.slee.CreateException;
  40. import javax.slee.InitialEventSelector;
  41. import javax.slee.RolledBackContext;
  42. import javax.slee.Sbb;
  43. import javax.slee.SbbContext;
  44. import javax.slee.SbbLocalObject;
  45. import javax.slee.serviceactivity.ServiceStartedEvent;
  46. import net.java.slee.resource.sip.SleeSipProvider;
  47. import org.apache.log4j.Logger;
  48. import org.mobicents.slee.ChildRelationExt;
  49. import org.mobicents.slee.sipevent.server.publication.jmx.PublicationControlManagement;
  50. import org.mobicents.slee.sipevent.server.publication.jmx.PublicationControlManagementMBean;
  51. /**
  52. * Sbb to control publication of sip events.
  53. *
  54. * @author Eduardo Martins
  55. *
  56. */
  57. public abstract class SipPublicationControlSbb implements Sbb, PublicationClientControlParent {
  58. private static Logger logger = Logger.getLogger(SipPublicationControlSbb.class);
  59. // JAIN-SIP provider & factories
  60. private SleeSipProvider sipProvider;
  61. private MessageFactory messageFactory;
  62. private HeaderFactory headerFactory;
  63. protected SbbContext sbbContext;
  64. private Context jndiContext;
  65. /**
  66. * SbbObject's context setting
  67. */
  68. public void setSbbContext(SbbContext sbbContext) {
  69. this.sbbContext=sbbContext;
  70. // retrieve factories, facilities & providers
  71. try {
  72. jndiContext = (Context) new InitialContext().lookup("java:comp/env");
  73. sipProvider = (SleeSipProvider)jndiContext.lookup("slee/resources/jainsip/1.2/provider");
  74. headerFactory = sipProvider.getHeaderFactory();
  75. messageFactory = sipProvider.getMessageFactory();
  76. }
  77. catch (Exception e) {
  78. logger.error("Unable to retrieve factories, facilities & providers",e);
  79. }
  80. }
  81. // --- INTERNAL CHILD SBB
  82. public abstract ChildRelationExt getPublicationControlChildRelation();
  83. private PublicationControlSbbLocalObject getPublicationControlChildSbb() {
  84. ChildRelationExt childRelation = getPublicationControlChildRelation();
  85. PublicationControlSbbLocalObject child = (PublicationControlSbbLocalObject) childRelation.get(ChildRelationExt.DEFAULT_CHILD_NAME);
  86. if (child == null) {
  87. try {
  88. child = (PublicationControlSbbLocalObject) childRelation.create(ChildRelationExt.DEFAULT_CHILD_NAME);
  89. } catch (Exception e) {
  90. logger.error("Failed to create child sbb",e);
  91. }
  92. }
  93. return child;
  94. }
  95. // -- CONFIGURATION
  96. /**
  97. * Retrieves the current configuration for this component from an MBean
  98. * @return
  99. */
  100. public static PublicationControlManagementMBean getConfiguration() {
  101. return PublicationControlManagement.getInstance();
  102. }
  103. /**
  104. * Initial event selector method for the PUBLISH event, defines the
  105. * presentity as the custom name.
  106. *
  107. * @param ies
  108. * @return
  109. */
  110. public InitialEventSelector iesPublish(InitialEventSelector ies) {
  111. final RequestEvent event = (RequestEvent) ies.getEvent();
  112. ies.setCustomName(event.getRequest().getRequestURI().toString());
  113. return ies;
  114. }
  115. // ----------- EVENT HANDLERS
  116. /**
  117. * PUBLISH event processing
  118. *
  119. * @param event
  120. * @param aci
  121. */
  122. public void onPublish(RequestEvent event,
  123. ActivityContextInterface aci) {
  124. // detach from aci, we don't want ot handle the activity end event
  125. SbbLocalObject sbbLocalObject = this.sbbContext.getSbbLocalObject();
  126. aci.detach(sbbLocalObject);
  127. // get child sbb that handles all the publication logic
  128. PublicationControlSbbLocalObject childSbb = getPublicationControlChildSbb();
  129. if (logger.isDebugEnabled()) {
  130. logger.debug("Processing PUBLISH request...");
  131. }
  132. if (childSbb == null) {
  133. try {
  134. // create response
  135. Response response = messageFactory.createResponse(Response.SERVER_INTERNAL_ERROR,event.getRequest());
  136. event.getServerTransaction().sendResponse(response);
  137. }
  138. catch (Exception f) {
  139. logger.error("Can't send error response!",f);
  140. }
  141. return;
  142. }
  143. /*
  144. * The presence of a body and the SIP-If-Match header field
  145. * determine the specific operation that the request is performing,
  146. * as described in Table 1.
  147. * +-----------+-------+---------------+---------------+
  148. * | Operation | Body? | SIP-If-Match? | Expires Value |
  149. * +-----------+-------+---------------+---------------+
  150. * | Initial | yes | no | > 0 |
  151. * | Refresh | no | yes | > 0 |
  152. * | Modify | yes | yes | > 0 |
  153. * | Remove | no | yes | 0 |
  154. * +-----------+-------+---------------+---------------+
  155. * Table 1: Publication Operations
  156. *
  157. * If expires does not exist then the service must choose it's value
  158. */
  159. // get event header
  160. EventHeader eventHeader = (EventHeader) event.getRequest().getHeader(
  161. EventHeader.NAME);
  162. if (eventHeader != null) {
  163. // check event package
  164. String eventPackage = eventHeader.getEventType();
  165. if (acceptsEventPackage(eventPackage,childSbb)) {
  166. URI entityURI = event.getRequest().getRequestURI();
  167. String entity = entityURI.toString();
  168. int i = entity.indexOf(';');
  169. if (i>0) {
  170. // remove all parameters (can't find a reference indicating any param that should be kept...)
  171. entity = entity.substring(0,i);
  172. }
  173. // The ESC inspects the Request-URI to determine whether this request
  174. // is targeted to a resource for which the ESC is responsible for
  175. // maintaining event state. If not, the ESC MUST return a 404 (Not
  176. // Found) response and skip the remaining steps.
  177. if (childSbb.isResponsibleForResource(entityURI,eventPackage)) {
  178. // process expires header
  179. ExpiresHeader expiresHeader = event.getRequest().getExpires();
  180. int expires;
  181. // if expires does not exist then set it's value to default
  182. // value
  183. if (expiresHeader == null) {
  184. expires = getConfiguration().getDefaultExpires();
  185. } else {
  186. expires = expiresHeader.getExpires();
  187. }
  188. // check expires value
  189. if (expires > 0) {
  190. // check if expires is not less than the allowed min expires
  191. if (expires >= getConfiguration().getMinExpires()) {
  192. // ensure expires is not bigger than max expires
  193. if (expires > getConfiguration().getMaxExpires()) {
  194. expires = getConfiguration().getMaxExpires();
  195. }
  196. // new publication or publication refresh ?
  197. SIPIfMatchHeader sipIfMatchHeader = (SIPIfMatchHeader)event.getRequest().getHeader(SIPIfMatchHeader.NAME);
  198. if (sipIfMatchHeader != null) {
  199. // refresh or modification of publication
  200. if (event.getRequest().getContentLength().getContentLength() == 0) {
  201. // refreshing a publication
  202. final Result result = childSbb.refreshPublication(entity, eventPackage, sipIfMatchHeader.getETag(), expires);
  203. if (result.getStatusCode() < 300) {
  204. try {
  205. sendOkResponse(event, result.getETag(), result.getExpires());
  206. }
  207. catch (Exception e) {
  208. logger.error("Error sending response to SIP client, removing related publication",e);
  209. childSbb.removePublication(entity, eventPackage, result.getETag());
  210. }
  211. }
  212. else {
  213. sendErrorResponse(result.getStatusCode(), event.getRequest(), event.getServerTransaction(), eventPackage, childSbb);
  214. }
  215. }
  216. else {
  217. ContentTypeHeader contentTypeHeader = (ContentTypeHeader) event.getRequest().getHeader(ContentTypeHeader.NAME);
  218. if (childSbb.acceptsContentType(eventPackage,contentTypeHeader)) {
  219. // modification
  220. final Result result = childSbb.modifyPublication(entity, eventPackage, sipIfMatchHeader.getETag(), new String(event.getRequest().getRawContent()), contentTypeHeader.getContentType(), contentTypeHeader.getContentSubType(), expires);
  221. if (result.getStatusCode() < 300) {
  222. try {
  223. sendOkResponse(event, result.getETag(), result.getExpires());
  224. } catch (Exception e) {
  225. logger.error("Error sending response to SIP client, removing related publication",e);
  226. childSbb.removePublication(entity, eventPackage, result.getETag());
  227. }
  228. }
  229. else {
  230. sendErrorResponse(result.getStatusCode(), event.getRequest(), event.getServerTransaction(), eventPackage, childSbb);
  231. }
  232. }
  233. else {
  234. // unsupported media type, send the ones supported
  235. sendErrorResponse(Response.UNSUPPORTED_MEDIA_TYPE,event.getRequest(),event.getServerTransaction(),eventPackage,childSbb);
  236. }
  237. }
  238. }
  239. else {
  240. // new publication
  241. if (event.getRequest().getContentLength().getContentLength() != 0) {
  242. ContentTypeHeader contentTypeHeader = (ContentTypeHeader) event.getRequest().getHeader(ContentTypeHeader.NAME);
  243. if (childSbb.acceptsContentType(eventPackage,contentTypeHeader)) {
  244. final Result result = childSbb.newPublication(entity, eventPackage, new String(event.getRequest().getRawContent()), contentTypeHeader.getContentType(), contentTypeHeader.getContentSubType(), expires);
  245. if (result.getStatusCode() < 300) {
  246. try {
  247. sendOkResponse(event, result.getETag(), result.getExpires());
  248. } catch (Exception e) {
  249. logger.error("Error sending response to SIP client, removing related publication",e);
  250. childSbb.removePublication(entity, eventPackage, result.getETag());
  251. }
  252. }
  253. else {
  254. sendErrorResponse(result.getStatusCode(), event.getRequest(), event.getServerTransaction(), eventPackage, childSbb);
  255. }
  256. }
  257. else {
  258. // unsupported media type, send the one supported
  259. sendErrorResponse(Response.UNSUPPORTED_MEDIA_TYPE,event.getRequest(),event.getServerTransaction(),eventPackage,childSbb);
  260. }
  261. }
  262. else {
  263. // send Bad Request since there is no content
  264. sendErrorResponse(Response.BAD_REQUEST,event.getRequest(),event.getServerTransaction(),eventPackage,childSbb);
  265. }
  266. }
  267. } else {
  268. // expires is > 0 but < min expires, respond (Interval
  269. // Too Brief) with Min-Expires = MINEXPIRES
  270. sendErrorResponse(Response.INTERVAL_TOO_BRIEF, event
  271. .getRequest(), event.getServerTransaction(),eventPackage,childSbb);
  272. }
  273. }
  274. else if (expires == 0) {
  275. SIPIfMatchHeader sipIfMatchHeader = (SIPIfMatchHeader)event.getRequest().getHeader(SIPIfMatchHeader.NAME);
  276. if (sipIfMatchHeader != null) {
  277. // remove publication
  278. int result = childSbb.removePublication(entity, eventPackage, sipIfMatchHeader.getETag());
  279. if (result < 300) {
  280. try {
  281. sendOkResponse(event, null, -1);
  282. } catch (Exception e) {
  283. logger.error("Error sending response to SIP client",e);
  284. }
  285. }
  286. else {
  287. sendErrorResponse(result, event.getRequest(), event.getServerTransaction(), eventPackage, childSbb);
  288. }
  289. }
  290. else {
  291. // send Bad Request since removal requires etag
  292. sendErrorResponse(Response.BAD_REQUEST,event.getRequest(),event.getServerTransaction(),eventPackage,childSbb);
  293. }
  294. } else {
  295. // expires can't be negative
  296. sendErrorResponse(Response.BAD_REQUEST, event.getRequest(),
  297. event.getServerTransaction(),eventPackage,childSbb);
  298. }
  299. }
  300. else {
  301. // not responsible for this resource
  302. sendErrorResponse(Response.NOT_FOUND, event.getRequest(), event.getServerTransaction(), eventPackage,childSbb);
  303. }
  304. } else {
  305. // wrong event package, send bad event type error
  306. sendErrorResponse(Response.BAD_EVENT, event.getRequest(), event
  307. .getServerTransaction(),eventPackage,childSbb);
  308. }
  309. } else {
  310. // subscribe does not have a event header
  311. sendErrorResponse(Response.BAD_REQUEST, event.getRequest(), event
  312. .getServerTransaction(),null,childSbb);
  313. }
  314. }
  315. public void onOptions(RequestEvent requestEvent, ActivityContextInterface aci) {
  316. if (logger.isDebugEnabled()) {
  317. logger.debug("Processing OPTIONS request");
  318. }
  319. aci.detach(this.sbbContext.getSbbLocalObject());
  320. /*
  321. * A client may probe the ESC for the support of PUBLISH using the
  322. * OPTIONS request defined in SIP [4]. The ESC processes OPTIONS
  323. * requests as defined in Section 11.2 of RFC 3261 [4]. In the response
  324. * to an OPTIONS request, the ESC SHOULD include "PUBLISH" to the list
  325. * of allowed methods in the Allow header field. Also, it SHOULD list
  326. * the supported event packages in an Allow-Events header field.
  327. *
  328. * The Allow header field may also be used to specifically announce
  329. * support for PUBLISH messages when registering. (See SIP Capabilities
  330. * [12] for details).
  331. */
  332. PublicationControlSbbLocalObject childSbb = getPublicationControlChildSbb();
  333. if (childSbb == null) {
  334. try {
  335. // create an error response:
  336. Response response = messageFactory.createResponse(Response.SERVER_INTERNAL_ERROR,requestEvent.getRequest());
  337. requestEvent.getServerTransaction().sendResponse(response);
  338. }
  339. catch (Exception f) {
  340. logger.error("Can't send error response!",f);
  341. }
  342. return;
  343. }
  344. try {
  345. // create successful response:
  346. Response response = messageFactory.createResponse(Response.OK,requestEvent.getRequest());
  347. // add headers here:
  348. String allowEventsHeader = "";
  349. boolean first = true;
  350. for (String acceptedEventPackage : childSbb.getEventPackages()) {
  351. if (first) {
  352. allowEventsHeader += acceptedEventPackage;
  353. first = false;
  354. }
  355. else {
  356. allowEventsHeader += ","+acceptedEventPackage;
  357. }
  358. }
  359. // Indicate the allowable event packages supported at the server:
  360. response.addHeader(headerFactory.createAllowEventsHeader(allowEventsHeader));
  361. //In the response to an OPTIONS request, the ESC SHOULD include "PUBLISH" to the list
  362. //of allowed methods in the Allow header field:
  363. response.addHeader(headerFactory.createAllowHeader("PUBLISH, SUBSCRIBE, NOTIFY, OPTIONS"));
  364. requestEvent.getServerTransaction().sendResponse(response);
  365. }
  366. catch (Exception e) {
  367. logger.error("Can't send response!",e);
  368. }
  369. }
  370. // ----------- AUX METHODS
  371. private void sendOkResponse(RequestEvent event,String eTag,int expires) throws Exception {
  372. // send 200 ok response with expires and sipEtag
  373. Response response = messageFactory.createResponse(Response.OK, event.getRequest());
  374. if (eTag != null) response.addHeader(headerFactory.createSIPETagHeader(eTag));
  375. if (expires != -1) response.addHeader(headerFactory.createExpiresHeader(expires));
  376. /* aayush..started adding here: (Ref issue #567)
  377. * The PUBLISH request had a P-charging-vector header
  378. * that consisted of an ICID and an orig-ioi parameter.The PS
  379. * needs to preserve that header and needs to add a term-ioi
  380. * parameter pointing to its own domain name:*/
  381. // 1. Get the header as received from the request:
  382. final PChargingVectorHeader pcv = (PChargingVectorHeader)
  383. event.getRequest().getHeader(PChargingVectorHeader.NAME);
  384. // 2. In case the request is received from a non-IMS VoIP network,P-charging-Vector wont be there,so check for pcv!=null
  385. if (pcv!=null) {
  386. pcv.setTerminatingIOI(getConfiguration().getPChargingVectorHeaderTerminatingIOI());
  387. response.addHeader(pcv);
  388. }
  389. // Also need to add the P-Charging-Function-Addresses header
  390. // as received from the PUBLISH request in the 200 OK being sent:
  391. final PChargingFunctionAddressesHeader pcfa = (PChargingFunctionAddressesHeader)
  392. event.getRequest().getHeader(PChargingFunctionAddressesHeader.NAME);
  393. if (pcfa!=null) response.addHeader(pcfa);
  394. event.getServerTransaction().sendResponse(response);
  395. }
  396. /*
  397. * Sends an error response with the specified status code, adding additional
  398. * headers if needed
  399. */
  400. private void sendErrorResponse(int responseCode, Request request,
  401. ServerTransaction serverTransaction, String eventPackage, PublicationControlSbbLocalObject childSbb) {
  402. try {
  403. // create response
  404. Response response = messageFactory.createResponse(responseCode,request);
  405. // add headers if needed
  406. if (responseCode == Response.BAD_EVENT && childSbb != null) {
  407. String allowEventsHeader = "";
  408. boolean first = true;
  409. for (String acceptedEventPackage : childSbb.getEventPackages()) {
  410. if (first) {
  411. allowEventsHeader += acceptedEventPackage;
  412. first = false;
  413. }
  414. else {
  415. allowEventsHeader += ","+acceptedEventPackage;
  416. }
  417. }
  418. response
  419. .addHeader(headerFactory.createAllowEventsHeader(allowEventsHeader));
  420. }
  421. else if (responseCode == Response.INTERVAL_TOO_BRIEF)
  422. response.addHeader(headerFactory.createMinExpiresHeader(getConfiguration().getMinExpires()));
  423. else if (responseCode == Response.UNSUPPORTED_MEDIA_TYPE && childSbb != null) {
  424. response.addHeader(childSbb.getAcceptsHeader(eventPackage));
  425. }
  426. serverTransaction.sendResponse(response);
  427. }
  428. catch (Exception e) {
  429. logger.error("Can't send response!",e);
  430. }
  431. }
  432. /**
  433. * verifies if the specified event packaged is accepted
  434. */
  435. private boolean acceptsEventPackage(String eventPackage,PublicationControlSbbLocalObject childSbb) {
  436. if (eventPackage != null) {
  437. for(String acceptedEventPackage : childSbb.getEventPackages()) {
  438. if (eventPackage.equals(acceptedEventPackage)) {
  439. return true;
  440. }
  441. }
  442. }
  443. return false;
  444. }
  445. public void onServiceStartedEvent(ServiceStartedEvent event, ActivityContextInterface aci) {
  446. logger.info("Mobicents SIP Event Publication Control service activated.");
  447. // FIXME forcing load of classes of childs, till deadlocks on slee class loaders nailed
  448. getPublicationControlChildSbb();
  449. aci.detach(sbbContext.getSbbLocalObject());
  450. }
  451. // ----------- SBB OBJECT's LIFE CYCLE
  452. public void sbbActivate() {}
  453. public void sbbCreate() throws CreateException {}
  454. public void sbbExceptionThrown(Exception arg0, Object arg1,
  455. ActivityContextInterface arg2) {}
  456. public void sbbLoad() {}
  457. public void sbbPassivate() {}
  458. public void sbbPostCreate() throws CreateException {}
  459. public void sbbRemove() {}
  460. public void sbbRolledBack(RolledBackContext arg0) {}
  461. public void sbbStore() {}
  462. public void unsetSbbContext() { this.sbbContext = null; }
  463. }