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

/servers/sip-presence/sip-event/server/subscription/rls-cache/ra/src/main/java/org/mobicents/slee/sipevent/server/rlscache/ResourceList.java

http://mobicents.googlecode.com/
Java | 542 lines | 389 code | 53 blank | 100 comment | 134 complexity | 18e6a826c37ab65f186f39aff9b23546 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.rlscache;
  23. import java.util.HashSet;
  24. import java.util.Iterator;
  25. import java.util.LinkedList;
  26. import java.util.Set;
  27. import java.util.concurrent.ConcurrentHashMap;
  28. import javax.xml.bind.JAXBElement;
  29. import org.mobicents.slee.sipevent.server.rlscache.RLSService.Status;
  30. import org.openxdm.xcap.client.appusage.resourcelists.jaxb.EntryRefType;
  31. import org.openxdm.xcap.client.appusage.resourcelists.jaxb.EntryType;
  32. import org.openxdm.xcap.client.appusage.resourcelists.jaxb.ExternalType;
  33. import org.openxdm.xcap.client.appusage.resourcelists.jaxb.ListType;
  34. import org.openxdm.xcap.common.uri.ElementSelector;
  35. import org.openxdm.xcap.common.uri.ElementSelectorStep;
  36. import org.openxdm.xcap.common.uri.ElementSelectorStepByAttr;
  37. public class ResourceList extends AbstractListReferenceTo implements ListReferenceFrom {
  38. private final ListReferenceFrom parent;
  39. private final ConcurrentHashMap<String,Entry> localEntries = new ConcurrentHashMap<String,Entry>();
  40. private final ConcurrentHashMap<String,ResourceList> lists = new ConcurrentHashMap<String,ResourceList>(1);
  41. private final ConcurrentHashMap<ListReferenceEndpointAddress, ListReferenceTo> toReferences = new ConcurrentHashMap<ListReferenceEndpointAddress, ListReferenceTo>(1);
  42. private final RLSServicesCacheResourceAdaptor ra;
  43. private ListType listType;
  44. private boolean updating = false;
  45. public ResourceList(ListReferenceEndpointAddress address, ListReferenceFrom parent, RLSServicesCacheResourceAdaptor ra) {
  46. super(address);
  47. this.parent = parent;
  48. this.ra = ra;
  49. }
  50. public void setListType(ListType listType) {
  51. synchronized (this) {
  52. updating = true;
  53. this.listType = listType;
  54. if (listType == null) {
  55. if (status != RLSService.Status.DOES_NOT_EXISTS) {
  56. status = RLSService.Status.DOES_NOT_EXISTS;
  57. for (Entry entry : localEntries.values()) {
  58. entry.setEntryType(null);
  59. }
  60. for (ResourceList list : lists.values()) {
  61. list.setListType(null);
  62. }
  63. updated();
  64. }
  65. } else {
  66. RLSService.Status oldStatus = status;
  67. processListType(listType);
  68. if (status == RLSService.Status.OK || oldStatus != status) {
  69. updated();
  70. }
  71. }
  72. updating = false;
  73. }
  74. }
  75. private void processListType(ListType listType) {
  76. status = RLSService.Status.OK;
  77. HashSet<String> entriesUpdated = new HashSet<String>();
  78. HashSet<String> listsUpdated = new HashSet<String>();
  79. HashSet<ListReferenceEndpointAddress> referencesToAdd = new HashSet<ListReferenceEndpointAddress>();
  80. /*
  81. * At this point, the RLS has a <list> element in its possession. The
  82. * next step is to obtain a flat list of URIs from this element. To do
  83. * that, it traverses the tree of elements rooted in the <list> element.
  84. * Before traversal begins, the RLS initializes two lists: the "flat
  85. * list", which will contain the flat list of the URI after traversal,
  86. * and the "traversed list", which contains a list of HTTP URIs in
  87. * <external> elements that have already been visited. Both lists are
  88. * initially empty. Next, tree traversal begins. A server can use any
  89. * tree-traversal ordering it likes, such as depth-first search or
  90. * breadth-first search. The processing at each element in the tree
  91. * depends on the name of the element:
  92. */
  93. for (Iterator<Object> i=listType.getListOrExternalOrEntry().iterator(); i.hasNext();) {
  94. JAXBElement<?> element = (JAXBElement<?>) i.next();
  95. if (element.getValue() instanceof EntryType) {
  96. /* o If the element is <entry>, the URI in the "uri" attribute of the
  97. * element is added to the flat list if it is not already present (based
  98. * on case-sensitive string equality) in that list, and the URI scheme
  99. * represents one that can be used to service subscriptions, such as SIP
  100. * [4] and pres [15].
  101. */
  102. EntryType entryType = (EntryType) element.getValue();
  103. entriesUpdated.add(entryType.getUri());
  104. Entry entry = localEntries.get(entryType.getUri());
  105. if (entry == null) {
  106. entry = addEntry(entryType.getUri());
  107. }
  108. entry.setEntryType(entryType);
  109. }
  110. else if (element.getValue() instanceof EntryRefType) {
  111. /* o If the element is an <entry-ref>, the relative path reference
  112. * making up the value of the "ref" attribute is resolved into an
  113. * absolute URI. This is done using the procedures defined in Section
  114. * 5.2 of RFC 3986 [7], using the XCAP root of the RLS services document
  115. * as the base URI. This absolute URI is resolved. If the result is not
  116. * a 200 OK containing a <entry> element, the SUBSCRIBE request SHOULD
  117. * be rejected with a 502 (Bad Gateway). Otherwise, the <entry> element
  118. * returned is processed as described in the previous step.
  119. */
  120. EntryRefType entryRefType = (EntryRefType) element.getValue();
  121. ListReferenceEndpointAddress entryRefAddress = ra.getAddressParser().getAddress(entryRefType.getRef(),false);
  122. if (entryRefAddress == null) {
  123. status = RLSService.Status.BAD_GATEWAY;
  124. continue;
  125. }
  126. if (!entryRefAddress.getElementSelector().getLastStep().getNameWithoutPrefix().equals("entry")) {
  127. // not pointing to an entry
  128. status = RLSService.Status.BAD_GATEWAY;
  129. continue;
  130. }
  131. referencesToAdd.add(entryRefAddress);
  132. }
  133. else if (element.getValue() instanceof ExternalType) {
  134. /* o If the element is an <external> element, the absolute URI making up
  135. * the value of the "anchor" attribute of the element is examined. If
  136. * the URI is on the traversed list, the server MUST cease traversing
  137. * the tree, and SHOULD reject the SUBSCRIBE request with a 502 (Bad
  138. * Gateway). If the URI is not on the traversed list, the server adds
  139. * the URI to the traversed list, and dereferences the URI. If the
  140. * result is not a 200 OK containing a <list> element, the SUBSCRIBE
  141. * request SHOULD be rejected with a 502 (Bad Gateway). Otherwise, the
  142. * RLS replaces the <external> element in its local copy of the tree
  143. * with the <list> element that was returned, and tree traversal
  144. * continues.
  145. *
  146. *
  147. * Because the <external> element is used to dynamically construct the
  148. * tree, there is a possibility of recursive evaluation of references.
  149. * The traversed list is used to prevent this from happening.
  150. *
  151. * Once the tree has been traversed, the RLS can create virtual
  152. * subscriptions to each URI in the flat list, as defined in [14]. In
  153. * the processing steps outlined above, when an <entry-ref> or
  154. * <external> element contains a reference that cannot be resolved,
  155. * failing the request is at SHOULD strength. In some cases, an RLS may
  156. * provide better service by creating virtual subscriptions to the URIs
  157. * in the flat list that could be obtained, omitting those that could
  158. * not. Only in those cases should the SHOULD recommendation be ignored.
  159. *
  160. *
  161. */
  162. //FIXME add support to really external uris
  163. ExternalType externalType = (ExternalType)element.getValue();
  164. ListReferenceEndpointAddress externalTypeAddress = ra.getAddressParser().getAddress(externalType.getAnchor(),true);
  165. if (externalTypeAddress == null) {
  166. status = RLSService.Status.BAD_GATEWAY;
  167. continue;
  168. }
  169. if (!externalTypeAddress.getElementSelector().getLastStep().getNameWithoutPrefix().equals("list")) {
  170. // not pointing to a list
  171. status = RLSService.Status.BAD_GATEWAY;
  172. continue;
  173. }
  174. referencesToAdd.add(externalTypeAddress);
  175. }
  176. else if (element.getValue() instanceof ListType) {
  177. ListType innerListType = (ListType) element.getValue();
  178. listsUpdated.add(innerListType.getName());
  179. ResourceList resourceList = lists.get(innerListType.getName());
  180. if (resourceList == null) {
  181. addResourceList(innerListType.getName());
  182. }
  183. resourceList.setListType(innerListType);
  184. }
  185. }
  186. // remove all entries that exist but are not in the list type
  187. Entry entry = null;
  188. String entryName = null;
  189. for (Iterator<String> it = localEntries.keySet().iterator();it.hasNext();) {
  190. entryName = it.next();
  191. if (!entriesUpdated.contains(entryName)) {
  192. entry = localEntries.get(entryName);
  193. if (entry.hasFromReferences()) {
  194. entry.setEntryType(null);
  195. }
  196. else {
  197. // no refs and does not really exist, remove
  198. it.remove();
  199. }
  200. }
  201. }
  202. // remove all inner lists that exist but are not in the list type
  203. ResourceList innerList = null;
  204. String innerListName = null;
  205. for (Iterator<String> it = lists.keySet().iterator();it.hasNext();) {
  206. innerListName = it.next();
  207. if (!listsUpdated.contains(innerListName)) {
  208. innerList = lists.get(innerListName);
  209. if (innerList.hasFromReferences()) {
  210. innerList.setListType(null);
  211. }
  212. else {
  213. // no refs and does not really exist, remove
  214. it.remove();
  215. }
  216. }
  217. }
  218. // remove existent references not found on this update and remove existent ones from the set to add
  219. ListReferenceEndpointAddress referenceAddress = null;
  220. for (Iterator<ListReferenceEndpointAddress> it = toReferences.keySet().iterator();it.hasNext();) {
  221. referenceAddress = it.next();
  222. if (!referencesToAdd.remove(referenceAddress)) {
  223. it.remove();
  224. ra.removeReference(getAddress(),referenceAddress);
  225. }
  226. }
  227. if (!referencesToAdd.isEmpty()) {
  228. // create references
  229. ListReferenceTo referenceTo = null;
  230. for (ListReferenceEndpointAddress address : referencesToAdd) {
  231. referenceTo = ra.addReference(this,address);
  232. if (referenceTo == null) {
  233. status = Status.BAD_GATEWAY;
  234. }
  235. else {
  236. toReferences.put(address, referenceTo);
  237. }
  238. }
  239. }
  240. }
  241. private Entry addEntry(String uri) {
  242. LinkedList<ElementSelectorStep> elementSelectorSteps = cloneListAddressElementSelectorSteps();
  243. elementSelectorSteps.add(new ElementSelectorStepByAttr("entry", "uri", uri));
  244. Entry newEntry = new Entry(new ListReferenceEndpointAddress(getAddress().getDocumentSelector(), new ElementSelector(elementSelectorSteps)));
  245. Entry entry = localEntries.putIfAbsent(uri, newEntry);
  246. if (entry == null) {
  247. entry = newEntry;
  248. }
  249. return entry;
  250. }
  251. private ResourceList addResourceList(String name) {
  252. LinkedList<ElementSelectorStep> elementSelectorSteps = cloneListAddressElementSelectorSteps();
  253. elementSelectorSteps.add(new ElementSelectorStepByAttr("list", "name", name));
  254. ResourceList newResourceList = new ResourceList(new ListReferenceEndpointAddress(getAddress().getDocumentSelector(), new ElementSelector(elementSelectorSteps)),this,ra);
  255. ResourceList resourceList = lists.putIfAbsent(name, newResourceList);
  256. if (resourceList == null) {
  257. resourceList = newResourceList;
  258. }
  259. return resourceList;
  260. }
  261. private LinkedList<ElementSelectorStep> cloneListAddressElementSelectorSteps() {
  262. LinkedList<ElementSelectorStep> elementSelectorSteps = new LinkedList<ElementSelectorStep>();
  263. ListReferenceEndpointAddress listAddress = getAddress();
  264. ElementSelector listElementSelector = listAddress.getElementSelector();
  265. for (int j=0;j<listElementSelector.getStepsSize();j++) {
  266. elementSelectorSteps.add(listElementSelector.getStep(j));
  267. }
  268. return elementSelectorSteps;
  269. }
  270. public ConcurrentHashMap<ListReferenceEndpointAddress, ListReferenceTo> getToReferences() {
  271. return toReferences;
  272. }
  273. @Override
  274. public RLSService.Status getStatus() {
  275. if (status == RLSService.Status.BAD_GATEWAY || status == RLSService.Status.DOES_NOT_EXISTS) {
  276. return status;
  277. }
  278. else {
  279. boolean resolving = false;
  280. RLSService.Status s = null;
  281. for (ResourceList list : lists.values()) {
  282. s = list.getStatus();
  283. if (s == RLSService.Status.BAD_GATEWAY) {
  284. return RLSService.Status.BAD_GATEWAY;
  285. }
  286. else if (s == RLSService.Status.RESOLVING) {
  287. resolving = true;
  288. }
  289. }
  290. for (ListReferenceTo reference : toReferences.values()) {
  291. s = reference.getStatus();
  292. if (s == RLSService.Status.BAD_GATEWAY) {
  293. return RLSService.Status.BAD_GATEWAY;
  294. }
  295. else if (s == RLSService.Status.RESOLVING) {
  296. resolving = true;
  297. }
  298. }
  299. if (resolving) {
  300. return RLSService.Status.RESOLVING;
  301. }
  302. return status;
  303. }
  304. }
  305. @Override
  306. public void updated(ListReferenceTo referenceUpdated) {
  307. synchronized (this) {
  308. if (updating) {
  309. // ignore
  310. return;
  311. }
  312. RLSService.Status referenceStatus = referenceUpdated.getStatus();
  313. if (status == RLSService.Status.BAD_GATEWAY) {
  314. if (referenceStatus != RLSService.Status.BAD_GATEWAY) {
  315. // the bad gateway status may have come from a loop in this reference or entry ref not found, rebuild list
  316. setListType(listType);
  317. }
  318. }
  319. else {
  320. if (referenceStatus == RLSService.Status.BAD_GATEWAY) {
  321. status = RLSService.Status.BAD_GATEWAY;
  322. updated();
  323. }
  324. else {
  325. if (referenceUpdated.getClass() == this.getClass()) {
  326. // resource list
  327. if (referenceStatus == RLSService.Status.OK) {
  328. if (isLoop((ResourceList)referenceUpdated, new HashSet<ListReferenceEndpointAddress>())) {
  329. status = RLSService.Status.BAD_GATEWAY;
  330. }
  331. }
  332. }
  333. else {
  334. // entry ref
  335. if (referenceStatus == RLSService.Status.DOES_NOT_EXISTS) {
  336. status = RLSService.Status.BAD_GATEWAY;
  337. }
  338. }
  339. updated();
  340. }
  341. }
  342. }
  343. }
  344. private Set<EntryType> entriesCached;
  345. @Override
  346. void updated() {
  347. entriesCached = null;
  348. if (parent != null) {
  349. parent.updated(this);
  350. }
  351. super.updated();
  352. }
  353. @Override
  354. public Set<EntryType> getEntries() {
  355. if (status == RLSService.Status.BAD_GATEWAY) {
  356. throw new IllegalStateException("list is in bad gateway state");
  357. }
  358. if (entriesCached != null) {
  359. return entriesCached;
  360. }
  361. synchronized (this) {
  362. Set<EntryType> result = new HashSet<EntryType>();
  363. Set<String> entryURIs = new HashSet<String>();
  364. for (Entry entry : localEntries.values()) {
  365. if (entryURIs.add(entry.getEntryType().getUri())) {
  366. result.add(entry.getEntryType());
  367. }
  368. }
  369. for (ResourceList list : lists.values()) {
  370. for (EntryType entryType : list.getEntries()) {
  371. if (entryURIs.add(entryType.getUri())) {
  372. result.add(entryType);
  373. }
  374. }
  375. }
  376. for (ListReferenceTo reference : toReferences.values()) {
  377. for (EntryType entryType : reference.getEntries()) {
  378. if (entryURIs.add(entryType.getUri())) {
  379. result.add(entryType);
  380. }
  381. }
  382. }
  383. entriesCached = result;
  384. return entriesCached;
  385. }
  386. }
  387. @Override
  388. public ListReferenceTo addFromReference(ListReferenceFrom from, ListReferenceEndpointAddress toAddress) {
  389. int thisAddressStepsSize = getAddress().getElementSelector().getStepsSize();
  390. if (toAddress.getElementSelector().getStepsSize() > thisAddressStepsSize) {
  391. // reference for a sub element
  392. ElementSelectorStepByAttr elementSelectorStep = (ElementSelectorStepByAttr) toAddress.getElementSelector().getStep(thisAddressStepsSize);
  393. if (elementSelectorStep.getNameWithoutPrefix().equals("list")) {
  394. // resource list
  395. ResourceList resourceList = lists.get(elementSelectorStep.getAttrValue());
  396. if (resourceList == null) {
  397. addResourceList(elementSelectorStep.getAttrValue());
  398. }
  399. return resourceList.addFromReference(from, toAddress);
  400. }
  401. else {
  402. // entry ref
  403. Entry entry = localEntries.get(elementSelectorStep.getAttrValue());
  404. if (entry == null) {
  405. entry = addEntry(elementSelectorStep.getAttrValue());
  406. }
  407. return entry.addFromReference(from,toAddress);
  408. }
  409. }
  410. else {
  411. return super.addFromReference(from,toAddress);
  412. }
  413. }
  414. @Override
  415. public void removeFromReference(ListReferenceEndpointAddress fromAddress, ListReferenceEndpointAddress toAddress) {
  416. int thisAddressStepsSize = getAddress().getElementSelector().getStepsSize();
  417. if (toAddress.getElementSelector().getStepsSize() > thisAddressStepsSize) {
  418. // reference for a sub element
  419. ElementSelectorStepByAttr elementSelectorStep = (ElementSelectorStepByAttr) toAddress.getElementSelector().getStep(thisAddressStepsSize);
  420. if (elementSelectorStep.getNameWithoutPrefix().equals("list")) {
  421. // resource list
  422. ResourceList resourceList = lists.get(elementSelectorStep.getAttrValue());
  423. if (resourceList != null) {
  424. resourceList.removeFromReference(fromAddress, toAddress);
  425. if (resourceList.getStatus() == RLSService.Status.DOES_NOT_EXISTS && !resourceList.hasFromReferences()) {
  426. lists.remove(elementSelectorStep.getAttrValue());
  427. }
  428. }
  429. }
  430. else {
  431. // entry ref
  432. Entry entry = localEntries.get(elementSelectorStep.getAttrValue());
  433. if (entry != null) {
  434. entry.removeFromReference(fromAddress, toAddress);
  435. if (entry.getStatus() == RLSService.Status.DOES_NOT_EXISTS && !entry.hasFromReferences()) {
  436. localEntries.remove(elementSelectorStep.getAttrValue());
  437. }
  438. }
  439. }
  440. }
  441. else {
  442. super.removeFromReference(fromAddress,toAddress);
  443. }
  444. }
  445. @Override
  446. public boolean hasFromReferences() {
  447. if (super.hasFromReferences()) {
  448. return true;
  449. }
  450. for (ResourceList list : lists.values()) {
  451. if (list.hasFromReferences()) {
  452. return true;
  453. }
  454. }
  455. for (Entry entry : localEntries.values()) {
  456. if (entry.hasFromReferences()) {
  457. return true;
  458. }
  459. }
  460. return false;
  461. }
  462. private boolean isLoop(ResourceList list, Set<ListReferenceEndpointAddress> references) {
  463. for (ResourceList innerList : list.lists.values()) {
  464. if (references.add(innerList.getAddress())) {
  465. if (isLoop(innerList, references)); {
  466. return true;
  467. }
  468. }
  469. }
  470. for (ListReferenceTo reference : list.toReferences.values()) {
  471. if (references.add(reference.getAddress())) {
  472. // check address is not the same as this one or an ancestor
  473. if (this.getAddress().getDocumentSelector().equals(reference.getAddress().getDocumentSelector()) &&
  474. this.getAddress().getElementSelector().toString().startsWith(reference.getAddress().getElementSelector().toString())) {
  475. // loop found
  476. return true;
  477. }
  478. // no loop, if resource list dig in
  479. if (reference.getClass() == this.getClass()) {
  480. // resource list
  481. if (isLoop((ResourceList)reference, references)); {
  482. return true;
  483. }
  484. }
  485. }
  486. }
  487. return false;
  488. }
  489. }