/protocols/ss7/isup/isup-impl/src/main/java/org/mobicents/protocols/ss7/isup/impl/message/ISUPMessageImpl.java

http://mobicents.googlecode.com/ · Java · 577 lines · 331 code · 86 blank · 160 comment · 61 complexity · 769ff3ad214b065cf08299cfbf6aabef MD5 · raw file

  1. /*
  2. * JBoss, Home of Professional Open Source
  3. * Copyright 2011, Red Hat, Inc. and individual contributors
  4. * by the @authors tag. See the copyright.txt in the distribution for a
  5. * full listing of individual contributors.
  6. *
  7. * This is free software; you can redistribute it and/or modify it
  8. * under the terms of the GNU Lesser General Public License as
  9. * published by the Free Software Foundation; either version 2.1 of
  10. * the License, or (at your option) any later version.
  11. *
  12. * This software is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this software; if not, write to the Free
  19. * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
  20. * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
  21. */
  22. package org.mobicents.protocols.ss7.isup.impl.message;
  23. import java.io.ByteArrayOutputStream;
  24. import java.io.IOException;
  25. import java.util.Map;
  26. import java.util.Set;
  27. import java.util.TreeMap;
  28. import org.mobicents.protocols.ss7.isup.ISUPParameterFactory;
  29. import org.mobicents.protocols.ss7.isup.ParameterException;
  30. import org.mobicents.protocols.ss7.isup.impl.message.parameter.AbstractISUPParameter;
  31. import org.mobicents.protocols.ss7.isup.impl.message.parameter.CircuitIdentificationCodeImpl;
  32. import org.mobicents.protocols.ss7.isup.impl.message.parameter.EndOfOptionalParametersImpl;
  33. import org.mobicents.protocols.ss7.isup.message.parameter.CircuitIdentificationCode;
  34. import org.mobicents.protocols.ss7.isup.message.parameter.ISUPParameter;
  35. import org.mobicents.protocols.ss7.isup.message.parameter.MessageType;
  36. /**
  37. * Start time:14:09:04 2009-04-20<br>
  38. * Project: mobicents-isup-stack<br>
  39. * This is super message class for all messages that we have. It defines some
  40. * methods that need to be implemented
  41. *
  42. * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
  43. */
  44. public abstract class ISUPMessageImpl extends AbstractISUPMessage {
  45. /**
  46. * To use one when encoding, created, possibly when decoding
  47. */
  48. protected static final EndOfOptionalParametersImpl _END_OF_OPTIONAL_PARAMETERS = new EndOfOptionalParametersImpl();
  49. //protected static final Logger logger = Logger.getLogger(ISUPMessageImpl.class);
  50. //TODO: change everything below into [], for such small size of arrays, its faster to even search through them.
  51. /**
  52. * F = mandatory fixed length parameter;<br>
  53. * for type F parameters: the length, in octets, of the parameter content;
  54. */
  55. protected Map<Integer, ISUPParameter> f_Parameters;
  56. /**
  57. * V = mandatory variable length parameter;<br>
  58. * for type V parameters: the length, in octets, of the length indicator and
  59. * of the parameter content. The minimum and the maximum length are
  60. * indicated;
  61. */
  62. protected Map<Integer, ISUPParameter> v_Parameters;
  63. /**
  64. * O = optional parameter of fixed or variable length; for type O
  65. * parameters: the length, in octets, of the parameter name, length
  66. * indicator and parameter content. For variable length parameters the
  67. * minimum and maximum length is indicated.
  68. */
  69. protected Map<Integer, ISUPParameter> o_Parameters;
  70. // magic
  71. protected Set<Integer> mandatoryCodes;
  72. protected Set<Integer> mandatoryVariableCodes;
  73. protected Set<Integer> optionalCodes;
  74. protected Map<Integer, Integer> mandatoryCodeToIndex;
  75. protected Map<Integer, Integer> mandatoryVariableCodeToIndex;
  76. protected Map<Integer, Integer> optionalCodeToIndex;
  77. protected CircuitIdentificationCode cic;
  78. protected int sls;
  79. public ISUPMessageImpl(Set<Integer> mandatoryCodes, Set<Integer> mandatoryVariableCodes, Set<Integer> optionalCodes,
  80. Map<Integer, Integer> mandatoryCode2Index, Map<Integer, Integer> mandatoryVariableCode2Index,
  81. Map<Integer, Integer> optionalCode2Index) {
  82. super();
  83. this.f_Parameters = new TreeMap<Integer, ISUPParameter>();
  84. this.v_Parameters = new TreeMap<Integer, ISUPParameter>();
  85. this.o_Parameters = new TreeMap<Integer, ISUPParameter>();
  86. this.mandatoryCodes = mandatoryCodes;
  87. this.mandatoryVariableCodes = mandatoryVariableCodes;
  88. this.optionalCodes = optionalCodes;
  89. this.mandatoryCodeToIndex = mandatoryCode2Index;
  90. this.mandatoryVariableCodeToIndex = mandatoryVariableCode2Index;
  91. this.optionalCodeToIndex = optionalCode2Index;
  92. }
  93. /**
  94. *
  95. */
  96. public ISUPMessageImpl() {
  97. // TODO Auto-generated constructor stub
  98. }
  99. @Override
  100. public void setSls(int sls) {
  101. // if(sls>=16 || sls<0)
  102. // {
  103. // throw new IllegalArgumentException("SLS must be in range of one byte, it is: "+sls+"!");
  104. // }
  105. this.sls = (sls & 0x0F);
  106. }
  107. @Override
  108. public int getSls() {
  109. return this.sls;
  110. }
  111. /**
  112. * @return <ul>
  113. * <li><b>true</b> - if all requried parameters are set</li>
  114. * <li><b>false</b> - otherwise</li>
  115. * </ul>
  116. */
  117. public abstract boolean hasAllMandatoryParameters();
  118. /**
  119. * Returns message code. See Q.763 Table 4. It simply return value of static
  120. * constant - _MESSAGE_TYPE, where value of parameter is value _MESSAGE_CODE
  121. *
  122. * @return
  123. */
  124. public abstract MessageType getMessageType();
  125. // ////////////////
  126. // CODE SECTION //
  127. // ////////////////
  128. public byte[] encode() throws ParameterException {
  129. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  130. // akward :)
  131. this.encode(bos);
  132. return bos.toByteArray();
  133. }
  134. public int encode(ByteArrayOutputStream bos) throws ParameterException {
  135. // bos.write(this.circuitIdentificationCode);
  136. boolean optionalPresent = this.o_Parameters.size() > 1;
  137. this.encodeMandatoryParameters(f_Parameters, bos);
  138. this.encodeMandatoryVariableParameters(v_Parameters, bos, optionalPresent);
  139. if (optionalPresent) {
  140. this.encodeOptionalParameters(o_Parameters, bos);
  141. }
  142. return bos.size();
  143. }
  144. // NOTE: those methods are more or less generic.
  145. protected void encodeMandatoryParameters(Map<Integer, ISUPParameter> parameters, ByteArrayOutputStream bos) throws ParameterException {
  146. // 1.5 Mandatory fixed part
  147. // Those parameters that are mandatory and of fixed length for a
  148. // particular message type will be
  149. // contained in the mandatory fixed part. The position, length and order
  150. // of the parameters is uniquely
  151. // defined by the message type; thus, the names of the parameters and
  152. // the length indicators are not
  153. // included in the message.
  154. if (this.cic == null) {
  155. // this will be changed to different exception
  156. throw new ParameterException("CIC is not set!");
  157. }
  158. ((AbstractISUPParameter)this.cic).encode(bos);
  159. for (ISUPParameter p : parameters.values()) {
  160. // System.err.println("ENCODE F: "+p.getCode()+"---> "+Utils.toHex(p.encode()));
  161. ((AbstractISUPParameter)p).encode(bos);
  162. }
  163. }
  164. /**
  165. * takes care of endoding parameters - poniters and actual parameters.
  166. *
  167. * @param parameters
  168. * - list of parameters
  169. * @param bos
  170. * - output
  171. * @param isOptionalPartPresent
  172. * - if <b>true</b> this will encode pointer to point for start
  173. * of optional part, otherwise it will encode this octet as zeros
  174. * @throws ParameterException
  175. */
  176. protected void encodeMandatoryVariableParameters(Map<Integer, ISUPParameter> parameters, ByteArrayOutputStream bos,
  177. boolean isOptionalPartPresent) throws ParameterException {
  178. try{
  179. byte[] pointers = null;
  180. // complicated
  181. if (!mandatoryVariablePartPossible()) {
  182. // we ommit pointer to this part, go straight for optional pointer.
  183. if (optionalPartIsPossible()) {
  184. if (isOptionalPartPresent) {
  185. pointers = new byte[] { 0x01 };
  186. } else {
  187. // zeros
  188. pointers = new byte[] { 0x00 };
  189. }
  190. bos.write(pointers);
  191. } else {
  192. // do nothing?
  193. }
  194. } else {
  195. if (optionalPartIsPossible()) {
  196. pointers = new byte[parameters.size() + 1];
  197. } else {
  198. pointers = new byte[parameters.size()];
  199. }
  200. ByteArrayOutputStream parametersBodyBOS = new ByteArrayOutputStream();
  201. byte lastParameterLength = 0;
  202. byte currentParameterLength = 0;
  203. for (int index = 0; index < parameters.size(); index++) {
  204. AbstractISUPParameter p = (AbstractISUPParameter)parameters.get(index);
  205. byte[] body = p.encode();
  206. currentParameterLength = (byte) body.length;
  207. if (body.length > 255) {
  208. // FIXME: is this check valid?
  209. throw new ParameterException("Length of body must not be greater than one octet - 255 ");
  210. }
  211. if (index == 0) {
  212. lastParameterLength = currentParameterLength;
  213. // This creates pointer to first mandatory variable param,
  214. // check on optional is required, since if its not defined
  215. // by message, pointer is omited.
  216. pointers[index] = (byte) (parameters.size() + (optionalPartIsPossible() ? 1 : 0));
  217. } else {
  218. pointers[index] = (byte) (pointers[index - 1] + lastParameterLength);
  219. lastParameterLength = currentParameterLength;
  220. }
  221. parametersBodyBOS.write(currentParameterLength);
  222. parametersBodyBOS.write(body);
  223. }
  224. // we ommit pointer to this part, go straight for optional pointer.
  225. if (optionalPartIsPossible()) {
  226. if (isOptionalPartPresent) {
  227. pointers[pointers.length - 1] = (byte) (pointers[pointers.length - 2] + lastParameterLength);
  228. } else {
  229. // zeros
  230. // pointers=new byte[]{0x00};
  231. }
  232. } else {
  233. // do nothing?
  234. }
  235. bos.write(pointers);
  236. bos.write(parametersBodyBOS.toByteArray());
  237. }
  238. }catch(ParameterException pe)
  239. {
  240. throw pe;
  241. }catch(Exception e)
  242. {
  243. throw new ParameterException(e);
  244. }
  245. }
  246. /**
  247. * This method must be called ONLY in case there are optional params. This
  248. * implies ISUPMessage.o_Parameters.size()>1 !!!
  249. *
  250. * @param parameters
  251. * @param bos
  252. * @throws ParameterException
  253. */
  254. protected void encodeOptionalParameters(Map<Integer, ISUPParameter> parameters, ByteArrayOutputStream bos) throws ParameterException {
  255. // NOTE: parameters MUST have as last endOfOptionalParametersParameter+1
  256. // param
  257. for (ISUPParameter p : parameters.values()) {
  258. if (p == null)
  259. continue;
  260. byte[] b = ((AbstractISUPParameter)p).encode();
  261. // System.err.println("ENCODE O: "+p.getCode()+"---> "+Utils.toHex(b));
  262. // FIXME: this can be slow, maybe we shoudl remove that, and code
  263. // this explicitly?
  264. if (b.length > 255) {
  265. throw new ParameterException("Parameter length is over 255: " + p);
  266. }
  267. if (!(p instanceof EndOfOptionalParametersImpl)) {
  268. bos.write(p.getCode());
  269. bos.write(b.length);
  270. }
  271. try{
  272. bos.write(b);
  273. }catch(IOException e)
  274. {
  275. throw new ParameterException("Failed to encode optional parameters.",e);
  276. }
  277. }
  278. }
  279. public int decode(byte[] b, ISUPParameterFactory parameterFactory) throws ParameterException {
  280. int index = 0;
  281. index += this.decodeMandatoryParameters(parameterFactory,b, index);
  282. if (mandatoryVariablePartPossible())
  283. index += this.decodeMandatoryVariableParameters(parameterFactory,b, index);
  284. if (!this.optionalPartIsPossible() || b.length == index || b[index] == 0x0) {
  285. return index;
  286. }
  287. // moving pointer to possible location
  288. // index++;
  289. // +1 for pointer location :)
  290. index += b[index];
  291. index += this.decodeOptionalParameters(parameterFactory,b, index);
  292. return index;
  293. }
  294. // Unfortunelty this cant be generic, can it?
  295. protected int decodeMandatoryParameters(ISUPParameterFactory parameterFactory,byte[] b, int index) throws ParameterException
  296. {
  297. int localIndex = index;
  298. if (b.length - index >= 3) {
  299. try {
  300. byte[] cic = new byte[2];
  301. cic[0] = b[index++];
  302. cic[1] = b[index++];
  303. this.cic = new CircuitIdentificationCodeImpl();
  304. ((AbstractISUPParameter)this.cic).decode(cic);
  305. } catch (Exception e) {
  306. // AIOOBE or IllegalArg
  307. throw new ParameterException("Failed to parse CircuitIdentificationCode due to: ", e);
  308. }
  309. try {
  310. // Message Type
  311. if (b[index] != this.getMessageType().getCode()) {
  312. throw new ParameterException("Message code is not: " + this.getMessageType().getCode());
  313. }
  314. } catch (Exception e) {
  315. // AIOOBE or IllegalArg
  316. throw new ParameterException("Failed to parse MessageCode due to: ", e);
  317. }
  318. index++;
  319. // return 3;
  320. return index - localIndex;
  321. } else {
  322. throw new IllegalArgumentException("byte[] must have atleast three octets");
  323. }
  324. }
  325. /**
  326. * decodes ptrs and returns offset from passed index value to first optional
  327. * parameter parameter
  328. *
  329. * @param b
  330. * @param index
  331. * @return
  332. * @throws ParameterException
  333. */
  334. protected int decodeMandatoryVariableParameters(ISUPParameterFactory parameterFactory,byte[] b, int index) throws ParameterException {
  335. // FIXME: possibly this should also be per msg, since if msg lacks
  336. // proper parameter, decoding wotn pick this up and will throw
  337. // some bad output, which wont give a clue about reason...
  338. int readCount = 0;
  339. // int optionalOffset = 0;
  340. if (b.length - index > 0) {
  341. byte extPIndex = -1;
  342. try {
  343. int count = getNumberOfMandatoryVariableLengthParameters();
  344. readCount = count;
  345. for (int parameterIndex = 0; parameterIndex < count; parameterIndex++) {
  346. int lengthPointerIndex = index + parameterIndex;
  347. int parameterLengthIndex = b[lengthPointerIndex] + lengthPointerIndex;
  348. int parameterLength = b[parameterLengthIndex];
  349. byte[] parameterBody = new byte[parameterLength];
  350. System.arraycopy(b, parameterLengthIndex + 1, parameterBody, 0, parameterLength);
  351. decodeMandatoryVariableBody(parameterFactory,parameterBody, parameterIndex);
  352. }
  353. // optionalOffset = b[index + readCount];
  354. } catch (ArrayIndexOutOfBoundsException aioobe) {
  355. throw new ParameterException(
  356. "Failed to read parameter, to few octets in buffer, parameter index: " + extPIndex, aioobe);
  357. } catch (IllegalArgumentException e) {
  358. throw new ParameterException("Failed to parse, paramet index: " + extPIndex, e);
  359. }
  360. } else {
  361. throw new ParameterException(
  362. "To few bytes to decode mandatory variable part. There should be atleast on byte to indicate optional part.");
  363. }
  364. // return readCount + optionalOffset;
  365. return readCount;
  366. }
  367. protected int decodeOptionalParameters(ISUPParameterFactory parameterFactory,byte[] b, int index) throws ParameterException {
  368. int localIndex = index;
  369. int readCount = 0;
  370. // if not, there are no params.
  371. if (b.length - index > 0) {
  372. // let it rip :)
  373. boolean readParameter = true;
  374. while (readParameter) {
  375. if (b.length - localIndex > 0 && b[localIndex] != 0) {
  376. readParameter = true;
  377. } else {
  378. readParameter = false;
  379. continue;
  380. }
  381. byte extPCode = -1;
  382. byte assumedParameterLength = -1;
  383. try {
  384. byte parameterCode = b[localIndex++];
  385. extPCode = parameterCode;
  386. byte parameterLength = b[localIndex++];
  387. assumedParameterLength = parameterLength;
  388. byte[] parameterBody = new byte[parameterLength];
  389. // This is bad, we will change this
  390. System.arraycopy(b, localIndex, parameterBody, 0, parameterLength);
  391. localIndex += parameterLength;
  392. readCount += 2 + parameterLength;
  393. decodeOptionalBody(parameterFactory,parameterBody, parameterCode);
  394. if (b.length - localIndex > 0 && b[localIndex] != 0) {
  395. readParameter = true;
  396. } else {
  397. readParameter = false;
  398. }
  399. } catch (ArrayIndexOutOfBoundsException aioobe) {
  400. throw new ParameterException("Failed to read parameter, to few octets in buffer, parameter code: "
  401. + extPCode + ", assumed length: " + assumedParameterLength, aioobe);
  402. } catch (IllegalArgumentException e) {
  403. throw new ParameterException("Failed to parse parameter: " + extPCode, e);
  404. }
  405. }
  406. }
  407. return readCount;
  408. }
  409. //TODO: add general method to handle decode and "addParam" so we can remove "copy/paste" code to create param and set it in msg.
  410. /**
  411. * @param parameterBody
  412. * @param parameterIndex
  413. */
  414. protected abstract void decodeMandatoryVariableBody(ISUPParameterFactory parameterFactory,byte[] parameterBody, int parameterIndex) throws ParameterException;
  415. protected abstract void decodeOptionalBody(ISUPParameterFactory parameterFactory,byte[] parameterBody, byte parameterCode) throws ParameterException;
  416. protected abstract int getNumberOfMandatoryVariableLengthParameters();
  417. protected abstract boolean optionalPartIsPossible();
  418. protected boolean mandatoryVariablePartPossible() {
  419. return getNumberOfMandatoryVariableLengthParameters() != 0;
  420. }
  421. // ////////////////////////
  422. // PARAM HANDLE SECTION //
  423. // ////////////////////////
  424. //TODO: define set/get methods here, just like diameter does and expose them ONLY via interface?
  425. // Some thing Oleg wants :)
  426. public void addParameter(ISUPParameter param) throws ParameterException {
  427. if (param == null) {
  428. throw new IllegalArgumentException("Argument must not be null");
  429. }
  430. int paramCode = param.getCode();
  431. if (this.mandatoryCodes.contains(paramCode)) {
  432. int index = this.mandatoryCodeToIndex.get(paramCode);
  433. this.f_Parameters.put(index, (AbstractISUPParameter)param);
  434. return;
  435. }
  436. if (this.mandatoryVariableCodes.contains(paramCode)) {
  437. int index = this.mandatoryVariableCodeToIndex.get(paramCode);
  438. this.v_Parameters.put(index, (AbstractISUPParameter)param);
  439. return;
  440. }
  441. if (this.optionalCodes.contains(paramCode)) {
  442. int index = this.optionalCodeToIndex.get(paramCode);
  443. this.o_Parameters.put(index, (AbstractISUPParameter)param);
  444. return;
  445. }
  446. throw new ParameterException("Parameter with code: " + paramCode
  447. + " is not defined in any type: mandatory, mandatory variable or optional");
  448. }
  449. public ISUPParameter getParameter(int parameterCode) throws ParameterException {
  450. if (this.mandatoryCodes.contains(parameterCode)) {
  451. int index = this.mandatoryCodeToIndex.get(parameterCode);
  452. return this.f_Parameters.get(index);
  453. }
  454. if (this.mandatoryVariableCodes.contains(parameterCode)) {
  455. int index = this.mandatoryVariableCodeToIndex.get(parameterCode);
  456. return this.v_Parameters.get(index);
  457. }
  458. if (this.optionalCodes.contains(parameterCode)) {
  459. int index = this.optionalCodeToIndex.get(parameterCode);
  460. return this.o_Parameters.get(index);
  461. }
  462. throw new ParameterException("Parameter with code: " + parameterCode
  463. + " is not defined in any type: mandatory, mandatory variable or optional");
  464. }
  465. public void removeParameter(int parameterCode) throws ParameterException {
  466. if (this.mandatoryCodes.contains(parameterCode)) {
  467. int index = this.mandatoryCodeToIndex.get(parameterCode);
  468. this.f_Parameters.remove(index);
  469. }
  470. if (this.mandatoryVariableCodes.contains(parameterCode)) {
  471. int index = this.mandatoryVariableCodeToIndex.get(parameterCode);
  472. this.v_Parameters.remove(index);
  473. }
  474. if (this.optionalCodes.contains(parameterCode)) {
  475. int index = this.optionalCodeToIndex.get(parameterCode);
  476. this.o_Parameters.remove(index);
  477. }
  478. throw new ParameterException("Parameter with code: " + parameterCode
  479. + " is not defined in any type: mandatory, mandatory variable or optional");
  480. }
  481. public String toString() {
  482. return super.toString() + "[" + getMessageType().getCode() + "]: F" + this.f_Parameters + ", V" + this.v_Parameters + ", O"
  483. + this.o_Parameters;
  484. }
  485. public CircuitIdentificationCode getCircuitIdentificationCode() {
  486. return this.cic;
  487. }
  488. public void setCircuitIdentificationCode(CircuitIdentificationCode cic) {
  489. this.cic = cic;
  490. }
  491. }