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

/servers/sip-presence/xdm/server/core/xcap-control/sbb/src/main/java/org/openxdm/xcap/server/slee/RequestProcessorSbb.java

http://mobicents.googlecode.com/
Java | 2047 lines | 1530 code | 161 blank | 356 comment | 386 complexity | df16add4c4bbea412252986dec8421a3 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.openxdm.xcap.server.slee;
  23. import java.io.InputStream;
  24. import java.io.Reader;
  25. import java.io.StringReader;
  26. import java.util.Collection;
  27. import java.util.HashMap;
  28. import java.util.Map;
  29. import javax.naming.Context;
  30. import javax.naming.InitialContext;
  31. import javax.naming.NamingException;
  32. import javax.slee.ActivityContextInterface;
  33. import javax.slee.RolledBackContext;
  34. import javax.slee.SbbContext;
  35. import javax.slee.facilities.Tracer;
  36. import javax.xml.transform.TransformerException;
  37. import javax.xml.xpath.XPath;
  38. import javax.xml.xpath.XPathConstants;
  39. import javax.xml.xpath.XPathExpressionException;
  40. import org.mobicents.slee.xdm.server.ServerConfiguration;
  41. import org.mobicents.xdm.common.util.dom.DocumentCloner;
  42. import org.mobicents.xdm.common.util.dom.DomUtils;
  43. import org.mobicents.xdm.server.appusage.AppUsage;
  44. import org.mobicents.xdm.server.appusage.AppUsageManagement;
  45. import org.mobicents.xdm.server.appusage.AuthorizationPolicy;
  46. import org.openxdm.xcap.common.error.BadRequestException;
  47. import org.openxdm.xcap.common.error.CannotDeleteConflictException;
  48. import org.openxdm.xcap.common.error.CannotInsertConflictException;
  49. import org.openxdm.xcap.common.error.ConflictException;
  50. import org.openxdm.xcap.common.error.ConstraintFailureConflictException;
  51. import org.openxdm.xcap.common.error.InternalServerErrorException;
  52. import org.openxdm.xcap.common.error.MethodNotAllowedException;
  53. import org.openxdm.xcap.common.error.NoParentConflictException;
  54. import org.openxdm.xcap.common.error.NotAuthorizedRequestException;
  55. import org.openxdm.xcap.common.error.NotFoundException;
  56. import org.openxdm.xcap.common.error.NotUTF8ConflictException;
  57. import org.openxdm.xcap.common.error.NotValidXMLFragmentConflictException;
  58. import org.openxdm.xcap.common.error.NotXMLAttributeValueConflictException;
  59. import org.openxdm.xcap.common.error.PreconditionFailedException;
  60. import org.openxdm.xcap.common.error.SchemaValidationErrorConflictException;
  61. import org.openxdm.xcap.common.error.UniquenessFailureConflictException;
  62. import org.openxdm.xcap.common.error.UnsupportedMediaTypeException;
  63. import org.openxdm.xcap.common.etag.ETagGenerator;
  64. import org.openxdm.xcap.common.resource.AttributeResource;
  65. import org.openxdm.xcap.common.resource.DocumentResource;
  66. import org.openxdm.xcap.common.resource.ElementResource;
  67. import org.openxdm.xcap.common.resource.NamespaceBindings;
  68. import org.openxdm.xcap.common.uri.AttributeSelector;
  69. import org.openxdm.xcap.common.uri.DocumentSelector;
  70. import org.openxdm.xcap.common.uri.ElementSelector;
  71. import org.openxdm.xcap.common.uri.ElementSelectorStep;
  72. import org.openxdm.xcap.common.uri.ElementSelectorStepByAttr;
  73. import org.openxdm.xcap.common.uri.ElementSelectorStepByPos;
  74. import org.openxdm.xcap.common.uri.ElementSelectorStepByPosAttr;
  75. import org.openxdm.xcap.common.uri.NodeSelector;
  76. import org.openxdm.xcap.common.uri.ParseException;
  77. import org.openxdm.xcap.common.uri.Parser;
  78. import org.openxdm.xcap.common.uri.ResourceSelector;
  79. import org.openxdm.xcap.common.uri.TerminalSelector;
  80. import org.openxdm.xcap.common.xml.NamespaceContext;
  81. import org.openxdm.xcap.common.xml.TextWriter;
  82. import org.openxdm.xcap.common.xml.XMLValidator;
  83. import org.openxdm.xcap.server.etag.ETagValidator;
  84. import org.openxdm.xcap.server.result.CreatedWriteResult;
  85. import org.openxdm.xcap.server.result.OKWriteResult;
  86. import org.openxdm.xcap.server.result.ReadResult;
  87. import org.openxdm.xcap.server.result.WriteResult;
  88. import org.openxdm.xcap.server.slee.resource.datasource.DataSourceSbbInterface;
  89. import org.w3c.dom.Attr;
  90. import org.w3c.dom.Document;
  91. import org.w3c.dom.Element;
  92. import org.w3c.dom.NamedNodeMap;
  93. import org.w3c.dom.Node;
  94. import org.w3c.dom.NodeList;
  95. public abstract class RequestProcessorSbb implements RequestProcessor,
  96. javax.slee.Sbb {
  97. private SbbContext sbbContext = null;
  98. private static Tracer logger;
  99. private DataSourceSbbInterface dataSourceSbbInterface;
  100. private AppUsageDataSourceImpl appUsageDataSource;
  101. private static final ServerConfiguration CONFIGURATION = ServerConfiguration
  102. .getInstance();
  103. private static final AppUsageManagement APPUSAGE_MANAGEMENT = AppUsageManagement
  104. .getInstance();
  105. /**
  106. * Called when an sbb object is instantied and enters the pooled state.
  107. */
  108. public void setSbbContext(SbbContext context) {
  109. this.sbbContext = context;
  110. if (logger == null) {
  111. logger = sbbContext.getTracer(this.getClass().getSimpleName());
  112. }
  113. try {
  114. Context myEnv = (Context) new InitialContext()
  115. .lookup("java:comp/env");
  116. dataSourceSbbInterface = (DataSourceSbbInterface) myEnv
  117. .lookup("slee/resources/openxdm/datasource/sbbrainterface");
  118. appUsageDataSource = new AppUsageDataSourceImpl(
  119. dataSourceSbbInterface);
  120. } catch (NamingException e) {
  121. logger.severe("Can't set sbb context.", e);
  122. }
  123. }
  124. public void unsetSbbContext() {
  125. this.sbbContext = null;
  126. }
  127. public void sbbCreate() throws javax.slee.CreateException {
  128. }
  129. public void sbbPostCreate() throws javax.slee.CreateException {
  130. }
  131. public void sbbActivate() {
  132. }
  133. public void sbbPassivate() {
  134. }
  135. public void sbbRemove() {
  136. }
  137. public void sbbLoad() {
  138. }
  139. public void sbbStore() {
  140. }
  141. public void sbbExceptionThrown(Exception exception, Object event,
  142. ActivityContextInterface activity) {
  143. if (logger.isFineEnabled())
  144. logger.fine("sbbExceptionThrown(exception=" + exception.toString()
  145. + ",event=" + event.toString() + ",activity="
  146. + activity.toString() + ")");
  147. }
  148. public void sbbRolledBack(RolledBackContext sbbRolledBack) {
  149. if (logger.isFineEnabled())
  150. logger.fine("sbbRolledBack(sbbRolledBack="
  151. + sbbRolledBack.toString() + ")");
  152. }
  153. /*
  154. * (non-Javadoc)
  155. *
  156. * @see
  157. * org.openxdm.xcap.server.slee.RequestProcessor#delete(org.openxdm.xcap
  158. * .common.uri.ResourceSelector, org.openxdm.xcap.server.etag.ETagValidator,
  159. * java.lang.String, java.lang.String)
  160. */
  161. @Override
  162. public WriteResult delete(ResourceSelector resourceSelector,
  163. ETagValidator eTagValidator, String xcapRoot,
  164. String authenticatedUser) throws NotFoundException,
  165. InternalServerErrorException, BadRequestException,
  166. CannotDeleteConflictException, PreconditionFailedException,
  167. MethodNotAllowedException, SchemaValidationErrorConflictException,
  168. UniquenessFailureConflictException,
  169. ConstraintFailureConflictException, NotAuthorizedRequestException {
  170. if (logger.isFineEnabled())
  171. logger.fine("deleting " + resourceSelector);
  172. AppUsage appUsage = null;
  173. try {
  174. // parse document selector
  175. final DocumentSelector documentSelector = DocumentSelector
  176. .valueOf(resourceSelector.getDocumentSelector());
  177. // get app usage
  178. appUsage = APPUSAGE_MANAGEMENT.getAppUsage(documentSelector
  179. .getAUID());
  180. if (appUsage == null) {
  181. // throw exception
  182. if (logger.isFineEnabled())
  183. logger.fine("appusage " + documentSelector.getAUID()
  184. + " not found");
  185. throw new NotFoundException();
  186. }
  187. // authorize user
  188. if (authenticatedUser != null
  189. && !appUsage.getAuthorizationPolicy().isAuthorized(
  190. authenticatedUser,
  191. AuthorizationPolicy.Operation.DELETE,
  192. documentSelector, appUsageDataSource)) {
  193. throw new NotAuthorizedRequestException();
  194. }
  195. // get document
  196. org.openxdm.xcap.common.datasource.Document document = dataSourceSbbInterface
  197. .getDocument(documentSelector);
  198. if (document == null) {
  199. // throw exception
  200. if (logger.isFineEnabled())
  201. logger.fine("document " + documentSelector + " not found");
  202. throw new NotFoundException();
  203. }
  204. if (logger.isFineEnabled())
  205. logger.fine("document " + documentSelector + " found");
  206. // check document etag
  207. if (eTagValidator != null) {
  208. eTagValidator.validate(document.getETag());
  209. if (logger.isFineEnabled())
  210. logger.fine("document " + documentSelector
  211. + " etag validated");
  212. } else {
  213. if (logger.isFineEnabled())
  214. logger.fine("document " + documentSelector
  215. + " etag validation not required");
  216. }
  217. if (resourceSelector.getNodeSelector() != null) {
  218. // elem, attr or namespace bind
  219. // parse node selector
  220. final NodeSelector nodeSelector = Parser.parseNodeSelector(
  221. resourceSelector.getNodeSelector(),
  222. resourceSelector.getNamespaceContext());
  223. if (logger.isFineEnabled())
  224. logger.fine("node selector " + nodeSelector
  225. + " found and parsed");
  226. // config namespace context
  227. final NamespaceContext namespaceContext = resourceSelector
  228. .getNamespaceContext();
  229. namespaceContext.setDefaultDocNamespace(appUsage
  230. .getDefaultDocumentNamespace());
  231. // clone doc
  232. final Document newDocumentDOM = DocumentCloner.clone(document
  233. .getAsDOMDocument());
  234. // get element
  235. final Element newElement = getElementForDeleteOrGet(
  236. newDocumentDOM, nodeSelector, false);
  237. // parse element selector
  238. final ElementSelector elementSelector = Parser
  239. .parseElementSelector(nodeSelector.getElementSelector());
  240. if (nodeSelector.getTerminalSelector() != null) {
  241. // delete attr or namespace bind
  242. // parse terminal selector
  243. TerminalSelector terminalSelector = Parser
  244. .parseTerminalSelector(nodeSelector
  245. .getTerminalSelector());
  246. if (logger.isFineEnabled())
  247. logger.fine("terminal selector " + terminalSelector
  248. + " found and parsed");
  249. if (terminalSelector instanceof AttributeSelector) {
  250. return deleteAttribute(document, newDocumentDOM,
  251. newElement, documentSelector, nodeSelector,
  252. elementSelector,
  253. (AttributeSelector) terminalSelector, appUsage,
  254. true);
  255. } else {
  256. // namespace selector, only GET method is allowed
  257. if (logger.isFineEnabled())
  258. logger.fine("terminal selector "
  259. + terminalSelector
  260. + " is a namespace selector, not allowed on delete");
  261. Map<String, String> map = new HashMap<String, String>();
  262. map.put("Allow", "GET");
  263. throw new MethodNotAllowedException(map);
  264. }
  265. } else {
  266. // delete element
  267. return deleteElement(document, newDocumentDOM, newElement,
  268. documentSelector, nodeSelector, elementSelector,
  269. appUsage, true);
  270. }
  271. } else {
  272. return deleteDocument(document, documentSelector, appUsage,
  273. true);
  274. }
  275. } catch (ParseException e) {
  276. if (logger.isFineEnabled())
  277. logger.fine("error parsing uri, returning not found");
  278. throw new NotFoundException();
  279. }
  280. }
  281. private WriteResult deleteDocument(
  282. org.openxdm.xcap.common.datasource.Document document,
  283. DocumentSelector documentSelector, AppUsage appUsage,
  284. boolean processResourceInterdependencies)
  285. throws InternalServerErrorException,
  286. ConstraintFailureConflictException,
  287. UniquenessFailureConflictException,
  288. SchemaValidationErrorConflictException {
  289. if (logger.isFineEnabled())
  290. logger.fine("deleting document " + documentSelector);
  291. if (logger.isFineEnabled())
  292. logger.fine("processing app usage resource interdependencies for "
  293. + documentSelector);
  294. // process resource interdependencies for the request app usage
  295. if (processResourceInterdependencies) {
  296. try {
  297. appUsage.processResourceInterdependenciesOnDeleteDocument(
  298. document.getAsDOMDocument(), documentSelector, this,
  299. appUsageDataSource);
  300. } catch (SchemaValidationErrorConflictException e) {
  301. // must rollback all changes in datasource
  302. if (!sbbContext.getRollbackOnly())
  303. sbbContext.setRollbackOnly();
  304. throw e;
  305. } catch (UniquenessFailureConflictException e) {
  306. if (!sbbContext.getRollbackOnly())
  307. sbbContext.setRollbackOnly();
  308. throw e;
  309. } catch (ConstraintFailureConflictException e) {
  310. if (!sbbContext.getRollbackOnly())
  311. sbbContext.setRollbackOnly();
  312. throw e;
  313. } catch (InternalServerErrorException e) {
  314. if (!sbbContext.getRollbackOnly())
  315. sbbContext.setRollbackOnly();
  316. throw e;
  317. }
  318. }
  319. if (logger.isFineEnabled())
  320. logger.fine("app usage resource interdependencies processed for "
  321. + documentSelector);
  322. // delete document
  323. try {
  324. dataSourceSbbInterface.deleteDocument(documentSelector,
  325. appUsage.getDefaultDocumentNamespace(), document);
  326. } catch (InternalServerErrorException e) {
  327. if (!sbbContext.getRollbackOnly())
  328. sbbContext.setRollbackOnly();
  329. throw e;
  330. }
  331. if (logger.isFineEnabled())
  332. logger.fine(documentSelector.toString() + " deleted");
  333. return new OKWriteResult();
  334. }
  335. private WriteResult deleteElement(
  336. final org.openxdm.xcap.common.datasource.Document oldDocument,
  337. final Document newDocumentDOM, final Element newElement,
  338. final DocumentSelector documentSelector,
  339. final NodeSelector nodeSelector,
  340. final ElementSelector elementSelector, AppUsage appUsage,
  341. boolean processResourceInterdependencies)
  342. throws InternalServerErrorException, NotFoundException,
  343. CannotDeleteConflictException,
  344. SchemaValidationErrorConflictException, BadRequestException,
  345. UniquenessFailureConflictException,
  346. ConstraintFailureConflictException {
  347. if (logger.isFineEnabled())
  348. logger.fine("deleting element " + elementSelector + " in document "
  349. + documentSelector);
  350. // check cannot delete
  351. ElementSelectorStep lastElementSelectorStep = elementSelector
  352. .getLastStep();
  353. if (lastElementSelectorStep instanceof ElementSelectorStepByPosAttr) {
  354. // need to check if it's the last sibring
  355. // with the same name and attr value
  356. ElementSelectorStepByPosAttr elementSelectorStepByPosAttr = (ElementSelectorStepByPosAttr) lastElementSelectorStep;
  357. if (elementSelectorStepByPosAttr.getName().equals("*")) {
  358. if (logger.isFineEnabled())
  359. logger.fine("element selector by attr and pos with wildcard name");
  360. // all elements wildcard
  361. Element siblingElement = newElement;
  362. while ((siblingElement = (Element) siblingElement
  363. .getNextSibling()) != null) {
  364. // get attribute with same name
  365. Attr siblingElementAttr = siblingElement
  366. .getAttributeNode(elementSelectorStepByPosAttr
  367. .getAttrName());
  368. // check if it has the same value
  369. if (siblingElementAttr != null
  370. && siblingElementAttr.getValue()
  371. .equals(elementSelectorStepByPosAttr
  372. .getAttrValue())) {
  373. // we have a sibling with the
  374. // same attribute with the same
  375. // value, so when we delete the
  376. // element the uri points to
  377. // this one
  378. if (logger.isFineEnabled())
  379. logger.fine("sibling element with same attr name and value, cannot delete");
  380. throw new CannotDeleteConflictException();
  381. }
  382. }
  383. } else {
  384. if (logger.isFineEnabled())
  385. logger.fine("element selector by attr and pos without wildcard name");
  386. Element siblingElement = newElement;
  387. while ((siblingElement = (Element) siblingElement
  388. .getNextSibling()) != null) {
  389. if (newElement.getNodeName().compareTo(
  390. siblingElement.getNodeName()) == 0
  391. && newElement.getNamespaceURI().compareTo(
  392. siblingElement.getNamespaceURI()) == 0) {
  393. // sibling with the same name
  394. // get attribute with same name
  395. Attr siblingElementAttr = siblingElement
  396. .getAttributeNode(elementSelectorStepByPosAttr
  397. .getAttrName());
  398. // check if it has the same
  399. // value
  400. if (siblingElementAttr != null
  401. && siblingElementAttr.getValue().equals(
  402. elementSelectorStepByPosAttr
  403. .getAttrValue())) {
  404. // we have a sibling with
  405. // the same attribute with
  406. // the same value, so when
  407. // we delete the element the
  408. // uri points to this one
  409. if (logger.isFineEnabled())
  410. logger.fine("sibling element with same attr name and value, cannot delete");
  411. throw new CannotDeleteConflictException();
  412. }
  413. }
  414. }
  415. }
  416. } else if (lastElementSelectorStep instanceof ElementSelectorStepByPos) {
  417. ElementSelectorStepByPos elementSelectorStepByPos = (ElementSelectorStepByPos) lastElementSelectorStep;
  418. /*
  419. * In particular, if a DELETE operation refers to an element by name
  420. * and position alone (parent/elname[n]), this is permitted only
  421. * when the element to be deleted is the last element amongst all
  422. * its siblings with that name. Similarly, if a DELETE operation
  423. * refers to an element by position alone (parent/*[n]), this is
  424. * permitted only when the elemented to be deleted is the last
  425. * amongst all sibling elements, regardless of name.
  426. */
  427. // find out if it's the last sibling
  428. if (elementSelectorStepByPos.getName().equals("*")) {
  429. if (logger.isFineEnabled())
  430. logger.fine("element selector by pos with wildcard name");
  431. if (newElement.getNextSibling() != null) {
  432. // not the last * sibling
  433. if (logger.isFineEnabled())
  434. logger.fine("not the last * sibling, cannot delete");
  435. throw new CannotDeleteConflictException();
  436. }
  437. } else {
  438. if (logger.isFineEnabled())
  439. logger.fine("element selector by pos without wildcard name");
  440. // search a next sibling with the same
  441. // name
  442. Element siblingElement = newElement;
  443. while ((siblingElement = (Element) siblingElement
  444. .getNextSibling()) != null) {
  445. if (newElement.getNodeName().compareTo(
  446. siblingElement.getNodeName()) == 0
  447. && newElement.getNamespaceURI().compareTo(
  448. siblingElement.getNamespaceURI()) == 0) {
  449. if (logger.isFineEnabled())
  450. logger.fine("sibling element with same name and ns after the selected element,cannot delete");
  451. throw new CannotDeleteConflictException();
  452. }
  453. }
  454. }
  455. }
  456. if (logger.isFineEnabled())
  457. logger.fine("element deleted");
  458. // the element can be deleted
  459. newElement.getParentNode().removeChild(newElement);
  460. if (logger.isFineEnabled())
  461. logger.fine("validating document after delete");
  462. // validate the updated document against it's schema
  463. appUsage.validateSchema(newDocumentDOM);
  464. if (logger.isFineEnabled())
  465. logger.fine("checking app usage constraints and resource interdependencies...");
  466. // verify app usage constraints
  467. appUsage.checkConstraintsOnDelete(newDocumentDOM,
  468. CONFIGURATION.getXcapRoot(), documentSelector,
  469. appUsageDataSource);
  470. // create new etag
  471. String newETag = ETagGenerator.generate(documentSelector.toString());
  472. // process resource interdependencies for the request app usage
  473. if (processResourceInterdependencies) {
  474. try {
  475. appUsage.processResourceInterdependenciesOnDeleteElement(
  476. newElement, documentSelector, newETag, nodeSelector,
  477. elementSelector, this, appUsageDataSource);
  478. } catch (SchemaValidationErrorConflictException e) {
  479. if (!sbbContext.getRollbackOnly())
  480. sbbContext.setRollbackOnly();
  481. throw e;
  482. } catch (UniquenessFailureConflictException e) {
  483. if (!sbbContext.getRollbackOnly())
  484. sbbContext.setRollbackOnly();
  485. throw e;
  486. } catch (ConstraintFailureConflictException e) {
  487. if (!sbbContext.getRollbackOnly())
  488. sbbContext.setRollbackOnly();
  489. throw e;
  490. } catch (InternalServerErrorException e) {
  491. if (!sbbContext.getRollbackOnly())
  492. sbbContext.setRollbackOnly();
  493. throw e;
  494. }
  495. }
  496. // update data source with document
  497. try {
  498. String newDocumentString = TextWriter.toString(newDocumentDOM);
  499. dataSourceSbbInterface.updateElement(documentSelector,
  500. appUsage.getDefaultDocumentNamespace(), oldDocument,
  501. newDocumentDOM, newDocumentString, newETag, nodeSelector,
  502. newElement, null);
  503. if (logger.isFineEnabled())
  504. logger.fine("document updated in data source");
  505. } catch (Exception e) {
  506. if (!sbbContext.getRollbackOnly())
  507. sbbContext.setRollbackOnly();
  508. throw new InternalServerErrorException(
  509. "Failed to serialize resulting dom document to string");
  510. }
  511. if (logger.isFineEnabled())
  512. logger.fine(elementSelector.toString() + " element in "
  513. + documentSelector + " deleted");
  514. return new OKWriteResult(newETag);
  515. }
  516. private WriteResult deleteAttribute(
  517. final org.openxdm.xcap.common.datasource.Document oldDocument,
  518. final Document newDocumentDOM, final Element newElement,
  519. final DocumentSelector documentSelector,
  520. final NodeSelector nodeSelector,
  521. final ElementSelector elementSelector,
  522. final AttributeSelector attributeSelector, final AppUsage appUsage,
  523. boolean processResourceInterdependencies)
  524. throws InternalServerErrorException, NotFoundException,
  525. SchemaValidationErrorConflictException, BadRequestException,
  526. UniquenessFailureConflictException,
  527. ConstraintFailureConflictException {
  528. if (logger.isFineEnabled())
  529. logger.fine("deleting attribute " + attributeSelector.getAttName()
  530. + " from element " + elementSelector + " in document "
  531. + documentSelector);
  532. final String attrName = attributeSelector.getAttName();
  533. String oldAttrValue = null;
  534. // note that getAttribute returns "" for a non existent attribute
  535. if (newElement.hasAttribute(attrName)) {
  536. oldAttrValue = newElement.getAttribute(attrName);
  537. }
  538. if (oldAttrValue != null) {
  539. // exists, delete it
  540. newElement.removeAttribute(attrName);
  541. if (logger.isFineEnabled())
  542. logger.fine("attribute found and deleted");
  543. } else {
  544. // does not exists
  545. if (logger.isFineEnabled())
  546. logger.fine("attribute to delete not found");
  547. throw new NotFoundException();
  548. }
  549. if (logger.isFineEnabled())
  550. logger.fine("validating document after delete");
  551. // validate the updated document against it's schema
  552. appUsage.validateSchema(newDocumentDOM);
  553. if (logger.isFineEnabled())
  554. logger.fine("checking app usage constraints and resource interdependencies...");
  555. // verify app usage constraints
  556. appUsage.checkConstraintsOnDelete(newDocumentDOM,
  557. CONFIGURATION.getXcapRoot(), documentSelector,
  558. appUsageDataSource);
  559. // create new etag
  560. String newETag = ETagGenerator.generate(documentSelector.toString());
  561. // process resource interdependencies
  562. if (processResourceInterdependencies) {
  563. try {
  564. appUsage.processResourceInterdependenciesOnDeleteAttribute(
  565. documentSelector, newETag, nodeSelector,
  566. elementSelector, attributeSelector, this,
  567. appUsageDataSource);
  568. } catch (SchemaValidationErrorConflictException e) {
  569. if (!sbbContext.getRollbackOnly())
  570. sbbContext.setRollbackOnly();
  571. throw e;
  572. } catch (UniquenessFailureConflictException e) {
  573. if (!sbbContext.getRollbackOnly())
  574. sbbContext.setRollbackOnly();
  575. throw e;
  576. } catch (ConstraintFailureConflictException e) {
  577. if (!sbbContext.getRollbackOnly())
  578. sbbContext.setRollbackOnly();
  579. throw e;
  580. } catch (InternalServerErrorException e) {
  581. if (!sbbContext.getRollbackOnly())
  582. sbbContext.setRollbackOnly();
  583. throw e;
  584. }
  585. }
  586. if (logger.isFineEnabled())
  587. logger.fine("app usage resource interdependencies processed for "
  588. + documentSelector);
  589. // update data source with document
  590. try {
  591. String newDocumentString = TextWriter.toString(newDocumentDOM);
  592. dataSourceSbbInterface.updateAttribute(documentSelector,
  593. appUsage.getDefaultDocumentNamespace(), oldDocument,
  594. newDocumentDOM, newDocumentString, newETag, nodeSelector,
  595. attributeSelector, oldAttrValue, null);
  596. if (logger.isFineEnabled())
  597. logger.fine("document updated in data source");
  598. } catch (Exception e) {
  599. if (!sbbContext.getRollbackOnly())
  600. sbbContext.setRollbackOnly();
  601. throw new InternalServerErrorException(
  602. "Failed to serialize resulting dom document to string");
  603. }
  604. return new OKWriteResult(newETag);
  605. }
  606. /*
  607. * (non-Javadoc)
  608. *
  609. * @see
  610. * org.openxdm.xcap.server.slee.RequestProcessor#get(org.openxdm.xcap.common
  611. * .uri.ResourceSelector, java.lang.String)
  612. */
  613. @Override
  614. public ReadResult get(ResourceSelector resourceSelector,
  615. String authenticatedUser) throws NotFoundException,
  616. InternalServerErrorException, BadRequestException,
  617. NotAuthorizedRequestException {
  618. AppUsage appUsage = null;
  619. try {
  620. // parse document parent String
  621. DocumentSelector documentSelector = DocumentSelector
  622. .valueOf(resourceSelector.getDocumentSelector());
  623. // get app usage from cache
  624. appUsage = APPUSAGE_MANAGEMENT.getAppUsage(documentSelector
  625. .getAUID());
  626. if (appUsage == null) {
  627. // throw exception
  628. if (logger.isFineEnabled())
  629. logger.fine("appusage not found");
  630. throw new NotFoundException();
  631. }
  632. // authorize user
  633. if (authenticatedUser != null
  634. && !appUsage.getAuthorizationPolicy().isAuthorized(
  635. authenticatedUser,
  636. AuthorizationPolicy.Operation.GET,
  637. documentSelector, appUsageDataSource)) {
  638. throw new NotAuthorizedRequestException();
  639. }
  640. // get document
  641. org.openxdm.xcap.common.datasource.Document document = dataSourceSbbInterface
  642. .getDocument(documentSelector);
  643. if (document == null) {
  644. // throw exception
  645. if (logger.isFineEnabled())
  646. logger.fine("document not found");
  647. throw new NotFoundException();
  648. }
  649. if (logger.isFineEnabled())
  650. logger.fine("document found");
  651. // get document's etag
  652. String eTag = document.getETag();
  653. // check node selector string from resource selector
  654. if (resourceSelector.getNodeSelector() != null) {
  655. // elem, attrib or namespace bind
  656. // parse node selector
  657. NodeSelector nodeSelector = Parser.parseNodeSelector(
  658. resourceSelector.getNodeSelector(),
  659. resourceSelector.getNamespaceContext());
  660. if (logger.isFineEnabled())
  661. logger.fine("node selector found and parsed");
  662. // create xpath
  663. XPath xpath = DomUtils.XPATH_FACTORY.newXPath();
  664. // add a namespace context to xpath to resolve bindings
  665. // config namespace context
  666. final NamespaceContext nsContext = resourceSelector
  667. .getNamespaceContext();
  668. nsContext.setDefaultDocNamespace(appUsage
  669. .getDefaultDocumentNamespace());
  670. xpath.setNamespaceContext(nsContext);
  671. if (logger.isFineEnabled())
  672. logger.fine("xpath initiated with namespace context");
  673. // get document as dom
  674. org.w3c.dom.Document domDocument = document.getAsDOMDocument();
  675. final Element element = getElementForDeleteOrGet(domDocument,
  676. nodeSelector, false);
  677. if (nodeSelector.getTerminalSelector() != null) {
  678. // parse terminal selector
  679. TerminalSelector terminalSelector = Parser
  680. .parseTerminalSelector(nodeSelector
  681. .getTerminalSelector());
  682. if (logger.isFineEnabled())
  683. logger.fine("terminal selector found and parsed");
  684. if (terminalSelector instanceof AttributeSelector) {
  685. // attribute selector, get attribute
  686. if (logger.isFineEnabled())
  687. logger.fine("terminal selector is an attribute selector");
  688. Attr attr = element
  689. .getAttributeNode(((AttributeSelector) terminalSelector)
  690. .getAttName());
  691. if (attr != null) {
  692. // exists, return its value
  693. if (logger.isFineEnabled())
  694. logger.fine("attribute found, returning result");
  695. return new ReadResult(eTag, new AttributeResource(
  696. attr.getNodeValue()));
  697. } else {
  698. // does not exists
  699. if (logger.isFineEnabled())
  700. logger.fine("attribute to retreive not found");
  701. throw new NotFoundException();
  702. }
  703. } else {
  704. // namespace selector, get namespace bindings
  705. if (logger.isFineEnabled())
  706. logger.fine("terminal selector is a namespace selector");
  707. return new ReadResult(eTag, getNamespaceBindings(
  708. element, DomUtils.getElementName(element),
  709. nsContext.getNamespaces()));
  710. }
  711. } else {
  712. // element
  713. if (logger.isFineEnabled())
  714. logger.fine("terminal selector not found, returining result with the element found");
  715. return new ReadResult(eTag, new ElementResource(
  716. TextWriter.toString(element)));
  717. }
  718. } else {
  719. // no node selector, just get the document
  720. if (logger.isFineEnabled())
  721. logger.fine("node selector not found, returning the document");
  722. return new ReadResult(eTag, new DocumentResource(
  723. document.getAsString(), appUsage.getMimetype()));
  724. }
  725. } catch (ParseException e) {
  726. if (logger.isFineEnabled())
  727. logger.fine("error in parsing uri.");
  728. throw new NotFoundException();
  729. } catch (TransformerException e) {
  730. logger.severe("unable to transform dom element to text.", e);
  731. throw new InternalServerErrorException(e.getMessage());
  732. }
  733. }
  734. private NamespaceBindings getNamespaceBindings(Node element,
  735. String elementName, Map<String, String> namespacesToGet)
  736. throws NotFoundException {
  737. boolean done = false;
  738. // init result namespaces map
  739. Map<String, String> result = new HashMap<String, String>();
  740. // remove empty prefix from "namespaces to get" map
  741. namespacesToGet.remove("");
  742. // create set of namespaces uri to get
  743. Collection<String> namespacesUris = namespacesToGet.values();
  744. while (done == false && element.getNodeType() == Node.ELEMENT_NODE) {
  745. // get element attributes
  746. NamedNodeMap elementAttributes = element.getAttributes();
  747. // process each one
  748. for (int i = 0; i < elementAttributes.getLength(); i++) {
  749. Node attributeNode = elementAttributes.item(i);
  750. if (attributeNode.getNodeName().compareTo("xmlns") == 0
  751. || attributeNode.getPrefix().compareTo("xmlns") == 0) {
  752. // its a namespace
  753. if (namespacesUris.contains(attributeNode.getNodeValue())) {
  754. // it was requested, add it to the result map
  755. result.put(attributeNode.getNodeName(),
  756. attributeNode.getNodeValue());
  757. if (result.size() == namespacesUris.size()) {
  758. done = true;
  759. break;
  760. }
  761. }
  762. }
  763. }
  764. // move to parent
  765. element = element.getParentNode();
  766. }
  767. if (!done) {
  768. // at least one was not found
  769. if (logger.isFineEnabled())
  770. logger.fine("didn't found any namespace binding, returning not found");
  771. throw new NotFoundException();
  772. } else {
  773. // return namespace bindings
  774. if (logger.isFineEnabled())
  775. logger.fine("found namespace binding(s)");
  776. return new NamespaceBindings(elementName, result);
  777. }
  778. }
  779. /*
  780. * (non-Javadoc)
  781. *
  782. * @see
  783. * org.openxdm.xcap.server.slee.RequestProcessor#put(org.openxdm.xcap.common
  784. * .uri.ResourceSelector, java.lang.String, java.io.InputStream,
  785. * org.openxdm.xcap.server.etag.ETagValidator, java.lang.String,
  786. * java.lang.String)
  787. */
  788. @Override
  789. public WriteResult put(ResourceSelector resourceSelector, String mimetype,
  790. InputStream contentStream, ETagValidator eTagValidator,
  791. String xcapRoot, String authenticatedUser)
  792. throws ConflictException, MethodNotAllowedException,
  793. UnsupportedMediaTypeException, InternalServerErrorException,
  794. PreconditionFailedException, BadRequestException,
  795. NotAuthorizedRequestException {
  796. DocumentSelector documentSelector = null;
  797. try {
  798. // parse document parent String
  799. documentSelector = DocumentSelector.valueOf(resourceSelector
  800. .getDocumentSelector());
  801. if (logger.isFineEnabled())
  802. logger.fine("document selector found and parsed: "
  803. + documentSelector);
  804. } catch (ParseException e) {
  805. // invalid document selector, throw no parent exception
  806. if (logger.isFineEnabled())
  807. logger.fine("failed to parse document selector, returning no parent conflict");
  808. throw new NoParentConflictException(xcapRoot);
  809. }
  810. // get app usage
  811. final AppUsage appUsage = APPUSAGE_MANAGEMENT
  812. .getAppUsage(documentSelector.getAUID());
  813. if (appUsage == null) {
  814. // throw exception
  815. if (logger.isFineEnabled())
  816. logger.fine("appusage " + documentSelector.getAUID()
  817. + " not found");
  818. throw new NoParentConflictException(xcapRoot);
  819. }
  820. if (logger.isFineEnabled())
  821. logger.fine("appusage " + documentSelector.getAUID() + " found");
  822. // authorize user
  823. if (authenticatedUser != null
  824. && !appUsage.getAuthorizationPolicy().isAuthorized(
  825. authenticatedUser, AuthorizationPolicy.Operation.PUT,
  826. documentSelector, appUsageDataSource)) {
  827. throw new NotAuthorizedRequestException();
  828. }
  829. // try to get document's resource
  830. org.openxdm.xcap.common.datasource.Document oldDocument = dataSourceSbbInterface
  831. .getDocument(documentSelector);
  832. if (oldDocument != null) {
  833. // validate etag if needed
  834. if (eTagValidator != null) {
  835. eTagValidator.validate(oldDocument.getETag());
  836. if (logger.isFineEnabled())
  837. logger.fine("document etag validated");
  838. } else {
  839. if (logger.isFineEnabled())
  840. logger.fine("document etag validation not required");
  841. }
  842. }
  843. if (resourceSelector.getNodeSelector() != null) {
  844. // put elem, attr, namespaces
  845. if (oldDocument == null) {
  846. // doc does not exists, throw exception
  847. throw new NoParentConflictException(xcapRoot
  848. + documentSelector.getCollection());
  849. }
  850. NodeSelector nodeSelector = null;
  851. ElementSelector elementSelector = null;
  852. try {
  853. // parse node selector
  854. nodeSelector = Parser.parseNodeSelector(
  855. resourceSelector.getNodeSelector(),
  856. resourceSelector.getNamespaceContext());
  857. elementSelector = Parser.parseElementSelector(nodeSelector
  858. .getElementSelector());
  859. if (logger.isFineEnabled())
  860. logger.fine("node selector found and parsed: "
  861. + nodeSelector);
  862. } catch (ParseException e) {
  863. // unable to parse the node selector, throw no parent
  864. // exception with the document as the existent ancestor
  865. if (logger.isFineEnabled())
  866. logger.fine("unable to parse the node selector or element selector, returning no parent conflict with the document as the existent ancestor");
  867. throw new NoParentConflictException(xcapRoot
  868. + resourceSelector.getDocumentSelector());
  869. }
  870. // config namespace context
  871. final NamespaceContext namespaceContext = resourceSelector
  872. .getNamespaceContext();
  873. namespaceContext.setDefaultDocNamespace(appUsage
  874. .getDefaultDocumentNamespace());
  875. // clone doc
  876. final Document newDocumentDOM = DocumentCloner.clone(oldDocument
  877. .getAsDOMDocument());
  878. // get element
  879. final Element element = getElementForPut(documentSelector,
  880. newDocumentDOM, nodeSelector, false);
  881. if (nodeSelector.getTerminalSelector() != null) {
  882. // put attr or namespaces
  883. try {
  884. TerminalSelector terminalSelector = Parser
  885. .parseTerminalSelector(nodeSelector
  886. .getTerminalSelector());
  887. if (terminalSelector instanceof AttributeSelector) {
  888. // verify mimetype
  889. if (mimetype == null
  890. || !mimetype.equals(AttributeResource.MIMETYPE)) {
  891. // mimetype is not correct
  892. throw new UnsupportedMediaTypeException();
  893. }
  894. // read attribute value (checking if is utf-8 too)
  895. final String newAttributeValue = XMLValidator
  896. .getUTF8String(contentStream);
  897. if (logger.isFineEnabled())
  898. logger.fine("attr content is utf-8");
  899. return putAttribute(oldDocument, newDocumentDOM,
  900. element, documentSelector, nodeSelector,
  901. elementSelector,
  902. (AttributeSelector) terminalSelector,
  903. newAttributeValue, appUsage, true);
  904. } else {
  905. if (logger.isFineEnabled())
  906. logger.fine("terminal selector is a namespace selector, not allowed on put");
  907. Map<String, String> map = new HashMap<String, String>();
  908. map.put("Allow", "GET");
  909. throw new MethodNotAllowedException(map);
  910. }
  911. } catch (ParseException e) {
  912. // unable to parse the node selector, throw no parent
  913. // exception with the document as the existent ancestor
  914. if (logger.isFineEnabled())
  915. logger.fine("unable to parse the node selector or element selector, returning no parent conflict with the document as the existent ancestor");
  916. throw new NoParentConflictException(xcapRoot
  917. + resourceSelector.getDocumentSelector());
  918. }
  919. } else {
  920. // check mimetype
  921. if (mimetype == null
  922. || !mimetype.equals(ElementResource.MIMETYPE)) {
  923. // mimetype is not correct
  924. throw new UnsupportedMediaTypeException();
  925. }
  926. // read and verify if content value is utf-8
  927. final String newElementAsString = XMLValidator
  928. .getUTF8String(contentStream);
  929. if (logger.isFineEnabled())
  930. logger.fine("content is utf-8");
  931. // create XML fragment node
  932. final Element newElement = XMLValidator
  933. .getWellFormedDocumentFragment(new StringReader(
  934. newElementAsString));
  935. if (logger.isFineEnabled())
  936. logger.fine("content is well formed document fragment");
  937. return putElement(oldDocument, newDocumentDOM, element,
  938. documentSelector, nodeSelector, elementSelector,
  939. newElement, appUsage, true);
  940. }
  941. } else {
  942. // put document
  943. // validate mimetype
  944. if (mimetype == null || !mimetype.equals(appUsage.getMimetype())) {
  945. // mimetype is not valid
  946. if (logger.isFineEnabled())
  947. logger.fine("invalid mimetype, does not matches the app usage");
  948. throw new UnsupportedMediaTypeException();
  949. }
  950. // verify if content is utf-8
  951. final Reader utf8reader = XMLValidator.getUTF8Reader(contentStream);
  952. if (logger.isFineEnabled())
  953. logger.fine("document content is utf-8");
  954. // build new document
  955. final Document newDomDocument = XMLValidator
  956. .getWellFormedDocument(utf8reader);
  957. if (logger.isFineEnabled())
  958. logger.fine("document content is well formed");
  959. return putDocument(documentSelector, oldDocument, newDomDocument,
  960. appUsage, true);
  961. }
  962. }
  963. // -- APP USAGE REQUEST PROCESSOR METHODS
  964. private WriteResult putDocument(DocumentSelector documentSelector,
  965. org.openxdm.xcap.common.datasource.Document oldDocument,
  966. Document newDocumentDOM, AppUsage appUsage,
  967. boolean processResourceInterdependencies) throws ConflictException,
  968. MethodNotAllowedException, UnsupportedMediaTypeException,
  969. InternalServerErrorException, PreconditionFailedException,
  970. BadRequestException, NotAuthorizedRequestException {
  971. Document oldDomDocument = null;
  972. if (oldDocument == null) { // DOCUMENTS DOES NOT EXIST
  973. if (logger.isFineEnabled())
  974. logger.fine("document not found");
  975. } else {
  976. // DOCUMENT EXISTS
  977. if (logger.isFineEnabled())
  978. logger.fine("document found");
  979. oldDomDocument = oldDocument.getAsDOMDocument();
  980. }
  981. // validate the updated document against it's schema
  982. appUsage.validateSchema(newDocumentDOM);
  983. if (logger.isFineEnabled())
  984. logger.fine("document validated by schema");
  985. // verify app usage constraints
  986. appUsage.checkConstraintsOnPut(newDocumentDOM,
  987. CONFIGURATION.getXcapRoot(), documentSelector,
  988. appUsageDataSource);
  989. if (logger.isFineEnabled())
  990. logger.fine("app usage constraints checked");
  991. // create new document etag
  992. String newETag = ETagGenerator.generate(documentSelector.toString());
  993. if (logger.isFineEnabled())
  994. logger.fine("new document etag generated");
  995. // process resource interdependencies for the request app usage
  996. if (processResourceInterdependencies) {
  997. try {
  998. appUsage.processResourceInterdependenciesOnPutDocument(
  999. oldDomDocument, newDocumentDOM, documentSelector,
  1000. newETag, this, appUsageDataSource);
  1001. } catch (SchemaValidationErrorConflictException e) {
  1002. if (!sbbContext.getRollbackOnly())
  1003. sbbContext.setRollbackOnly();
  1004. throw e;
  1005. } catch (UniquenessFailureConflictException e) {
  1006. if (!sbbContext.getRollbackOnly())
  1007. sbbContext.setRollbackOnly();
  1008. throw e;
  1009. } catch (InternalServerErrorException e) {
  1010. if (!sbbContext.getRollbackOnly())
  1011. sbbContext.setRollbackOnly();
  1012. throw e;
  1013. } catch (ConstraintFailureConflictException e) {
  1014. if (!sbbContext.getRollbackOnly())
  1015. sbbContext.setRollbackOnly();
  1016. throw e;
  1017. }
  1018. }
  1019. if (logger.isFineEnabled())
  1020. logger.fine("app usage resource interdependencies processed");
  1021. // update data source with document
  1022. try {
  1023. String newDocumentString = TextWriter.toString(newDocumentDOM);
  1024. if (oldDocument == null) {
  1025. dataSourceSbbInterface.createDocument(documentSelector,
  1026. appUsage.getDefaultDocumentNamespace(), newDocumentDOM,
  1027. newDocumentString, newETag);
  1028. if (logger.isFineEnabled())
  1029. logger.fine("document created in data source");
  1030. } else {
  1031. dataSourceSbbInterface.updateDocument(documentSelector,
  1032. appUsage.getDefaultDocumentNamespace(), oldDocument,
  1033. newDocumentDOM, newDocumentString, newETag);
  1034. if (logger.isFineEnabled())
  1035. logger.fine("document updated in data source");
  1036. }
  1037. } catch (Exception e) {
  1038. logger.severe(
  1039. "Failed to serialize resulting dom document to string", e);
  1040. throw new InternalServerErrorException(
  1041. "Failed to serialize resulting dom document to string", e);
  1042. }
  1043. return oldDocument == null ? new CreatedWriteResult(newETag)
  1044. : new OKWriteResult(newETag);
  1045. }
  1046. private WriteResult putElement(
  1047. final org.openxdm.xcap.common.datasource.Document oldDocument,
  1048. final Document newDocumentDOM, final Element oldElement,
  1049. final DocumentSelector documentSelector,
  1050. final NodeSelector nodeSelector,
  1051. final ElementSelector elementSelector, Element newElement,
  1052. final AppUsage appUsage, boolean processResourceInterdependencies)
  1053. throws InternalServerErrorException, NoParentConflictException,
  1054. SchemaValidationErrorConflictException,
  1055. UniquenessFailureConflictException,
  1056. ConstraintFailureConflictException, CannotInsertConflictException,
  1057. NotValidXMLFragmentConflictException, NotUTF8ConflictException,
  1058. BadRequestException, NotAuthorizedRequestException {
  1059. if (logger.isFineEnabled())
  1060. logger.fine("putting element " + elementSelector + " in "
  1061. + documentSelector);
  1062. if (oldElement != null) {
  1063. // replace element
  1064. // verify if cannot insert
  1065. ElementSelectorStep lastElementSelectorStep = elementSelector
  1066. .getLastStep();
  1067. // if element's tag name is not equal to
  1068. // this step's name then cannot insert
  1069. if (!newElement.getTagName().equals(
  1070. lastElementSelectorStep.getName())) {
  1071. if (logger.isFineEnabled())
  1072. logger.fine("element's tag name is not equal to this step's name, cannot insert");
  1073. throw new CannotInsertConflictException();
  1074. }
  1075. if (lastElementSelectorStep instanceof ElementSelectorStepByAttr) {
  1076. ElementSelectorStepByAttr elementSelectorStepByAttr = (ElementSelectorStepByAttr) lastElementSelectorStep;
  1077. // check attr value
  1078. String elementAttrValue = newElement
  1079. .getAttribute(elementSelectorStepByAttr.getAttrName());
  1080. if (elementAttrValue == null
  1081. || !elementAttrValue.equals(elementSelectorStepByAttr
  1082. .getAttrValue())) {
  1083. if (logger.isFineEnabled())
  1084. logger.fine("element selector's last step has an attr and it's new value changes this attr value, cannot insert");
  1085. throw new CannotInsertConflictException();
  1086. }
  1087. }
  1088. // import the element node
  1089. newElement = (Element) newDocumentDOM.importNode(newElement, true);
  1090. // replace node
  1091. oldElement.getParentNode().replaceChild(newElement, oldElement);
  1092. if (logger.isFineEnabled())
  1093. logger.fine("element " + elementSelector + " replaced in "
  1094. + documentSelector);
  1095. } else {
  1096. // new element
  1097. final Element elementParent = getElementForPut(documentSelector,
  1098. newDocumentDOM, nodeSelector, true);
  1099. if (elementParent == null) {
  1100. if (logger.isFineEnabled())
  1101. logger.fine("element parent not found, returning no parent conflict");
  1102. XPath xpath = DomUtils.XPATH_FACTORY.newXPath();
  1103. xpath.setNamespaceContext(nodeSelector.getNamespaceContext());
  1104. throw new NoParentConflictException(getElementExistentAncestor(
  1105. CONFIGURATION.getXcapRoot(),
  1106. documentSelector.toString(),
  1107. nodeSelector.getElementParentSelectorWithEmptyPrefix(),
  1108. newDocumentDOM, xpath));
  1109. } else {
  1110. // put new element
  1111. newElement = (Element) newDocumentDOM.importNode(newElement,
  1112. true);
  1113. // get element step
  1114. ElementSelectorStep elementLastStep = elementSelector
  1115. .getLastStep();
  1116. // get element name & namespace
  1117. String elementNamespace = null;
  1118. String elementName = null;
  1119. String elementNamePrefix = elementLastStep.getPrefix();
  1120. if (elementNamePrefix != null) {
  1121. // get element name without prefix
  1122. elementName = elementLastStep.getNameWithoutPrefix();
  1123. // and get namespace
  1124. elementNamespace = nodeSelector.getNamespaceContext()
  1125. .getNamespaceURI(elementNamePrefix);
  1126. } else {
  1127. // get element name without prefix
  1128. elementName = elementLastStep.getName();
  1129. // and get namespace
  1130. elementNamespace = nodeSelector.getNamespaceContext()
  1131. .getNamespaceURI("");
  1132. }
  1133. // if new element node name is not the same as in the uri then
  1134. // cannot
  1135. // insert
  1136. if (!newElement.getNodeName().equals(elementName)) {
  1137. if (logger.isFineEnabled())
  1138. logger.fine("element node name is not the same as in the uri, cannot insert");
  1139. throw new CannotInsertConflictException();
  1140. }
  1141. if (elementLastStep instanceof ElementSelectorStepByPos) {
  1142. // position defined
  1143. if (logger.isFineEnabled())
  1144. logger.fine("element selector's last step with position defined");
  1145. ElementSelectorStepByPos elementSelectorStepByPos = (ElementSelectorStepByPos) elementLastStep;
  1146. if (elementSelectorStepByPos.getPos() == 1) {
  1147. // POS = 1
  1148. if (!(elementLastStep instanceof ElementSelectorStepByPosAttr)) {
  1149. // NO ATTR TEST, *[1] e name[1], either way, just
  1150. // append to
  1151. // the parent
  1152. if (logger.isFineEnabled())
  1153. logger.fine("element selector's last step without attr test defined");
  1154. elementParent.appendChild(newElement);
  1155. if (logger.isFineEnabled())
  1156. logger.fine("element appended to parent");
  1157. } else {
  1158. // ATTR TEST
  1159. if (logger.isFineEnabled())
  1160. logger.fine("element selector's last step with attr test defined");
  1161. // verify that the element has this step atribute
  1162. // with this
  1163. // step attribute value, if not it cannot insert
  1164. ElementSelectorStepByPosAttr elementSelectorStepByPosAttr = (ElementSelectorStepByPosAttr) elementLastStep;
  1165. String elementAttrName = elementSelectorStepByPosAttr
  1166. .getAttrName();
  1167. String elementAttrValue = newElement
  1168. .getAttribute(elementAttrName);
  1169. if (elementAttrValue == null
  1170. || !elementAttrValue
  1171. .equals(elementSelectorStepByPosAttr
  1172. .getAttrValue())) {
  1173. if (logger.isFineEnabled())
  1174. logger.fine("element selector's last step has an atribute and the attribute value does not matches, cannot insert");
  1175. throw new CannotInsertConflictException();
  1176. }
  1177. // *[1][attr-test], insert before the first element
  1178. // name[1][attr-test], insert before the first
  1179. // element with
  1180. // same name
  1181. NodeList elementParentChilds = elementParent
  1182. .getChildNodes();
  1183. boolean inserted = false;
  1184. for (int i = 0; i < elementParentChilds.getLength(); i++) {
  1185. if (elementParentChilds.item(i) instanceof Element
  1186. && ((elementName
  1187. .equals(elementParentChilds
  1188. .item(i).getNodeName()) && elementParentChilds
  1189. .item(i).getNamespaceURI()
  1190. .equals(elementNamespace)) || (elementName
  1191. .equals("*")))) {
  1192. elementParent.insertBefore(newElement,
  1193. elementParentChilds.item(i));
  1194. if (logger.isFineEnabled())
  1195. logger.fine("element inserted at pos "
  1196. + i);
  1197. inserted = true;
  1198. break;
  1199. }
  1200. }
  1201. if (!inserted) {
  1202. // didn't found an element just append to parent
  1203. elementParent.appendChild(newElement);
  1204. if (logger.isFineEnabled())
  1205. logger.fine("element appended to parent");
  1206. }
  1207. }
  1208. }
  1209. else {
  1210. // POS > 1, must find the pos-1 element and insert after
  1211. if (elementLastStep instanceof ElementSelectorStepByPosAttr) {
  1212. // ATTR TEST
  1213. if (logger.isFineEnabled())
  1214. logger.fine("element selector's last step with attr test defined");
  1215. // verify that the element has this step atribute
  1216. // with this
  1217. // step attribute value, if not it cannot insert
  1218. ElementSelectorStepByPosAttr elementSelectorStepByPosAttr = (ElementSelectorStepByPosAttr) elementLastStep;
  1219. String elementAttrName = elementSelectorStepByPosAttr
  1220. .getAttrName();
  1221. String elementAttrValue = newElement
  1222. .getAttribute(elementAttrName);
  1223. if (elementAttrValue == null
  1224. || !elementAttrValue
  1225. .equals(elementSelectorStepByPosAttr
  1226. .getAttrValue())) {
  1227. if (logger.isFineEnabled())
  1228. logger.fine("element selector's last step has an atribute and the attribute value does not matches, cannot insert");
  1229. throw new CannotInsertConflictException();
  1230. }
  1231. }
  1232. // *[pos>1], name[pos>1], *[pos>1][attr-test],
  1233. // name[pos>1][attr-test], insert in the parent after
  1234. // the pos-1
  1235. // element
  1236. NodeList elementParentChilds = elementParent
  1237. .getChildNodes();
  1238. boolean inserted = false;
  1239. int elementsFound = 0;
  1240. for (int i = 0; i < elementParentChilds.getLength(); i++) {
  1241. if (elementParentChilds.item(i) instanceof Element
  1242. && ((elementName.equals(elementParentChilds
  1243. .item(i).getNodeName()) && elementParentChilds
  1244. .item(i).getNamespaceURI()
  1245. .equals(elementNamespace)) || (elementName
  1246. .equals("*")))) {
  1247. elementsFound++;
  1248. if (elementsFound == elementSelectorStepByPos
  1249. .getPos() - 1) {
  1250. // insert after
  1251. if (i == elementParentChilds.getLength() - 1) {
  1252. // no node after, use append
  1253. elementParent.appendChild(newElement);
  1254. if (logger.isFineEnabled())
  1255. logger.fine("element appended to parent");
  1256. } else {
  1257. // node after exists, insert before
  1258. elementParent
  1259. .insertBefore(newElement,
  1260. elementParentChilds
  1261. .item(i + 1));
  1262. if (logger.isFineEnabled())
  1263. logger.fine("element inserted at pos "
  1264. + i + 1);
  1265. }
  1266. inserted = true;
  1267. break;
  1268. }
  1269. }
  1270. }
  1271. if (!inserted) {
  1272. // didn't found pos-1 element, cannot insert
  1273. if (logger.isFineEnabled())
  1274. logger.fine("didn't found "
  1275. + (elementSelectorStepByPos.getPos() - 1)
  1276. + " element, cannot insert");
  1277. throw new CannotInsertConflictException();
  1278. }
  1279. }
  1280. }
  1281. else if (elementLastStep instanceof ElementSelectorStepByAttr) {
  1282. // no position defined
  1283. if (logger.isFineEnabled())
  1284. logger.fine("element selector's last step with attr test defined only");
  1285. // first verify element has this step atribute with this
  1286. // step
  1287. // attribute value, if not it cannot insert
  1288. ElementSelectorStepByAttr elementSelectorStepByAttr = (ElementSelectorStepByAttr) elementLastStep;
  1289. String elementAttrValue = newElement
  1290. .getAttribute(elementSelectorStepByAttr
  1291. .getAttrName());
  1292. if (elementAttrValue == null
  1293. || !elementAttrValue
  1294. .equals(elementSelectorStepByAttr
  1295. .getAttrValue())) {
  1296. if (logger.isFineEnabled())
  1297. logger.fine("element selector's last step has an atribute and the attribute value does not matches, cannot insert");
  1298. throw new CannotInsertConflictException();
  1299. }
  1300. // insert after the last with same name
  1301. NodeList elementParentChilds = elementParent
  1302. .getChildNodes();
  1303. boolean inserted = false;
  1304. for (int i = elementParentChilds.getLength() - 1; i > -1; i--) {
  1305. if (elementParentChilds.item(i) instanceof Element) {
  1306. if (elementParentChilds.item(i) instanceof Element
  1307. && ((elementName.equals(elementParentChilds
  1308. .item(i).getNodeName()) && elementParentChilds
  1309. .item(i).getNamespaceURI()
  1310. .equals(elementNamespace)) || (elementName
  1311. .equals("*")))) {
  1312. // insert after this element
  1313. if (i == elementParentChilds.getLength() - 1) {
  1314. elementParent.appendChild(newElement);
  1315. if (logger.isFineEnabled())
  1316. logger.fine("element appended to parent");
  1317. } else {
  1318. elementParent.insertBefore(newElement,
  1319. elementParentChilds.item(i + 1));
  1320. if (logger.isFineEnabled())
  1321. logger.fine("element inserted at pos "
  1322. + i + 1);
  1323. }
  1324. inserted = true;
  1325. break;
  1326. }
  1327. }
  1328. }
  1329. if (!inserted) {
  1330. // didn't found an element with same name and namespace,
  1331. // just
  1332. // append to parent
  1333. elementParent.appendChild(newElement);
  1334. if (logger.isFineEnabled())
  1335. logger.fine("element appended to parent");
  1336. }
  1337. }
  1338. else {
  1339. // no position and attr defined, it's the first child or the
  1340. // first
  1341. // with this name so just append new element
  1342. elementParent.appendChild(newElement);
  1343. if (logger.isFineEnabled())
  1344. logger.fine("element selector's last step without attr test or position defined, element appended to parent");
  1345. }
  1346. if (logger.isFineEnabled())
  1347. logger.fine("element parent found, new element added");
  1348. }
  1349. }
  1350. // validate the updated document against it's schema
  1351. appUsage.validateSchema(newDocumentDOM);
  1352. if (logger.isFineEnabled())
  1353. logger.fine("document validated by schema");
  1354. // verify app usage constraints
  1355. appUsage.checkConstraintsOnPut(newDocumentDOM,
  1356. CONFIGURATION.getXcapRoot(), documentSelector,
  1357. appUsageDataSource);
  1358. if (logger.isFineEnabled())
  1359. logger.fine("app usage constraints checked");
  1360. // create new document etag
  1361. String newETag = ETagGenerator.generate(documentSelector.toString());
  1362. if (logger.isFineEnabled())
  1363. logger.fine("new document etag generated");
  1364. // process resource interdependencies for the request app usage
  1365. if (processResourceInterdependencies) {
  1366. try {
  1367. appUsage.processResourceInterdependenciesOnPutElement(
  1368. oldElement, newElement, newDocumentDOM,
  1369. documentSelector, newETag, nodeSelector,
  1370. elementSelector, this, appUsageDataSource);
  1371. } catch (SchemaValidationErrorConflictException e) {
  1372. if (!sbbContext.getRollbackOnly())
  1373. sbbContext.setRollbackOnly();
  1374. throw e;
  1375. } catch (UniquenessFailureConflictException e) {
  1376. if (!sbbContext.getRollbackOnly())
  1377. sbbContext.setRollbackOnly();
  1378. throw e;
  1379. } catch (InternalServerErrorException e) {
  1380. if (!sbbContext.getRollbackOnly())
  1381. sbbContext.setRollbackOnly();
  1382. throw e;
  1383. } catch (ConstraintFailureConflictException e) {
  1384. if (!sbbContext.getRollbackOnly())
  1385. sbbContext.setRollbackOnly();
  1386. throw e;
  1387. }
  1388. }
  1389. if (logger.isFineEnabled())
  1390. logger.fine("app usage resource interdependencies processed");
  1391. // update data source with document
  1392. try {
  1393. String newDocumentString = TextWriter.toString(newDocumentDOM);
  1394. dataSourceSbbInterface.updateElement(documentSelector,
  1395. appUsage.getDefaultDocumentNamespace(), oldDocument,
  1396. newDocumentDOM, newDocumentString, newETag, nodeSelector,
  1397. oldElement, newElement);
  1398. if (logger.isFineEnabled())
  1399. logger.fine("document updated in data source");
  1400. } catch (Exception e) {
  1401. logger.severe(
  1402. "Failed to serialize resulting dom document to string", e);
  1403. throw new InternalServerErrorException(
  1404. "Failed to serialize resulting dom document to string", e);
  1405. }
  1406. return oldElement == null ? new CreatedWriteResult(newETag)
  1407. : new OKWriteResult(newETag);
  1408. }
  1409. private WriteResult putAttribute(
  1410. final org.openxdm.xcap.common.datasource.Document oldDocument,
  1411. final Document newDocumentDOM, final Element newElement,
  1412. final DocumentSelector documentSelector,
  1413. final NodeSelector nodeSelector,
  1414. final ElementSelector elementSelector,
  1415. final AttributeSelector attributeSelector,
  1416. final String newAttributeValue, final AppUsage appUsage,
  1417. boolean processResourceInterdependencies)
  1418. throws InternalServerErrorException, NoParentConflictException,
  1419. SchemaValidationErrorConflictException,
  1420. UniquenessFailureConflictException,
  1421. ConstraintFailureConflictException,
  1422. NotXMLAttributeValueConflictException, BadRequestException,
  1423. CannotInsertConflictException, NotAuthorizedRequestException {
  1424. if (logger.isFineEnabled())
  1425. logger.fine("putting attribute " + attributeSelector
  1426. + " in element " + elementSelector + ", in doc "
  1427. + documentSelector);
  1428. String oldAttributeValue = null;
  1429. if (newElement == null) {
  1430. // throw no parent exception since there is
  1431. // no element but we have a terminal
  1432. // selector
  1433. XPath xpath = DomUtils.XPATH_FACTORY.newXPath();
  1434. xpath.setNamespaceContext(nodeSelector.getNamespaceContext());
  1435. throw new NoParentConflictException(getElementExistentAncestor(
  1436. CONFIGURATION.getXcapRoot(), documentSelector.toString(),
  1437. nodeSelector.getElementSelectorWithEmptyPrefix(),
  1438. newDocumentDOM, xpath));
  1439. }
  1440. // verify if attribute value is
  1441. // valid AttValue
  1442. XMLValidator.checkAttValue(newAttributeValue);
  1443. if (logger.isFineEnabled())
  1444. logger.fine("attr value is valid AttValue");
  1445. // get attribute
  1446. final Attr attribute = newElement.getAttributeNode(attributeSelector
  1447. .getAttName());
  1448. if (attribute != null) {
  1449. // ATTR EXISTS
  1450. if (logger.isFineEnabled())
  1451. logger.fine("attr found in document");
  1452. oldAttributeValue = attribute.getNodeValue();
  1453. // verify if cannot insert,
  1454. // e.g .../x[id1="1"]/@id1
  1455. // and attValue = 2
  1456. ElementSelectorStep lastElementSelectorStep = elementSelector
  1457. .getLastStep();
  1458. if (lastElementSelectorStep instanceof ElementSelectorStepByAttr) {
  1459. ElementSelectorStepByAttr elementSelectorByAttr = (ElementSelectorStepByAttr) lastElementSelectorStep;
  1460. // if this step attr
  1461. // name is the specified
  1462. // attrName and this
  1463. // step attrValue is not
  1464. // the same as the
  1465. // specified attr value,
  1466. // it cannot insert
  1467. if (elementSelectorByAttr.getAttrName().equals(
  1468. attributeSelector.getAttName())
  1469. && !elementSelectorByAttr.getAttrValue().equals(
  1470. newAttributeValue)) {
  1471. if (logger.isFineEnabled())
  1472. logger.fine("element selector's last step attr name is the specified attrName and this step attrValue is not the same as the specified attr value, cannot insert");
  1473. throw new CannotInsertConflictException();
  1474. }
  1475. }
  1476. } else { // ATTR DOES NOT EXISTS
  1477. if (logger.isFineEnabled())
  1478. logger.fine("attr not found in document");
  1479. }
  1480. // set attribute
  1481. newElement.setAttributeNS(null, attributeSelector.getAttName(),
  1482. newAttributeValue);
  1483. if (logger.isFineEnabled())
  1484. logger.fine("attr set");
  1485. // validate the updated document against it's schema
  1486. appUsage.validateSchema(newDocumentDOM);
  1487. if (logger.isFineEnabled())
  1488. logger.fine("document validated by schema");
  1489. // verify app usage constraints
  1490. appUsage.checkConstraintsOnPut(newDocumentDOM,
  1491. CONFIGURATION.getXcapRoot(), documentSelector,
  1492. appUsageDataSource);
  1493. if (logger.isFineEnabled())
  1494. logger.fine("app usage constraints checked");
  1495. // create new document etag
  1496. String newETag = ETagGenerator.generate(documentSelector.toString());
  1497. if (logger.isFineEnabled())
  1498. logger.fine("new document etag generated");
  1499. // process resource interdependencies for the request app usage
  1500. if (processResourceInterdependencies) {
  1501. try {
  1502. appUsage.processResourceInterdependenciesOnPutAttribute(
  1503. oldAttributeValue, newAttributeValue, documentSelector,
  1504. newETag, nodeSelector, elementSelector,
  1505. attributeSelector, this, appUsageDataSource);
  1506. } catch (SchemaValidationErrorConflictException e) {
  1507. if (!sbbContext.getRollbackOnly())
  1508. sbbContext.setRollbackOnly();
  1509. throw e;
  1510. } catch (UniquenessFailureConflictException e) {
  1511. if (!sbbContext.getRollbackOnly())
  1512. sbbContext.setRollbackOnly();
  1513. throw e;
  1514. } catch (InternalServerErrorException e) {
  1515. if (!sbbContext.getRollbackOnly())
  1516. sbbContext.setRollbackOnly();
  1517. throw e;
  1518. } catch (ConstraintFailureConflictException e) {
  1519. if (!sbbContext.getRollbackOnly())
  1520. sbbContext.setRollbackOnly();
  1521. throw e;
  1522. }
  1523. }
  1524. if (logger.isFineEnabled())
  1525. logger.fine("app usage resource interdependencies processed");
  1526. // update data source with document
  1527. try {
  1528. String newDocumentString = TextWriter.toString(newDocumentDOM);
  1529. dataSourceSbbInterface.updateAttribute(documentSelector,
  1530. appUsage.getDefaultDocumentNamespace(), oldDocument,
  1531. newDocumentDOM, newDocumentString, newETag, nodeSelector,
  1532. attributeSelector, oldAttributeValue, newAttributeValue);
  1533. if (logger.isFineEnabled())
  1534. logger.fine("document updated in data source");
  1535. } catch (Exception e) {
  1536. logger.severe(
  1537. "Failed to serialize resulting dom document to string", e);
  1538. throw new InternalServerErrorException(
  1539. "Failed to serialize resulting dom document to string", e);
  1540. }
  1541. return attribute != null ? new OKWriteResult(newETag)
  1542. : new CreatedWriteResult(newETag);
  1543. }
  1544. /*
  1545. * (non-Javadoc)
  1546. *
  1547. * @see
  1548. * org.mobicents.xdm.server.appusage.AppUsageRequestProcessor#deleteDocument
  1549. * (org.openxdm.xcap.common.uri.DocumentSelector,
  1550. * org.mobicents.xdm.server.appusage.AppUsage)
  1551. */
  1552. @Override
  1553. public void deleteDocument(DocumentSelector documentSelector,
  1554. AppUsage appUsage) throws InternalServerErrorException,
  1555. NotFoundException, SchemaValidationErrorConflictException,
  1556. UniquenessFailureConflictException,
  1557. ConstraintFailureConflictException {
  1558. // get document
  1559. org.openxdm.xcap.common.datasource.Document document = dataSourceSbbInterface
  1560. .getDocument(documentSelector);
  1561. if (document == null) {
  1562. // throw exception
  1563. if (logger.isFineEnabled())
  1564. logger.fine("document " + documentSelector + " not found");
  1565. throw new NotFoundException();
  1566. }
  1567. ;
  1568. // document exists
  1569. if (logger.isFineEnabled())
  1570. logger.fine("document " + documentSelector + " found");
  1571. deleteDocument(document, documentSelector, appUsage, false);
  1572. }
  1573. /*
  1574. * (non-Javadoc)
  1575. *
  1576. * @see
  1577. * org.mobicents.xdm.server.appusage.AppUsageRequestProcessor#deleteElement
  1578. * (org.openxdm.xcap.common.uri.DocumentSelector,
  1579. * org.openxdm.xcap.common.uri.NodeSelector,
  1580. * org.openxdm.xcap.common.uri.ElementSelector,
  1581. * org.openxdm.xcap.common.xml.NamespaceContext,
  1582. * org.mobicents.xdm.server.appusage.AppUsage)
  1583. */
  1584. @Override
  1585. public void deleteElement(final DocumentSelector documentSelector,
  1586. final NodeSelector nodeSelector,
  1587. final ElementSelector elementSelector, AppUsage appUsage)
  1588. throws InternalServerErrorException,
  1589. UniquenessFailureConflictException,
  1590. ConstraintFailureConflictException, NotFoundException,
  1591. CannotDeleteConflictException,
  1592. SchemaValidationErrorConflictException, BadRequestException {
  1593. // get document
  1594. final org.openxdm.xcap.common.datasource.Document oldDocument = dataSourceSbbInterface
  1595. .getDocument(documentSelector);
  1596. if (oldDocument == null) {
  1597. // throw exception
  1598. if (logger.isFineEnabled())
  1599. logger.fine("document not found");
  1600. throw new NotFoundException();
  1601. }
  1602. // clone doc
  1603. final Document newDocumentDOM = DocumentCloner.clone(oldDocument
  1604. .getAsDOMDocument());
  1605. // get element
  1606. final Element newElement = getElementForDeleteOrGet(newDocumentDOM,
  1607. nodeSelector, false);
  1608. deleteElement(oldDocument, newDocumentDOM, newElement,
  1609. documentSelector, nodeSelector, elementSelector, appUsage,
  1610. false);
  1611. }
  1612. /*
  1613. * (non-Javadoc)
  1614. *
  1615. * @see
  1616. * org.mobicents.xdm.server.appusage.AppUsageRequestProcessor#deleteAttribute
  1617. * (org.openxdm.xcap.common.uri.DocumentSelector,
  1618. * org.openxdm.xcap.common.uri.NodeSelector,
  1619. * org.openxdm.xcap.common.uri.ElementSelector,
  1620. * org.openxdm.xcap.common.uri.AttributeSelector,
  1621. * org.mobicents.xdm.server.appusage.AppUsage)
  1622. */
  1623. @Override
  1624. public void deleteAttribute(final DocumentSelector documentSelector,
  1625. final NodeSelector nodeSelector,
  1626. final ElementSelector elementSelector,
  1627. final AttributeSelector attributeSelector, final AppUsage appUsage)
  1628. throws InternalServerErrorException, BadRequestException,
  1629. NotFoundException, SchemaValidationErrorConflictException,
  1630. UniquenessFailureConflictException,
  1631. ConstraintFailureConflictException {
  1632. // get document
  1633. final org.openxdm.xcap.common.datasource.Document oldDocument = dataSourceSbbInterface
  1634. .getDocument(documentSelector);
  1635. if (oldDocument == null) {
  1636. // throw exception
  1637. if (logger.isFineEnabled())
  1638. logger.fine("document not found");
  1639. throw new NotFoundException();
  1640. }
  1641. // clone doc
  1642. final Document newDocumentDOM = DocumentCloner.clone(oldDocument
  1643. .getAsDOMDocument());
  1644. // get element
  1645. final Element newElement = getElementForDeleteOrGet(newDocumentDOM,
  1646. nodeSelector, false);
  1647. deleteAttribute(oldDocument, newDocumentDOM, newElement,
  1648. documentSelector, nodeSelector, elementSelector,
  1649. attributeSelector, appUsage, false);
  1650. }
  1651. /*
  1652. * (non-Javadoc)
  1653. *
  1654. * @see
  1655. * org.mobicents.xdm.server.appusage.AppUsageRequestProcessor#putDocument
  1656. * (org.openxdm.xcap.common.uri.DocumentSelector, org.w3c.dom.Document,
  1657. * org.mobicents.xdm.server.appusage.AppUsage)
  1658. */
  1659. @Override
  1660. public boolean putDocument(DocumentSelector documentSelector,
  1661. Document newDomDocument, AppUsage appUsage)
  1662. throws InternalServerErrorException, NoParentConflictException,
  1663. SchemaValidationErrorConflictException,
  1664. UniquenessFailureConflictException,
  1665. ConstraintFailureConflictException, ConflictException,
  1666. MethodNotAllowedException, UnsupportedMediaTypeException,
  1667. PreconditionFailedException, BadRequestException,
  1668. NotAuthorizedRequestException {
  1669. // try to get document's resource
  1670. org.openxdm.xcap.common.datasource.Document oldDocument = dataSourceSbbInterface
  1671. .getDocument(documentSelector);
  1672. WriteResult result = putDocument(documentSelector, oldDocument,
  1673. newDomDocument, appUsage, false);
  1674. return result.getResponseStatus() == 201;
  1675. }
  1676. /*
  1677. * (non-Javadoc)
  1678. *
  1679. * @see
  1680. * org.mobicents.xdm.server.appusage.AppUsageRequestProcessor#putElement
  1681. * (org.openxdm.xcap.common.uri.DocumentSelector,
  1682. * org.openxdm.xcap.common.uri.NodeSelector,
  1683. * org.openxdm.xcap.common.uri.ElementSelector,
  1684. * org.openxdm.xcap.common.xml.NamespaceContext, org.w3c.dom.Element,
  1685. * org.mobicents.xdm.server.appusage.AppUsage)
  1686. */
  1687. @Override
  1688. public boolean putElement(final DocumentSelector documentSelector,
  1689. final NodeSelector nodeSelector,
  1690. final ElementSelector elementSelector, Element newElement,
  1691. final AppUsage appUsage) throws InternalServerErrorException,
  1692. NoParentConflictException, SchemaValidationErrorConflictException,
  1693. UniquenessFailureConflictException,
  1694. ConstraintFailureConflictException, CannotInsertConflictException,
  1695. NotValidXMLFragmentConflictException, NotUTF8ConflictException,
  1696. BadRequestException, NotAuthorizedRequestException {
  1697. // get doc
  1698. org.openxdm.xcap.common.datasource.Document oldDocument = dataSourceSbbInterface
  1699. .getDocument(documentSelector);
  1700. if (oldDocument == null) {
  1701. // doc does not exists, throw exception
  1702. throw new NoParentConflictException(CONFIGURATION.getXcapRoot()
  1703. + "/" + documentSelector.getCollection());
  1704. }
  1705. // clone doc
  1706. final Document newDocumentDOM = DocumentCloner.clone(oldDocument
  1707. .getAsDOMDocument());
  1708. // get old element
  1709. final Element oldElement = getElementForPut(documentSelector,
  1710. newDocumentDOM, nodeSelector, false);
  1711. // put element
  1712. WriteResult result = putElement(oldDocument, newDocumentDOM,
  1713. oldElement, documentSelector, nodeSelector, elementSelector,
  1714. newElement, appUsage, false);
  1715. return result.getResponseStatus() == 201;
  1716. }
  1717. @Override
  1718. public boolean putAttribute(DocumentSelector documentSelector,
  1719. NodeSelector nodeSelector, ElementSelector elementSelector,
  1720. AttributeSelector attributeSelector, String attrValue,
  1721. AppUsage appUsage) throws InternalServerErrorException,
  1722. NoParentConflictException, SchemaValidationErrorConflictException,
  1723. UniquenessFailureConflictException,
  1724. ConstraintFailureConflictException,
  1725. NotXMLAttributeValueConflictException, BadRequestException,
  1726. CannotInsertConflictException, NotAuthorizedRequestException {
  1727. // get doc
  1728. org.openxdm.xcap.common.datasource.Document oldDocument = dataSourceSbbInterface
  1729. .getDocument(documentSelector);
  1730. if (oldDocument == null) {
  1731. // doc does not exists, throw exception
  1732. throw new NoParentConflictException(CONFIGURATION.getXcapRoot()
  1733. + "/" + documentSelector.getCollection());
  1734. }
  1735. // clone doc
  1736. final Document newDocumentDOM = DocumentCloner.clone(oldDocument
  1737. .getAsDOMDocument());
  1738. // get old element
  1739. final Element newElement = getElementForPut(documentSelector,
  1740. newDocumentDOM, nodeSelector, false);
  1741. // put element
  1742. WriteResult result = putAttribute(oldDocument, newDocumentDOM,
  1743. newElement, documentSelector, nodeSelector, elementSelector,
  1744. attributeSelector, attrValue, appUsage, false);
  1745. return result.getResponseStatus() == 201;
  1746. }
  1747. // AUX
  1748. private String getElementExistentAncestor(String xcapRoot,
  1749. String documentSelector, String elementSelectorWithEmptyPrefix,
  1750. Document document, XPath xpath) {
  1751. // first part is the xcap uri that points to the document
  1752. StringBuilder sb = new StringBuilder(xcapRoot).append(documentSelector);
  1753. // loop till we find an existing ancestor
  1754. String elementAncestor = null;
  1755. int index = -1;
  1756. while ((index = elementSelectorWithEmptyPrefix.lastIndexOf('/')) > 0) {
  1757. elementSelectorWithEmptyPrefix = elementSelectorWithEmptyPrefix
  1758. .substring(0, index);
  1759. try {
  1760. Element element = (Element) xpath.evaluate(
  1761. elementSelectorWithEmptyPrefix, document,
  1762. XPathConstants.NODE);
  1763. if (element != null) {
  1764. elementAncestor = elementSelectorWithEmptyPrefix;
  1765. break;
  1766. }
  1767. } catch (XPathExpressionException e) {
  1768. // silently ignore an invalid xpath expression, specs requires
  1769. // it
  1770. }
  1771. }
  1772. if (elementAncestor != null) {
  1773. // existing element ancestor found
  1774. // remove empty prefixes if those exist
  1775. elementAncestor = elementAncestor.replaceAll("/:", "/");
  1776. // and add it to the ancestor
  1777. sb.append("/~~").append(elementAncestor);
  1778. }
  1779. String ancestor = sb.toString();
  1780. if (logger.isFineEnabled())
  1781. logger.fine("existing ancestor is " + ancestor);
  1782. return ancestor;
  1783. }
  1784. private Element getElement(Document domDocument,
  1785. String elementSelectorWithEmptyPrefixes,
  1786. NamespaceContext namespaceContext) throws IllegalArgumentException {
  1787. if (logger.isFineEnabled())
  1788. logger.fine("retrieving element "
  1789. + elementSelectorWithEmptyPrefixes);
  1790. // lets use xpath
  1791. final XPath xpath = DomUtils.XPATH_FACTORY.newXPath();
  1792. // set context to resolve namespace bindings
  1793. xpath.setNamespaceContext(namespaceContext);
  1794. try {
  1795. // exec query to get element
  1796. final NodeList elementNodeList = (NodeList) xpath.evaluate(
  1797. elementSelectorWithEmptyPrefixes, domDocument,
  1798. XPathConstants.NODESET);
  1799. if (elementNodeList.getLength() == 1) {
  1800. if (logger.isFineEnabled())
  1801. logger.fine("element " + elementSelectorWithEmptyPrefixes
  1802. + " found");
  1803. return (Element) elementNodeList.item(0);
  1804. } else if (elementNodeList.getLength() == 0) {
  1805. if (logger.isFineEnabled()) {
  1806. logger.fine("element " + elementSelectorWithEmptyPrefixes
  1807. + " not found");
  1808. }
  1809. return null;
  1810. } else {
  1811. if (logger.isFineEnabled()) {
  1812. logger.fine("multiple elements match "
  1813. + elementSelectorWithEmptyPrefixes);
  1814. }
  1815. throw new IllegalArgumentException("multiple elements match "
  1816. + elementSelectorWithEmptyPrefixes);
  1817. }
  1818. } catch (XPathExpressionException e) {
  1819. // error in xpath expression
  1820. if (logger.isFineEnabled())
  1821. logger.fine("unable to retrieve element "
  1822. + elementSelectorWithEmptyPrefixes
  1823. + " error in xpath expression", e);
  1824. throw new IllegalArgumentException("unable to retrieve element "
  1825. + elementSelectorWithEmptyPrefixes
  1826. + " error in xpath expression", e);
  1827. }
  1828. }
  1829. private Element getElementForDeleteOrGet(Document document,
  1830. NodeSelector nodeSelector, boolean parent)
  1831. throws BadRequestException, NotFoundException {
  1832. String elementSelectorWithEmptyPrefix = parent ? nodeSelector
  1833. .getElementParentSelectorWithEmptyPrefix() : nodeSelector
  1834. .getElementSelectorWithEmptyPrefix();
  1835. // get element
  1836. Element element = null;
  1837. try {
  1838. element = getElement(document, elementSelectorWithEmptyPrefix,
  1839. nodeSelector.getNamespaceContext());
  1840. } catch (IllegalArgumentException e) {
  1841. if (nodeSelector.elementSelectorHasUnbindedPrefixes()) {
  1842. // element selector has unbinded prefixe(s)
  1843. if (logger.isFineEnabled())
  1844. logger.fine("element selector doesn't have prefixe(s) bound, bad request");
  1845. throw new BadRequestException();
  1846. } else {
  1847. // nothing wrong with prefixes, return not found
  1848. // exception
  1849. if (logger.isFineEnabled())
  1850. logger.fine("element not found");
  1851. throw new NotFoundException();
  1852. }
  1853. }
  1854. if (element == null) {
  1855. if (logger.isFineEnabled())
  1856. logger.fine("element not found");
  1857. throw new NotFoundException();
  1858. }
  1859. return element;
  1860. }
  1861. private Element getElementForPut(DocumentSelector documentSelector,
  1862. Document document, NodeSelector nodeSelector, boolean parent)
  1863. throws BadRequestException, NoParentConflictException {
  1864. String elementSelectorWithEmptyPrefix = parent ? nodeSelector
  1865. .getElementParentSelectorWithEmptyPrefix() : nodeSelector
  1866. .getElementSelectorWithEmptyPrefix();
  1867. // get element
  1868. try {
  1869. return getElement(document, elementSelectorWithEmptyPrefix,
  1870. nodeSelector.getNamespaceContext());
  1871. } catch (IllegalArgumentException e) {
  1872. if (nodeSelector.elementSelectorHasUnbindedPrefixes()) {
  1873. if (logger.isFineEnabled())
  1874. logger.fine("element selector doesn't have prefixe(s) bound, bad request");
  1875. throw new BadRequestException();
  1876. } else {
  1877. XPath xpath = DomUtils.XPATH_FACTORY.newXPath();
  1878. xpath.setNamespaceContext(nodeSelector.getNamespaceContext());
  1879. throw new NoParentConflictException(getElementExistentAncestor(
  1880. CONFIGURATION.getXcapRoot(),
  1881. documentSelector.toString(),
  1882. elementSelectorWithEmptyPrefix, document, xpath));
  1883. }
  1884. }
  1885. }
  1886. }