PageRenderTime 42ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 0ms

/servers/diameter/core/jdiameter/impl/src/main/java/org/jdiameter/common/impl/validation/DictionaryImpl.java

http://mobicents.googlecode.com/
Java | 790 lines | 536 code | 126 blank | 128 comment | 111 complexity | 3cc9ce2efe3126de55132a689c8a062d 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 2010, 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.jdiameter.common.impl.validation;
  23. import java.io.File;
  24. import java.io.FileInputStream;
  25. import java.io.FileNotFoundException;
  26. import java.io.IOException;
  27. import java.io.InputStream;
  28. import java.util.ArrayList;
  29. import java.util.Comparator;
  30. import java.util.HashMap;
  31. import java.util.List;
  32. import java.util.Map;
  33. import java.util.TreeMap;
  34. import javax.xml.parsers.DocumentBuilder;
  35. import javax.xml.parsers.DocumentBuilderFactory;
  36. import org.jdiameter.api.Message;
  37. import org.jdiameter.api.validation.AvpNotAllowedException;
  38. import org.jdiameter.api.validation.AvpRepresentation;
  39. import org.jdiameter.api.validation.Dictionary;
  40. import org.jdiameter.api.validation.MessageRepresentation;
  41. import org.jdiameter.api.validation.ValidatorLevel;
  42. import org.jdiameter.client.impl.DictionarySingleton;
  43. import org.slf4j.Logger;
  44. import org.slf4j.LoggerFactory;
  45. import org.w3c.dom.Document;
  46. import org.w3c.dom.Element;
  47. import org.w3c.dom.Node;
  48. import org.w3c.dom.NodeList;
  49. /**
  50. * Implementation of {@link Dictionary} interface.
  51. *
  52. * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
  53. * @author <a href="mailto:brainslog@gmail.com"> Alexandre Mendonca </a>
  54. * @since 1.5.4.0-build404
  55. */
  56. public class DictionaryImpl implements Dictionary {
  57. private static transient Logger logger = LoggerFactory.getLogger(DictionaryImpl.class);
  58. public static final Dictionary INSTANCE = new DictionaryImpl("dictionary.xml");
  59. private static final String UNDEFINED_AVP_TYPE = "UNDEFINED";
  60. private static final String AVP_DEFAULT_INDEX = "-1";
  61. private static final String AVP_DEFAULT_MULTIPLICITY = AvpRepresentation._MP_ZERO_OR_MORE;
  62. public static final String _AVP_ATTRIBUTE_NAME = "name";
  63. public static final String _AVP_ATTRIBUTE_CODE = "code";
  64. public static final String _AVP_ATTRIBUTE_VENDOR = "vendor";
  65. public static final String _AVP_ATTRIBUTE_MULTIPLICITY = "multiplicity";
  66. public static final String _AVP_ATTRIBUTE_INDEX = "index";
  67. private Map<AvpRepresentation, AvpRepresentation> avpMap = new HashMap<AvpRepresentation, AvpRepresentation>();
  68. private Map<String, AvpRepresentation> avpByNameMap = new HashMap<String, AvpRepresentation>();
  69. private Map<String, String> vendorMap = new HashMap<String, String>();
  70. private Map<MessageRepresentation, MessageRepresentation> commandMap = new HashMap<MessageRepresentation, MessageRepresentation>();
  71. private Map<String, String> typedefMap = new HashMap<String, String>();
  72. private boolean configured = false;
  73. private DictionaryImpl(String confFile) {
  74. this.init(confFile);
  75. }
  76. private void init(String confFile) {
  77. InputStream is = null;
  78. try {
  79. is = DictionarySingleton.class.getResourceAsStream(confFile);
  80. if(is == null) {
  81. logger.debug("Failed to locate dictionary configuration file: {}, in class classloader. Trying thread context class loader.", confFile);
  82. is = Thread.currentThread().getContextClassLoader().getResourceAsStream(confFile);
  83. }
  84. if(is == null) {
  85. logger.debug("Failed to locate dictionary configuration file: {}, in thread context class loader. Trying using 'config/' prefix.", confFile);
  86. is = Thread.currentThread().getContextClassLoader().getResourceAsStream("config/" + confFile);
  87. }
  88. if(is == null) {
  89. logger.debug("Failed to locate dictionary configuration file: {}, in thread context class loader. Trying regular file.", confFile);
  90. File fDict = new File(confFile);
  91. if(fDict.exists()) {
  92. is = new FileInputStream(fDict);
  93. }
  94. else {
  95. logger.debug("Failed to locate dictionary configuration file: {}, from regular file. Trying using 'config/' prefix.", confFile);
  96. fDict = new File("config/" + confFile);
  97. if(fDict.exists()) {
  98. is = new FileInputStream(fDict);
  99. }
  100. }
  101. }
  102. if(is != null) {
  103. this.configure(is);
  104. }
  105. else {
  106. this.setEnabled(false);
  107. logger.warn("Failed to initialize and configure Diameter Dictionary since configuration file was not found. Validator is disabled.");
  108. }
  109. }
  110. catch(FileNotFoundException fnfe) {
  111. logger.debug("Could not load configuration file: {}, from any known location.", confFile);
  112. }
  113. finally {
  114. if(is != null) {
  115. try {
  116. is.close();
  117. }
  118. catch (IOException e) {
  119. logger.error("", e);
  120. }
  121. }
  122. }
  123. }
  124. // Parser functions ---------------------------------------------------------
  125. /*
  126. * (non-Javadoc)
  127. *
  128. * @see org.jdiameter.api.validation.Dictionary#configure(java.io.InputStream)
  129. */
  130. public void configure(InputStream is) {
  131. if (is == null) {
  132. logger.error("No input stream to configure dictionary from?");
  133. return;
  134. }
  135. try {
  136. long startTime = System.currentTimeMillis();
  137. this.avpByNameMap = new TreeMap<String, AvpRepresentation>(new Comparator<String>() {
  138. public int compare(String o1, String o2) {
  139. return (o1 == null) ? 1 : (o2 == null) ? -1 : o1.compareTo(o2);
  140. }
  141. });
  142. this.vendorMap = new HashMap<String, String>();
  143. this.typedefMap = new HashMap<String, String>();
  144. this.avpMap = new HashMap<AvpRepresentation, AvpRepresentation>();
  145. this.commandMap = new HashMap<MessageRepresentation, MessageRepresentation>();
  146. DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
  147. dbf.setValidating(false);
  148. DocumentBuilder db = dbf.newDocumentBuilder();
  149. Document doc = db.parse(is);
  150. doc.getDocumentElement().normalize();
  151. this.parseVendors(doc);
  152. this.parseTypeDefs(doc);
  153. this.parseAvps(doc);
  154. this.parseCommands(doc);
  155. this.configured = true;
  156. long endTime = System.currentTimeMillis();
  157. if(logger.isInfoEnabled()) {
  158. logger.info("Mobicents Diameter Dictionary loaded in {}ms -- Vendors[{}] Commands[{}] Types[{}] AVPs[{}]",
  159. new Object[] { (endTime - startTime), vendorMap.size(), commandMap.size(), typedefMap.size(), avpMap.size() });
  160. }
  161. if (logger.isInfoEnabled()) {
  162. StringBuffer sb = new StringBuffer();
  163. int c = 0;
  164. for (AvpRepresentation key : this.avpMap.keySet()) {
  165. if (this.avpMap.get(key).isWeak()) {
  166. c++;
  167. sb.append("---------------------------------\n").append("Found incomplete AVP definition:\n").append(this.avpMap.get(key)).append("\n");
  168. }
  169. }
  170. if (c > 0) {
  171. sb.append("------- TOTAL INCOMPLETE AVPS COUNT: ").append(c).append(" -------");
  172. logger.info(sb.toString());
  173. }
  174. }
  175. }
  176. catch (Exception e) {
  177. this.enabled = false;
  178. this.configured = false;
  179. logger.error("Failed to parse validator configuration. Validator disabled.", e);
  180. }
  181. finally {
  182. // close?
  183. try {
  184. is.close();
  185. }
  186. catch (IOException e) {
  187. logger.debug("Failed to close InputStream for Dictionary XML.", e);
  188. }
  189. }
  190. }
  191. /**
  192. * Parses the <vendor /> attributes from a Dictionary XML Document
  193. *
  194. * @param doc the DOM object representing the XML Document with the Dictionary definitions
  195. */
  196. protected void parseVendors(Document doc) {
  197. // Parse vendors, we will need those.
  198. // Format: <vendor vendor-id="TGPP" code="10415" name="3GPP" />
  199. NodeList vendorNodes = doc.getElementsByTagName("vendor");
  200. for (int v = 0; v < vendorNodes.getLength(); v++) {
  201. Node vendorNode = vendorNodes.item(v);
  202. if (vendorNode.getNodeType() == Node.ELEMENT_NODE) {
  203. Element vendorElement = (Element) vendorNode;
  204. // Get the Code (number) and ID (string)
  205. String vendorCode = vendorElement.getAttribute("code");
  206. String vendorId = vendorElement.getAttribute("vendor-id");
  207. vendorMap.put(vendorId, vendorCode);
  208. }
  209. }
  210. }
  211. /**
  212. * Parses the <typedefn /> attributes from a Dictionary XML Document
  213. *
  214. * @param doc the DOM object representing the XML Document with the Dictionary definitions
  215. */
  216. protected void parseTypeDefs(Document doc) {
  217. // Parse type definitions. Handy to match against defined AVP types
  218. // and to fill AVPs with generic function.
  219. // Format: <typedefn type-name="Integer32" />
  220. // <typedefn type-name="Enumerated" type-parent="Integer32" />
  221. NodeList typedefNodes = doc.getElementsByTagName("typedefn");
  222. for (int td = 0; td < typedefNodes.getLength(); td++) {
  223. Node typedefNode = typedefNodes.item(td);
  224. if (typedefNode.getNodeType() == Node.ELEMENT_NODE) {
  225. Element typedefElement = (Element) typedefNode;
  226. String typeName = typedefElement.getAttribute("type-name");
  227. String typeParent = typedefElement.getAttribute("type-parent");
  228. // UTF8String and Time are special situations, we don't want to convert these.
  229. if (typeParent == null || typeParent.equals("") || typeName.equals("UTF8String") || typeName.equals("Time")) {
  230. typeParent = typeName;
  231. }
  232. typedefMap.put(typeName, typeParent);
  233. }
  234. }
  235. }
  236. /**
  237. * Parses the <typedefn /> attributes from a Dictionary XML Document
  238. *
  239. * @param doc the DOM object representing the XML Document with the Dictionary definitions
  240. */
  241. protected void parseAvps(Document doc) {
  242. // Format: <avpdefn name="Talk-Burst-Volume" code="1256" vendor-id="TGPP" mandatory="must" protected="may" may-encrypt="true" vendor-bit="must" >
  243. // <type type-name="Unsigned32" />
  244. // </avpdefn>
  245. NodeList avpDefnNodes = doc.getElementsByTagName("avpdefn");
  246. for (int i = 0; i < avpDefnNodes.getLength(); i++) {
  247. Node avpNode = avpDefnNodes.item(i);
  248. Element avpDefnElement = (Element) avpNode;
  249. String avpName = avpDefnElement.getAttribute("name");
  250. String avpCode = avpDefnElement.getAttribute("code");
  251. String avpVendorId = avpDefnElement.getAttribute("vendor-id");
  252. String avpMandatory = avpDefnElement.getAttribute("mandatory");
  253. String avpProtected = avpDefnElement.getAttribute("protected").equals("") ? "may" : avpDefnElement.getAttribute("protected");
  254. String avpMayEncrypt = avpDefnElement.getAttribute("may-encrypt");
  255. String avpVendorBit = avpDefnElement.getAttribute("vendor-bit");
  256. long vendorCode = getVendorCode(avpVendorId);
  257. // Let's figure out the type
  258. // It can be:
  259. // <type type-name="UTF8String" />
  260. // OR
  261. // <grouped> <avp name="PoC-Change-Time" multiplicity="1" /> ... </grouped>
  262. // OR
  263. // <type type-name="Enumerated"> <enum code="0" name="MULTICAST" /> ... </enumerated>
  264. String avpType = UNDEFINED_AVP_TYPE;
  265. List<AvpRepresentation> groupedAvpChilds = new ArrayList<AvpRepresentation>();
  266. NodeList avpDefnChildNodes = avpNode.getChildNodes();
  267. for (int j = 0; j < avpDefnChildNodes.getLength(); j++) {
  268. Node avpDefnChildNode = avpDefnChildNodes.item(j);
  269. if (avpDefnChildNode.getNodeType() == Node.ELEMENT_NODE) {
  270. Element avpDefnChildElement = (Element) avpDefnChildNode;
  271. if (avpDefnChildElement.getNodeName().equals("grouped")) {
  272. avpType = "Grouped";
  273. // Let's fetch the childs
  274. // Format: <avp name="PoC-Change-Time" multiplicity="1" />
  275. NodeList groupedAvpMembers = avpDefnChildElement.getChildNodes();
  276. for (int gChildIndex = 0; gChildIndex < groupedAvpMembers.getLength(); gChildIndex++) {
  277. Node groupedAvpChildNode = groupedAvpMembers.item(gChildIndex);
  278. if (groupedAvpChildNode.getNodeType() == Node.ELEMENT_NODE) {
  279. Element groupedAvpChildElement = (Element) groupedAvpChildNode;
  280. String childName = null;
  281. String childMultiplicity = AVP_DEFAULT_MULTIPLICITY;
  282. String childIndexIndicator = AVP_DEFAULT_INDEX;
  283. if (!groupedAvpChildElement.hasAttribute("name")) {
  284. if (logger.isDebugEnabled()) {
  285. logger.debug(new StringBuffer("[ERROR] Grouped child does not have name, grouped avp: Name[").append(avpName).append("] Description[").
  286. append("").append("] Code[").append(avpCode).append("] May-Encrypt[").append(avpMayEncrypt).append("] Mandatory[").
  287. append(avpMandatory).append("] Protected [").append(avpProtected).append("] Vendor-Bit [").append(avpVendorBit).append("] Vendor-Id [").
  288. append(avpVendorId).append("] Constrained[").append("").append("] Type [").append(avpType).append("]").toString());
  289. }
  290. continue;
  291. }
  292. else {
  293. childName = groupedAvpChildElement.getAttribute("name");
  294. }
  295. childMultiplicity = groupedAvpChildElement.hasAttribute("multiplicity") ?
  296. groupedAvpChildElement.getAttribute("multiplicity") : AvpRepresentation._MP_ZERO_OR_MORE;
  297. childIndexIndicator = groupedAvpChildElement.hasAttribute("index") ?
  298. groupedAvpChildElement.getAttribute("index") : "-1";
  299. // have we parsed this child definition already?
  300. AvpRepresentation childRep = this.avpByNameMap.get(childName);
  301. AvpRepresentationImpl child = null;
  302. if(childRep != null) {
  303. try {
  304. child = (AvpRepresentationImpl) childRep.clone();
  305. }
  306. catch (CloneNotSupportedException cnse) {
  307. // It should not happen, but anyway
  308. if(logger.isWarnEnabled()) {
  309. logger.warn("Unable to clone AVP " + childRep, cnse);
  310. }
  311. }
  312. }
  313. else {
  314. child = new AvpRepresentationImpl(childName, vendorCode);
  315. child.markWeak(true);
  316. }
  317. child.setMultiplicityIndicator(childMultiplicity);
  318. child.markFixPosition(Integer.valueOf(childIndexIndicator));
  319. groupedAvpChilds.add(child);
  320. }
  321. }
  322. }
  323. else if (avpDefnChildElement.getNodeName().equals("type")) {
  324. avpType = avpDefnChildElement.getAttribute("type-name");
  325. //FIXME: baranowb: why this is like that? This changes type of AVP to primitive ONE..? Checks against type dont make sense, ie to check for Address type...
  326. avpType = typedefMap.get(avpType);
  327. if(avpType == null) {
  328. logger.warn("Unknown AVP Type ({}) for AVP with code {} and vendor-id {} ",
  329. new Object[] { avpDefnChildElement.getAttribute("type-name"), avpCode, avpVendorId});
  330. }
  331. }
  332. else {
  333. logger.warn("Unknown AVP Definition child element for AVP with code {} and vendor-id {} ", avpCode, avpVendorId);
  334. }
  335. }
  336. }
  337. try {
  338. AvpRepresentationImpl avp = null;
  339. avp = new AvpRepresentationImpl(avpName, "N/A", Integer.valueOf(avpCode), avpMayEncrypt.equals("yes"), avpMandatory,
  340. avpProtected, avpVendorBit, vendorCode, avpType);
  341. if (avp.isGrouped()) {
  342. avp.setChildren(groupedAvpChilds);
  343. // we are not strong enough, children are referenced ONLY by name, so we are
  344. // weak until all children can be resolved to strong representation
  345. avp.markWeak(true);
  346. }
  347. resolveWeakLinks(avp);
  348. AvpRepresentation existingAvp = null;
  349. if((existingAvp = avpMap.get(avp)) != null) {
  350. logger.warn("Duplicated AVP Definition for AVP Code: {}, Vendor-Id: {}. See TRACE logs for definitions.", avp.getCode(), avp.getVendorId());
  351. logger.trace("Existing AVP:\r\n {}\r\n New AVP:\r\n {}", existingAvp, avp);
  352. }
  353. else {
  354. avpMap.put(avp, avp);
  355. }
  356. AvpRepresentation oldAvp = avpByNameMap.put(avp.getName(), avp);
  357. if(oldAvp != null) {
  358. logger.debug("[WARN] Overwrited definition of AVP with the same name: Old: {}, New: {}", new Object[] { oldAvp, avp });
  359. }
  360. }
  361. catch (Exception e) {
  362. if (logger.isDebugEnabled()) {
  363. logger.debug(new StringBuffer("[ERROR] Failed Parsing AVP: Name[").append(avpName).append("] Description[").append("N/A").append("] Code[").append(avpCode).append("] May-Encrypt[").append(avpMayEncrypt).append("] Mandatory[").append(avpMandatory).append("] Protected [").append(avpProtected).append("] Vendor-Bit [").append(avpVendorBit).append("] Vendor-Id [").append(avpVendorId).append("] Constrained[").append("N/A").append("] Type [").append(avpType).append("]").toString(), e);
  364. }
  365. }
  366. }
  367. for(AvpRepresentation rep : avpMap.values()) {
  368. markWeaks((AvpRepresentationImpl) rep);
  369. }
  370. }
  371. private boolean markWeaks(AvpRepresentationImpl rep) {
  372. if(rep.isGrouped()) {
  373. boolean isWeak = false;
  374. for(AvpRepresentation repC : rep.getChildren()) {
  375. if(markWeaks((AvpRepresentationImpl) repC)) {
  376. isWeak = true;
  377. }
  378. }
  379. rep.markWeak(isWeak);
  380. }
  381. else {
  382. rep.markWeak(rep.getCode() == -1);
  383. }
  384. return rep.isWeak();
  385. }
  386. /**
  387. * For a given AVP resolves the weak links (where AVP definition in grouped
  388. * AVPs is not yet known, and only added by Name)
  389. *
  390. * @param newAvp the AVP which was just defined
  391. */
  392. private void resolveWeakLinks(AvpRepresentation newAvp) {
  393. for(AvpRepresentation avp : avpMap.values()) {
  394. if(avp.isGrouped()) {
  395. if(avp.getName().equals(newAvp.getName())) {
  396. continue;
  397. }
  398. List<AvpRepresentation> avpChilds = avp.getChildren();
  399. for(int n = 0; n < avpChilds.size(); n++) {
  400. AvpRepresentation avpChild = avpChilds.get(n);
  401. if(avpChild.getName().equals(newAvp.getName())) {
  402. try {
  403. AvpRepresentationImpl strongAvp = (AvpRepresentationImpl) newAvp.clone();
  404. strongAvp.setMultiplicityIndicator(avpChild.getMultiplicityIndicator());
  405. strongAvp.markFixPosition(avpChild.getPositionIndex());
  406. strongAvp.markWeak(false);
  407. avpChilds.set(n, strongAvp);
  408. resolveWeakLinks(avp);
  409. }
  410. catch (CloneNotSupportedException cnse) {
  411. // It should not happen, but anyway
  412. if(logger.isWarnEnabled()) {
  413. logger.warn("Unable to clone AVP " + newAvp, cnse);
  414. }
  415. }
  416. }
  417. }
  418. }
  419. }
  420. }
  421. /**
  422. * @param doc
  423. * @param nameToCode
  424. * @param avpMap
  425. * @return
  426. */
  427. private void parseCommands(Document doc) {
  428. // here all grouped AVPs should have proper filling.
  429. // now lets go through message definition, we have to respect application nodes
  430. NodeList applicationNodes = doc.getElementsByTagName("application");
  431. // Map<MessageRepresentation, MessageRepresentation> commandMap = new
  432. // HashMap<MessageRepresentation, MessageRepresentation>();
  433. for (int applicationIndex = 0; applicationIndex < applicationNodes.getLength(); applicationIndex++) {
  434. if (applicationNodes.item(applicationIndex).getNodeType() == Node.ELEMENT_NODE) {
  435. Element applicationElement = (Element) applicationNodes.item(applicationIndex);
  436. if (!applicationElement.hasAttribute("id")) {
  437. logger.debug("[ERROR] Application definition does not have ID, skipping message");
  438. continue;
  439. }
  440. long applicationCode = Long.valueOf(applicationElement.getAttribute("id"));
  441. NodeList commandNodes = applicationElement.getElementsByTagName("command");
  442. for (int c = 0; c < commandNodes.getLength(); c++) {
  443. Node commandNode = commandNodes.item(c);
  444. if (commandNode.getNodeType() == Node.ELEMENT_NODE) {
  445. Element commandElement = (Element) commandNode;
  446. if (!commandElement.hasAttribute("request")) {
  447. logger.debug("[ERROR] Command for application: {} does not define if its request or answer, skipping.", applicationCode);
  448. continue;
  449. }
  450. String commandName = commandElement.getAttribute("name");
  451. String commandCode = commandElement.getAttribute("code");
  452. String isRequest = commandElement.getAttribute("request");
  453. MessageRepresentationImpl msg = new MessageRepresentationImpl(Integer.valueOf(commandCode), applicationCode,
  454. Boolean.parseBoolean(isRequest), commandName);
  455. Map<AvpRepresentation, AvpRepresentation> commandAvpList = new HashMap<AvpRepresentation, AvpRepresentation>();
  456. commandMap.put(msg, msg);
  457. // now we have to process avp defs for this message :)
  458. NodeList commandAvpsList = commandElement.getElementsByTagName("avp");
  459. for (int commandAvpIndex = 0; commandAvpIndex < commandAvpsList.getLength(); commandAvpIndex++) {
  460. if (commandAvpsList.item(commandAvpIndex).getNodeType() == Node.ELEMENT_NODE) {
  461. Element commandAvpElement = (Element) commandAvpsList.item(commandAvpIndex);
  462. String multiplicity = null;
  463. String name = null;
  464. String index = null;
  465. if (!commandAvpElement.hasAttribute("name")) {
  466. logger.debug("[ERROR] Command defines avp without name! Command: {}, Code: {}, ApplicationID: {}",
  467. new Object[] { msg.getName(), msg.getCommandCode(), msg.getApplicationId() });
  468. continue;
  469. }
  470. else {
  471. name = commandAvpElement.getAttribute("name");
  472. }
  473. if (!commandAvpElement.hasAttribute("multiplicity")) {
  474. logger.debug("[WARN] Command defines avp without multiplicity.");
  475. multiplicity = AvpRepresentation._MP_ZERO_OR_MORE;
  476. }
  477. else {
  478. multiplicity = commandAvpElement.getAttribute("multiplicity");
  479. }
  480. index = commandAvpElement.hasAttribute("index") ? commandAvpElement.getAttribute("index") : "-1";
  481. String avpCode = commandAvpElement.getAttribute("code");
  482. String avpVendor = commandAvpElement.getAttribute("vendor");
  483. if (avpCode == null) {
  484. logger.debug("[ERROR] Command defines avp without code! Command: {}, Code: {}, ApplicationID: {}",
  485. new Object[] { msg.getName(), msg.getCommandCode(), msg.getApplicationId() });
  486. continue;
  487. }
  488. if (avpVendor == null) {
  489. logger.debug("[WARN] Command defines avp without vendor, assuming default. Command: {}, Code: {}, ApplicationID: {}",
  490. new Object[] { msg.getName(), msg.getCommandCode(), msg.getApplicationId() });
  491. avpVendor = "0";
  492. }
  493. // here we have name and multiplicity. we have to get avp def from name, clone and set multiplicity.
  494. AvpRepresentation strongRepresentation = null;
  495. AvpRepresentation strongKey = getMapKey(Integer.valueOf(avpCode), Long.valueOf(avpVendor));
  496. strongRepresentation = this.avpMap.get(strongKey);
  497. if (strongRepresentation != null && !strongRepresentation.isWeak()) {
  498. AvpRepresentationImpl clone;
  499. try {
  500. clone = (AvpRepresentationImpl) strongRepresentation.clone();
  501. clone.setMultiplicityIndicator(multiplicity);
  502. clone.markFixPosition(Integer.valueOf(index));
  503. commandAvpList.put(clone, clone);
  504. }
  505. catch (CloneNotSupportedException cnse) {
  506. // It should not happen, but anyway
  507. if(logger.isWarnEnabled()) {
  508. logger.warn("Unable to clone AVP " + strongRepresentation, cnse);
  509. }
  510. }
  511. }
  512. else {
  513. logger.debug("[WARN] No strong avp for key {}, in name: {}", new Object[] {strongKey, name});
  514. continue;
  515. }
  516. }
  517. }
  518. msg.setMessageAvps(commandAvpList);
  519. }
  520. }
  521. }
  522. }
  523. }
  524. /*
  525. * (non-Javadoc)
  526. *
  527. * @see org.jdiameter.api.validation.Dictionary#isConfigured()
  528. */
  529. public boolean isConfigured() {
  530. return this.configured;
  531. }
  532. public AvpRepresentation getAvp(int code) {
  533. return getAvp(code, 0);
  534. }
  535. public AvpRepresentation getAvp(int code, long vendorId) {
  536. if (!this.configured) {
  537. return null;
  538. }
  539. AvpRepresentation avp = avpMap.get(getMapKey(code, vendorId));
  540. if (avp == null) {
  541. logger.warn("AVP with code {} and Vendor-Id {} not present in dictionary!", code, vendorId);
  542. }
  543. return avp;
  544. }
  545. public AvpRepresentation getAvp(String avpName) {
  546. return this.configured ? avpByNameMap.get(avpName) : null;
  547. }
  548. private long getVendorCode(String vendorId) {
  549. long value = -1;
  550. if (vendorId == null) {
  551. value = 0;
  552. }
  553. else {
  554. String vendorCode = vendorMap.get(vendorId);
  555. value = vendorCode == null ? 0 : Long.parseLong(vendorCode);
  556. }
  557. return value;
  558. }
  559. private AvpRepresentation getMapKey(int avpCode, long vendorId) {
  560. return new AvpRepresentationImpl(avpCode, vendorId);
  561. }
  562. /*
  563. * (non-Javadoc)
  564. *
  565. * @see org.jdiameter.api.validation.Dictionary#getMessage(int, boolean)
  566. */
  567. public MessageRepresentation getMessage(int commandCode, boolean isRequest) {
  568. return this.getMessage(commandCode, 0, isRequest);
  569. }
  570. /*
  571. * (non-Javadoc)
  572. *
  573. * @see org.jdiameter.api.validation.Dictionary#getMessage(int, long, boolean)
  574. */
  575. public MessageRepresentation getMessage(int commandCode, long applicationId, boolean isRequest) {
  576. if (!this.configured) {
  577. return null;
  578. }
  579. MessageRepresentation key = new MessageRepresentationImpl(commandCode, applicationId, isRequest);
  580. return this.commandMap.get(key);
  581. }
  582. // Validation ---------------------------------------------------------------
  583. private boolean enabled = true;
  584. private ValidatorLevel sendValidationLevel = ValidatorLevel.ALL;
  585. private ValidatorLevel receiveValidationLevel = ValidatorLevel.OFF;
  586. /*
  587. * (non-Javadoc)
  588. *
  589. * @see org.jdiameter.api.validation.Dictionary#isValidate()
  590. */
  591. public boolean isEnabled() {
  592. return this.enabled;
  593. }
  594. /*
  595. * (non-Javadoc)
  596. *
  597. * @see org.jdiameter.api.validation.Dictionary#getSendLevel()
  598. */
  599. public ValidatorLevel getSendLevel() {
  600. return this.sendValidationLevel;
  601. }
  602. /*
  603. * (non-Javadoc)
  604. *
  605. * @see org.jdiameter.api.validation.Dictionary#getReceiveLevel()
  606. */
  607. public ValidatorLevel getReceiveLevel() {
  608. return this.receiveValidationLevel;
  609. }
  610. public void setSendLevel(ValidatorLevel level) {
  611. this.sendValidationLevel = level;
  612. }
  613. public void setReceiveLevel(ValidatorLevel level) {
  614. this.receiveValidationLevel = level;
  615. }
  616. public void setEnabled(boolean enabled) {
  617. this.enabled = enabled;
  618. }
  619. public void setConfigured(boolean configured) {
  620. this.configured = configured;
  621. }
  622. public void validate(Message msg, boolean incoming) throws AvpNotAllowedException {
  623. if (!enabled || !configured) {
  624. return;
  625. }
  626. MessageRepresentationImpl rep = new MessageRepresentationImpl(msg.getCommandCode(), msg.getApplicationId(), msg.isRequest());
  627. rep = (MessageRepresentationImpl) this.commandMap.get(rep);
  628. if (rep == null) {
  629. // no notion, lets leave it.
  630. logger.warn("Validation could not be performed, command not defined!. Code={}, Application-Id={}, Req={}",
  631. new Object[] { msg.getCommandCode(), msg.getApplicationId(), msg.isRequest() });
  632. return;
  633. }
  634. rep.validate(msg, (incoming ? receiveValidationLevel : sendValidationLevel));
  635. }
  636. // Helper methods -----------------------------------------------------------
  637. public Map<AvpRepresentation, AvpRepresentation> getAvpMap() {
  638. return avpMap;
  639. }
  640. public Map<String, String> getVendorMap() {
  641. return vendorMap;
  642. }
  643. public Map<MessageRepresentation, MessageRepresentation> getCommandMap() {
  644. return commandMap;
  645. }
  646. public Map<String, String> getTypedefMap() {
  647. return typedefMap;
  648. }
  649. public Map<String, AvpRepresentation> getNameToCodeMap() {
  650. return avpByNameMap;
  651. }
  652. protected void printAvpTree(AvpRepresentation rep, String tab) {
  653. String x = tab + "+-- " + rep.getCode() + "/" + rep.getVendorId();
  654. while(x.length() < 25) {
  655. x += ".";
  656. }
  657. System.out.println(x + rep.getName() + " > " + rep.getType());
  658. if(rep.isGrouped()) {
  659. for(AvpRepresentation repC : rep.getChildren()) {
  660. printAvpTree(repC, " " + tab);
  661. }
  662. }
  663. }
  664. }