PageRenderTime 40ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

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

http://mobicents.googlecode.com/
Java | 682 lines | 424 code | 65 blank | 193 comment | 68 complexity | 93ba6b113d9134db9737a5fe760048f8 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 java.io.Serializable;
  24. import java.io.StringReader;
  25. import java.util.List;
  26. import javax.sip.address.URI;
  27. import javax.sip.header.ContentTypeHeader;
  28. import javax.sip.header.Header;
  29. import javax.sip.message.Response;
  30. import javax.xml.transform.dom.DOMSource;
  31. import org.mobicents.slee.sipevent.server.publication.data.ComposedPublication;
  32. import org.mobicents.slee.sipevent.server.publication.data.ComposedPublicationKey;
  33. import org.mobicents.slee.sipevent.server.publication.data.Publication;
  34. import org.mobicents.slee.sipevent.server.publication.data.PublicationControlDataSource;
  35. import org.mobicents.slee.sipevent.server.publication.data.PublicationKey;
  36. import org.mobicents.slee.sipevent.server.publication.jmx.PublicationControlManagement;
  37. import org.mobicents.xdm.common.util.dom.DomUtils;
  38. import org.w3c.dom.Document;
  39. import org.xml.sax.InputSource;
  40. /**
  41. *
  42. * @author martins
  43. *
  44. */
  45. public abstract class AbstractPublicationControl implements PublicationControl {
  46. private static final Result UNSUPPORTED_MEDIA_TYPE = new Result(
  47. Response.UNSUPPORTED_MEDIA_TYPE);
  48. private static final Result FORBIDDEN = new Result(Response.FORBIDDEN);
  49. private static final Result SERVER_INTERNAL_ERROR = new Result(
  50. Response.SERVER_INTERNAL_ERROR);
  51. private static final Result CONDITIONAL_REQUEST_FAILED = new Result(
  52. Response.CONDITIONAL_REQUEST_FAILED);
  53. protected abstract PublicationControlLogger getLogger();
  54. protected abstract ImplementedPublicationControl getImplementedPublicationControl();
  55. private static final PublicationControlManagement management = PublicationControlManagement
  56. .getInstance();
  57. private static final PublicationControlDataSource dataSource = management.getDataSource();
  58. /*
  59. * (non-Javadoc)
  60. *
  61. * @see
  62. * org.mobicents.slee.sipevent.server.publication.PublicationClientControl
  63. * #newPublication(java.lang.String, java.lang.String, java.lang.String,
  64. * java.lang.String, java.lang.String, int)
  65. */
  66. public Result newPublication(String entity, String eventPackage,
  67. String document, String contentType, String contentSubType,
  68. int expires) {
  69. final PublicationControlLogger logger = getLogger();
  70. if (logger.isDebugEnabled()) {
  71. logger.debug("new publication request: entity=" + entity
  72. + ",eventPackage=" + eventPackage);
  73. }
  74. Publication publication = null;
  75. Result result = null;
  76. // get child sbb
  77. final ImplementedPublicationControl impl = getImplementedPublicationControl();
  78. try {
  79. Document domDocument = unmarshallDocument(document, eventPackage, impl);
  80. if (domDocument == null) {
  81. // If the content type of the request does
  82. // not match the event package, or is not understood by the ESC,
  83. // the
  84. // ESC MUST reject the request with an appropriate response,
  85. // such as
  86. // 415 (Unsupported Media Type)
  87. if (logger.isInfoEnabled()) {
  88. logger.info("publication for resource " + entity
  89. + " on event package " + eventPackage
  90. + " has unsupported media type");
  91. }
  92. return UNSUPPORTED_MEDIA_TYPE;
  93. }
  94. // authorize publication
  95. if (!impl.authorizePublication(entity, eventPackage, domDocument)) {
  96. if (logger.isInfoEnabled()) {
  97. logger.info("publication for resource " + entity
  98. + " on event package " + eventPackage
  99. + " not authorized");
  100. }
  101. result = FORBIDDEN;
  102. } else {
  103. // create SIP-ETag
  104. final String eTag = ETagGenerator
  105. .generate(entity, eventPackage);
  106. // create publication pojo
  107. final PublicationKey publicationKey = new PublicationKey(eTag,
  108. entity, eventPackage);
  109. publication = new Publication(publicationKey, document,
  110. contentType, contentSubType);
  111. publication.setDocumentAsDOM(domDocument);
  112. // set timer
  113. setTimer(publication, expires);
  114. // update or create composed publication
  115. ComposedPublication composedPublication = getComposedPublication(
  116. entity, eventPackage);
  117. if (composedPublication == null) {
  118. // single publication, composed publication is the new
  119. // publication
  120. composedPublication = ComposedPublication
  121. .fromPublication(publication);
  122. dataSource.add(composedPublication);
  123. } else {
  124. // composed publication exists
  125. composedPublication = updateComposedPublication(
  126. publication, getPublications(entity, eventPackage),
  127. null,impl);
  128. }
  129. // persist data
  130. dataSource.add(publication);
  131. // notify subscribers
  132. impl.notifySubscribers(composedPublication);
  133. if (logger.isInfoEnabled()) {
  134. logger.info("New " + publication + " for " + expires
  135. + " seconds");
  136. }
  137. result = new Result(200, eTag, expires);
  138. }
  139. } catch (Exception e) {
  140. logger.error("failed to create publication", e);
  141. if (publication != null) {
  142. // timer may be set, cancel it
  143. cancelTimer(publication);
  144. }
  145. result = SERVER_INTERNAL_ERROR;
  146. }
  147. return result;
  148. }
  149. private Document unmarshallDocument(String document, String eventPackage, ImplementedPublicationControl implementedPublicationControl) {
  150. // unmarshall document
  151. StringReader reader = new StringReader(document);
  152. try {
  153. Document domDocument = DomUtils.DOCUMENT_BUILDER_NS_AWARE_FACTORY.newDocumentBuilder().parse(new InputSource(reader));
  154. implementedPublicationControl.getSchema(eventPackage).newValidator().validate(new DOMSource(domDocument));
  155. return domDocument;
  156. }
  157. catch (Exception e) {
  158. PublicationControlLogger logger = getLogger();
  159. if (logger.isDebugEnabled()) {
  160. logger.error("failed to parse publication content",e);
  161. }
  162. return null;
  163. }
  164. finally {
  165. reader.close();
  166. }
  167. }
  168. /**
  169. * @param publication
  170. * @param expires
  171. * @throws Exception
  172. */
  173. protected abstract void setTimer(Publication publication, int expires)
  174. throws Exception;
  175. /*
  176. * (non-Javadoc)
  177. *
  178. * @see
  179. * org.mobicents.slee.sipevent.server.publication.PublicationClientControl
  180. * #refreshPublication(java.lang.String, java.lang.String, java.lang.String,
  181. * int)
  182. */
  183. public Result refreshPublication(String entity, String eventPackage,
  184. String oldETag, int expires) {
  185. final PublicationControlLogger logger = getLogger();
  186. if (logger.isDebugEnabled()) {
  187. logger.debug("refresh Publication: entity=" + entity
  188. + ",eventPackage=" + eventPackage + ",eTag=" + oldETag
  189. + ",expires=" + expires);
  190. }
  191. Result result = null;
  192. try {
  193. // get publication
  194. final Publication publication = getPublication(oldETag, entity,
  195. eventPackage);
  196. if (publication == null) {
  197. if (logger.isInfoEnabled()) {
  198. logger.info("can't refresh publication for resource "
  199. + entity + " on event package " + eventPackage
  200. + " with eTag " + oldETag + ", it does not exist");
  201. }
  202. result = CONDITIONAL_REQUEST_FAILED;
  203. } else {
  204. // create new SIP-ETag
  205. final String eTag = ETagGenerator
  206. .generate(entity, eventPackage);
  207. // create new publication pojo
  208. final PublicationKey newPublicationKey = new PublicationKey(
  209. eTag, entity, eventPackage);
  210. final Publication newPublication = new Publication(
  211. newPublicationKey, publication.getDocumentAsString(),
  212. publication.getContentType(), publication
  213. .getContentSubType());
  214. // reset timer
  215. resetTimer(publication, newPublication, expires);
  216. // replace publication
  217. dataSource.replace(publication, newPublication);
  218. // inform parent publication is valid
  219. result = new Result(Response.OK, eTag, expires);
  220. if (logger.isInfoEnabled()) {
  221. logger.info("Refreshed " + publication + " for " + expires
  222. + " seconds");
  223. }
  224. }
  225. } catch (Exception e) {
  226. logger.error("failed to refresh publication", e);
  227. result = SERVER_INTERNAL_ERROR;
  228. }
  229. return result;
  230. }
  231. /**
  232. * @param publication
  233. * @param newPublication
  234. * @param expires
  235. * @throws Exception
  236. */
  237. protected abstract void resetTimer(Publication publication,
  238. Publication newPublication, int expires) throws Exception;
  239. /**
  240. * @param timerID
  241. */
  242. protected abstract void cancelTimer(Publication publication);
  243. /*
  244. * (non-Javadoc)
  245. *
  246. * @see
  247. * org.mobicents.slee.sipevent.server.publication.PublicationClientControl
  248. * #removePublication(java.lang.String, java.lang.String, java.lang.String)
  249. */
  250. public int removePublication(String entity, String eventPackage, String eTag) {
  251. final PublicationControlLogger logger = getLogger();
  252. if (logger.isDebugEnabled()) {
  253. logger.debug("removePublication: entity=" + entity
  254. + ",eventPackage=" + eventPackage + ",eTag=" + eTag);
  255. }
  256. int result = -1;
  257. try {
  258. // get publications for the entity and event package and look for
  259. // the one related with the request
  260. Publication publication = null;
  261. final Publication[] publications = getPublications(entity,
  262. eventPackage);
  263. for (Publication otherPublication : publications) {
  264. if (otherPublication.getPublicationKey().getETag().equals(eTag)) {
  265. publication = otherPublication;
  266. break;
  267. }
  268. }
  269. if (publication == null) {
  270. if (logger.isInfoEnabled()) {
  271. logger.info("can't remove publication for resource "
  272. + entity + " on event package " + eventPackage
  273. + " with eTag " + eTag + ", it does not exist");
  274. }
  275. result = Response.CONDITIONAL_REQUEST_FAILED;
  276. } else {
  277. // cancel timer
  278. cancelTimer(publication);
  279. // remove old publication
  280. dataSource.delete(publication);
  281. // get child sbb
  282. final ImplementedPublicationControl impl = getImplementedPublicationControl();
  283. // we need to re-compose all publications except the one being
  284. // removed
  285. final ComposedPublication composedPublication = removeFromComposedPublication(
  286. publication, publications, true, impl);
  287. if (composedPublication.getDocumentAsString() == null
  288. && management
  289. .isUseAlternativeValueForExpiredPublication()) {
  290. // give the event package implementation sbb a chance to
  291. // define
  292. // an alternative publication value for the one expired,
  293. // this can allow a behavior such as defining offline status
  294. // in a presence resource
  295. final Publication alternativePublication = impl
  296. .getAlternativeValueForExpiredPublication(publication);
  297. if (alternativePublication != null) {
  298. composedPublication
  299. .setContentSubType(alternativePublication
  300. .getContentSubType());
  301. composedPublication
  302. .setContentType(alternativePublication
  303. .getContentType());
  304. composedPublication.setDocumentAsString(alternativePublication
  305. .getDocumentAsString());
  306. composedPublication
  307. .setDocumentAsDOM(alternativePublication
  308. .getDocumentAsDOM());
  309. }
  310. }
  311. // inform parent publication is removed
  312. result = Response.OK;
  313. if (logger.isInfoEnabled()) {
  314. logger.info("Removed " + publication);
  315. }
  316. // notify subscribers
  317. impl.notifySubscribers(composedPublication);
  318. }
  319. } catch (Exception e) {
  320. logger.error("failed to remove publication", e);
  321. result = Response.SERVER_INTERNAL_ERROR;
  322. }
  323. return result;
  324. }
  325. /*
  326. * (non-Javadoc)
  327. *
  328. * @see
  329. * org.mobicents.slee.sipevent.server.publication.PublicationClientControl
  330. * #modifyPublication(java.lang.String, java.lang.String, java.lang.String,
  331. * java.lang.String, java.lang.String, java.lang.String, int)
  332. */
  333. public Result modifyPublication(String entity, String eventPackage,
  334. String oldETag, String document, String contentType,
  335. String contentSubType, int expires) {
  336. final PublicationControlLogger logger = getLogger();
  337. if (logger.isDebugEnabled()) {
  338. getLogger().debug(
  339. "modifyPublication: entity=" + entity + ",eventPackage="
  340. + eventPackage + ",eTag=" + oldETag);
  341. }
  342. Result result = null;
  343. try {
  344. // get child sbb
  345. final ImplementedPublicationControl impl = getImplementedPublicationControl();
  346. // get publications for the entity and event package and look for
  347. // the one related with the request
  348. Publication publication = null;
  349. final Publication[] publications = getPublications(entity,
  350. eventPackage);
  351. for (Publication otherPublication : publications) {
  352. if (otherPublication.getPublicationKey().getETag().equals(
  353. oldETag)) {
  354. publication = otherPublication;
  355. break;
  356. }
  357. }
  358. if (publication == null) {
  359. if (logger.isInfoEnabled()) {
  360. logger.info("can't modify publication for resource "
  361. + entity + " on event package " + eventPackage
  362. + " with eTag " + oldETag + ", it does not exist");
  363. }
  364. result = CONDITIONAL_REQUEST_FAILED;
  365. } else {
  366. // unmarshall document
  367. final Document unmarshalledContent = unmarshallDocument(document,eventPackage,impl);
  368. if (unmarshalledContent == null) {
  369. // If the content type of the request does
  370. // not match the event package, or is not understood by the
  371. // ESC,
  372. // the
  373. // ESC MUST reject the request with an appropriate response,
  374. // such as
  375. // 415 (Unsupported Media Type)
  376. if (logger.isInfoEnabled()) {
  377. logger.info("publication for resource " + entity
  378. + " on event package " + eventPackage
  379. + " has unsupported media type");
  380. }
  381. return UNSUPPORTED_MEDIA_TYPE;
  382. }
  383. // authorize publication
  384. if (!impl.authorizePublication(entity, eventPackage, unmarshalledContent)) {
  385. result = FORBIDDEN;
  386. if (logger.isInfoEnabled()) {
  387. logger.info("publication for resource " + entity
  388. + " on event package " + eventPackage
  389. + " not authorized");
  390. }
  391. } else {
  392. // create new SIP-ETag
  393. final String eTag = ETagGenerator.generate(entity,
  394. eventPackage);
  395. // create new publication pojo with new key and document
  396. final PublicationKey newPublicationKey = new PublicationKey(
  397. eTag, entity, eventPackage);
  398. final Publication newPublication = new Publication(
  399. newPublicationKey, document, contentType,
  400. contentSubType);
  401. newPublication.setDocumentAsDOM(unmarshalledContent);
  402. // get composed publication and rebuild it
  403. final ComposedPublication composedPublication = updateComposedPublication(
  404. newPublication, publications, oldETag, impl);
  405. // reset timer
  406. resetTimer(publication, newPublication, expires);
  407. // replace data
  408. dataSource.replace(publication, newPublication);
  409. // notify subscribers
  410. impl.notifySubscribers(composedPublication);
  411. // inform parent publication is valid
  412. result = new Result(Response.OK, eTag, expires);
  413. if (logger.isInfoEnabled()) {
  414. logger.info(publication + " modified.");
  415. }
  416. }
  417. }
  418. } catch (Exception e) {
  419. logger.error("failed to refresh publication", e);
  420. result = SERVER_INTERNAL_ERROR;
  421. }
  422. return result;
  423. }
  424. /**
  425. * @param newPublication
  426. * @param composedPublication
  427. */
  428. private ComposedPublication updateComposedPublication(
  429. Publication publication, Publication[] publications,
  430. String exceptETag, ImplementedPublicationControl impl) {
  431. final ComposedPublication composedPublication = ComposedPublication
  432. .fromPublication(publication);
  433. Document unmarshalledContent = publication
  434. .getDocumentAsDOM();
  435. for (Publication otherPublication : publications) {
  436. if (exceptETag == null
  437. || !otherPublication.getPublicationKey().getETag().equals(
  438. exceptETag)) {
  439. unmarshalledContent = impl.getStateComposer(publication.getPublicationKey().getEventPackage())
  440. .compose(unmarshalledContent,
  441. otherPublication.getDocumentAsDOM());
  442. }
  443. }
  444. composedPublication.setDocumentAsDOM(unmarshalledContent);
  445. composedPublication.forceDocumentUpdate();
  446. dataSource.update(composedPublication);
  447. return composedPublication;
  448. }
  449. /**
  450. * a timer has occurred in a dialog regarding a publication
  451. *
  452. * @param event
  453. * @param aci
  454. */
  455. public void timerExpired(Serializable timerID) {
  456. final PublicationControlLogger logger = getLogger();
  457. // get publication
  458. final Publication publication = dataSource.getFromTimerID(timerID);
  459. if (publication != null) {
  460. try {
  461. // get child sbb
  462. final ImplementedPublicationControl impl = getImplementedPublicationControl();
  463. // remove publication
  464. dataSource.delete(publication);
  465. if (logger.isInfoEnabled()) {
  466. logger.info(publication + " removed. Timer expired.");
  467. }
  468. // we need to re-compose all publications except the one being
  469. // removed
  470. final ComposedPublication composedPublication = removeFromComposedPublication(
  471. publication, getPublications(publication
  472. .getPublicationKey().getEntity(), publication
  473. .getPublicationKey().getEventPackage()), false,impl);
  474. if (composedPublication.getDocumentAsString() == null
  475. && management
  476. .isUseAlternativeValueForExpiredPublication()) {
  477. // give the event package implementation sbb a chance to
  478. // define
  479. // an alternative publication value for the one expired,
  480. // this can allow a behavior such as defining offline status
  481. // in a presence resource
  482. final Publication alternativePublication = impl
  483. .getAlternativeValueForExpiredPublication(publication);
  484. if (alternativePublication != null) {
  485. composedPublication
  486. .setContentSubType(alternativePublication
  487. .getContentSubType());
  488. composedPublication
  489. .setContentType(alternativePublication
  490. .getContentType());
  491. composedPublication.setDocumentAsString(alternativePublication
  492. .getDocumentAsString());
  493. composedPublication
  494. .setDocumentAsDOM(alternativePublication
  495. .getDocumentAsDOM());
  496. }
  497. }
  498. // notify subscribers
  499. impl.notifySubscribers(composedPublication);
  500. } catch (Exception e) {
  501. logger.error("failed to remove publication that expired", e);
  502. }
  503. }
  504. }
  505. // ----------- SBB LOCAL OBJECT
  506. public void shutdown() {
  507. // nothing to do
  508. }
  509. /*
  510. * (non-Javadoc)
  511. *
  512. * @seeorg.mobicents.slee.sipevent.server.publication.PublicationControl#
  513. * getComposedPublication(java.lang.String, java.lang.String)
  514. */
  515. public ComposedPublication getComposedPublication(String entity,
  516. String eventPackage) {
  517. return dataSource
  518. .get(new ComposedPublicationKey(entity, eventPackage));
  519. }
  520. /*
  521. * (non-Javadoc)
  522. *
  523. * @seeorg.mobicents.slee.sipevent.server.publication.PublicationControl#
  524. * acceptsContentType(java.lang.String, javax.sip.header.ContentTypeHeader)
  525. */
  526. public boolean acceptsContentType(String eventPackage,
  527. ContentTypeHeader contentTypeHeader) {
  528. return getImplementedPublicationControl().acceptsContentType(
  529. eventPackage, contentTypeHeader);
  530. }
  531. /*
  532. * (non-Javadoc)
  533. *
  534. * @seeorg.mobicents.slee.sipevent.server.publication.PublicationControl#
  535. * getAcceptsHeader(java.lang.String)
  536. */
  537. public Header getAcceptsHeader(String eventPackage) {
  538. return getImplementedPublicationControl()
  539. .getAcceptsHeader(eventPackage);
  540. }
  541. /*
  542. * (non-Javadoc)
  543. *
  544. * @seeorg.mobicents.slee.sipevent.server.publication.PublicationControl#
  545. * getEventPackages()
  546. */
  547. public String[] getEventPackages() {
  548. return getImplementedPublicationControl().getEventPackages();
  549. }
  550. /*
  551. * (non-Javadoc)
  552. *
  553. * @seeorg.mobicents.slee.sipevent.server.publication.PublicationControl#
  554. * isResponsibleForResource(javax.sip.address.URI)
  555. */
  556. public boolean isResponsibleForResource(URI uri, String eventPackage) {
  557. return getImplementedPublicationControl().isResponsibleForResource(uri,eventPackage);
  558. }
  559. // ----------- AUX METHODS
  560. private ComposedPublication removeFromComposedPublication(
  561. Publication publication, Publication[] publications,
  562. boolean publicationIsInPublications, ImplementedPublicationControl impl) {
  563. final String entity = publication.getPublicationKey().getEntity();
  564. final String eventPackage = publication.getPublicationKey()
  565. .getEventPackage();
  566. final ComposedPublicationKey composedPublicationKey = new ComposedPublicationKey(
  567. entity, eventPackage);
  568. final ComposedPublication composedPublication = new ComposedPublication(composedPublicationKey);
  569. // rebuild composed content with all publications (except the one
  570. // removed)
  571. Document composedPublicationUnmarshalledContent = null;
  572. for (Publication otherPublication : publications) {
  573. if (!publicationIsInPublications
  574. || !otherPublication.getPublicationKey().getETag().equals(
  575. publication.getPublicationKey().getETag())) {
  576. composedPublicationUnmarshalledContent = impl.getStateComposer(eventPackage)
  577. .compose(composedPublicationUnmarshalledContent,
  578. otherPublication.getDocumentAsDOM());
  579. }
  580. }
  581. if (composedPublicationUnmarshalledContent == null) {
  582. // just remove composed state
  583. dataSource.delete(composedPublicationKey);
  584. } else {
  585. composedPublication.setDocumentAsDOM(
  586. composedPublicationUnmarshalledContent);
  587. composedPublication.setContentType(publication.getContentType());
  588. composedPublication.setContentSubType(publication.getContentSubType());
  589. composedPublication.forceDocumentUpdate();
  590. // update
  591. dataSource.update(composedPublication);
  592. }
  593. return composedPublication;
  594. }
  595. private Publication getPublication(String eTag, String entity,
  596. String eventPackage) {
  597. return dataSource.get(new PublicationKey(eTag, entity,
  598. eventPackage));
  599. }
  600. private static final Publication[] EMPTY_ARRAY_PUBLICATIONS = {};
  601. /**
  602. *
  603. * @param entity
  604. * @param eventPackage
  605. * @return null if there are no publications
  606. */
  607. private Publication[] getPublications(String entity, String eventPackage) {
  608. final List<?> resultList = dataSource.getPublications(eventPackage,
  609. entity);
  610. if (resultList.size() == 0) {
  611. return EMPTY_ARRAY_PUBLICATIONS;
  612. } else {
  613. return resultList
  614. .toArray(new Publication[resultList.size()]);
  615. }
  616. }
  617. }