/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
- /*
- * JBoss, Home of Professional Open Source
- * Copyright 2011, Red Hat, Inc. and individual contributors
- * by the @authors tag. See the copyright.txt in the distribution for a
- * full listing of individual contributors.
- *
- * This is free software; you can redistribute it and/or modify it
- * under the terms of the GNU Lesser General Public License as
- * published by the Free Software Foundation; either version 2.1 of
- * the License, or (at your option) any later version.
- *
- * This software is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this software; if not, write to the Free
- * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
- */
-
- package org.mobicents.protocols.ss7.isup.impl.message;
-
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.util.Map;
- import java.util.Set;
- import java.util.TreeMap;
-
- import org.mobicents.protocols.ss7.isup.ISUPParameterFactory;
- import org.mobicents.protocols.ss7.isup.ParameterException;
- import org.mobicents.protocols.ss7.isup.impl.message.parameter.AbstractISUPParameter;
- import org.mobicents.protocols.ss7.isup.impl.message.parameter.CircuitIdentificationCodeImpl;
- import org.mobicents.protocols.ss7.isup.impl.message.parameter.EndOfOptionalParametersImpl;
- import org.mobicents.protocols.ss7.isup.message.parameter.CircuitIdentificationCode;
- import org.mobicents.protocols.ss7.isup.message.parameter.ISUPParameter;
- import org.mobicents.protocols.ss7.isup.message.parameter.MessageType;
-
-
- /**
- * Start time:14:09:04 2009-04-20<br>
- * Project: mobicents-isup-stack<br>
- * This is super message class for all messages that we have. It defines some
- * methods that need to be implemented
- *
- * @author <a href="mailto:baranowb@gmail.com"> Bartosz Baranowski </a>
- */
- public abstract class ISUPMessageImpl extends AbstractISUPMessage {
-
- /**
- * To use one when encoding, created, possibly when decoding
- */
- protected static final EndOfOptionalParametersImpl _END_OF_OPTIONAL_PARAMETERS = new EndOfOptionalParametersImpl();
-
- //protected static final Logger logger = Logger.getLogger(ISUPMessageImpl.class);
-
- //TODO: change everything below into [], for such small size of arrays, its faster to even search through them.
- /**
- * F = mandatory fixed length parameter;<br>
- * for type F parameters: the length, in octets, of the parameter content;
- */
- protected Map<Integer, ISUPParameter> f_Parameters;
- /**
- * V = mandatory variable length parameter;<br>
- * for type V parameters: the length, in octets, of the length indicator and
- * of the parameter content. The minimum and the maximum length are
- * indicated;
- */
- protected Map<Integer, ISUPParameter> v_Parameters;
- /**
- * O = optional parameter of fixed or variable length; for type O
- * parameters: the length, in octets, of the parameter name, length
- * indicator and parameter content. For variable length parameters the
- * minimum and maximum length is indicated.
- */
- protected Map<Integer, ISUPParameter> o_Parameters;
-
- // magic
- protected Set<Integer> mandatoryCodes;
- protected Set<Integer> mandatoryVariableCodes;
- protected Set<Integer> optionalCodes;
-
- protected Map<Integer, Integer> mandatoryCodeToIndex;
- protected Map<Integer, Integer> mandatoryVariableCodeToIndex;
- protected Map<Integer, Integer> optionalCodeToIndex;
-
- protected CircuitIdentificationCode cic;
- protected int sls;
- public ISUPMessageImpl(Set<Integer> mandatoryCodes, Set<Integer> mandatoryVariableCodes, Set<Integer> optionalCodes,
- Map<Integer, Integer> mandatoryCode2Index, Map<Integer, Integer> mandatoryVariableCode2Index,
- Map<Integer, Integer> optionalCode2Index) {
- super();
-
- this.f_Parameters = new TreeMap<Integer, ISUPParameter>();
- this.v_Parameters = new TreeMap<Integer, ISUPParameter>();
- this.o_Parameters = new TreeMap<Integer, ISUPParameter>();
-
- this.mandatoryCodes = mandatoryCodes;
- this.mandatoryVariableCodes = mandatoryVariableCodes;
- this.optionalCodes = optionalCodes;
-
- this.mandatoryCodeToIndex = mandatoryCode2Index;
- this.mandatoryVariableCodeToIndex = mandatoryVariableCode2Index;
- this.optionalCodeToIndex = optionalCode2Index;
-
- }
-
- /**
- *
- */
- public ISUPMessageImpl() {
- // TODO Auto-generated constructor stub
- }
-
- @Override
- public void setSls(int sls) {
- // if(sls>=16 || sls<0)
- // {
- // throw new IllegalArgumentException("SLS must be in range of one byte, it is: "+sls+"!");
- // }
- this.sls = (sls & 0x0F);
- }
-
- @Override
- public int getSls() {
- return this.sls;
- }
-
- /**
- * @return <ul>
- * <li><b>true</b> - if all requried parameters are set</li>
- * <li><b>false</b> - otherwise</li>
- * </ul>
- */
- public abstract boolean hasAllMandatoryParameters();
-
- /**
- * Returns message code. See Q.763 Table 4. It simply return value of static
- * constant - _MESSAGE_TYPE, where value of parameter is value _MESSAGE_CODE
- *
- * @return
- */
- public abstract MessageType getMessageType();
-
- // ////////////////
- // CODE SECTION //
- // ////////////////
- public byte[] encode() throws ParameterException {
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- // akward :)
- this.encode(bos);
- return bos.toByteArray();
- }
-
- public int encode(ByteArrayOutputStream bos) throws ParameterException {
-
- // bos.write(this.circuitIdentificationCode);
-
- boolean optionalPresent = this.o_Parameters.size() > 1;
- this.encodeMandatoryParameters(f_Parameters, bos);
- this.encodeMandatoryVariableParameters(v_Parameters, bos, optionalPresent);
- if (optionalPresent) {
- this.encodeOptionalParameters(o_Parameters, bos);
- }
-
- return bos.size();
- }
-
- // NOTE: those methods are more or less generic.
- protected void encodeMandatoryParameters(Map<Integer, ISUPParameter> parameters, ByteArrayOutputStream bos) throws ParameterException {
- // 1.5 Mandatory fixed part
- // Those parameters that are mandatory and of fixed length for a
- // particular message type will be
- // contained in the mandatory fixed part. The position, length and order
- // of the parameters is uniquely
- // defined by the message type; thus, the names of the parameters and
- // the length indicators are not
- // included in the message.
- if (this.cic == null) {
- // this will be changed to different exception
- throw new ParameterException("CIC is not set!");
- }
- ((AbstractISUPParameter)this.cic).encode(bos);
- for (ISUPParameter p : parameters.values()) {
- // System.err.println("ENCODE F: "+p.getCode()+"---> "+Utils.toHex(p.encode()));
- ((AbstractISUPParameter)p).encode(bos);
- }
- }
-
- /**
- * takes care of endoding parameters - poniters and actual parameters.
- *
- * @param parameters
- * - list of parameters
- * @param bos
- * - output
- * @param isOptionalPartPresent
- * - if <b>true</b> this will encode pointer to point for start
- * of optional part, otherwise it will encode this octet as zeros
- * @throws ParameterException
- */
- protected void encodeMandatoryVariableParameters(Map<Integer, ISUPParameter> parameters, ByteArrayOutputStream bos,
- boolean isOptionalPartPresent) throws ParameterException {
- try{
- byte[] pointers = null;
- // complicated
- if (!mandatoryVariablePartPossible()) {
- // we ommit pointer to this part, go straight for optional pointer.
- if (optionalPartIsPossible()) {
- if (isOptionalPartPresent) {
- pointers = new byte[] { 0x01 };
- } else {
- // zeros
- pointers = new byte[] { 0x00 };
- }
- bos.write(pointers);
- } else {
- // do nothing?
- }
-
- } else {
- if (optionalPartIsPossible()) {
- pointers = new byte[parameters.size() + 1];
- } else {
- pointers = new byte[parameters.size()];
- }
- ByteArrayOutputStream parametersBodyBOS = new ByteArrayOutputStream();
- byte lastParameterLength = 0;
- byte currentParameterLength = 0;
- for (int index = 0; index < parameters.size(); index++) {
- AbstractISUPParameter p = (AbstractISUPParameter)parameters.get(index);
-
- byte[] body = p.encode();
- currentParameterLength = (byte) body.length;
- if (body.length > 255) {
- // FIXME: is this check valid?
- throw new ParameterException("Length of body must not be greater than one octet - 255 ");
- }
- if (index == 0) {
- lastParameterLength = currentParameterLength;
-
- // This creates pointer to first mandatory variable param,
- // check on optional is required, since if its not defined
- // by message, pointer is omited.
- pointers[index] = (byte) (parameters.size() + (optionalPartIsPossible() ? 1 : 0));
- } else {
-
- pointers[index] = (byte) (pointers[index - 1] + lastParameterLength);
- lastParameterLength = currentParameterLength;
- }
-
- parametersBodyBOS.write(currentParameterLength);
- parametersBodyBOS.write(body);
- }
-
- // we ommit pointer to this part, go straight for optional pointer.
- if (optionalPartIsPossible()) {
- if (isOptionalPartPresent) {
- pointers[pointers.length - 1] = (byte) (pointers[pointers.length - 2] + lastParameterLength);
- } else {
- // zeros
- // pointers=new byte[]{0x00};
- }
- } else {
- // do nothing?
- }
-
- bos.write(pointers);
- bos.write(parametersBodyBOS.toByteArray());
- }
- }catch(ParameterException pe)
- {
- throw pe;
- }catch(Exception e)
- {
- throw new ParameterException(e);
- }
- }
-
- /**
- * This method must be called ONLY in case there are optional params. This
- * implies ISUPMessage.o_Parameters.size()>1 !!!
- *
- * @param parameters
- * @param bos
- * @throws ParameterException
- */
- protected void encodeOptionalParameters(Map<Integer, ISUPParameter> parameters, ByteArrayOutputStream bos) throws ParameterException {
-
- // NOTE: parameters MUST have as last endOfOptionalParametersParameter+1
- // param
- for (ISUPParameter p : parameters.values()) {
-
- if (p == null)
- continue;
-
- byte[] b = ((AbstractISUPParameter)p).encode();
- // System.err.println("ENCODE O: "+p.getCode()+"---> "+Utils.toHex(b));
- // FIXME: this can be slow, maybe we shoudl remove that, and code
- // this explicitly?
- if (b.length > 255) {
- throw new ParameterException("Parameter length is over 255: " + p);
- }
- if (!(p instanceof EndOfOptionalParametersImpl)) {
- bos.write(p.getCode());
-
- bos.write(b.length);
- }
- try{
- bos.write(b);
- }catch(IOException e)
- {
- throw new ParameterException("Failed to encode optional parameters.",e);
- }
- }
-
- }
-
- public int decode(byte[] b, ISUPParameterFactory parameterFactory) throws ParameterException {
- int index = 0;
- index += this.decodeMandatoryParameters(parameterFactory,b, index);
-
- if (mandatoryVariablePartPossible())
- index += this.decodeMandatoryVariableParameters(parameterFactory,b, index);
-
- if (!this.optionalPartIsPossible() || b.length == index || b[index] == 0x0) {
- return index;
- }
-
- // moving pointer to possible location
- // index++;
-
- // +1 for pointer location :)
- index += b[index];
-
- index += this.decodeOptionalParameters(parameterFactory,b, index);
- return index;
- }
-
- // Unfortunelty this cant be generic, can it?
- protected int decodeMandatoryParameters(ISUPParameterFactory parameterFactory,byte[] b, int index) throws ParameterException
- {
- int localIndex = index;
- if (b.length - index >= 3) {
- try {
- byte[] cic = new byte[2];
- cic[0] = b[index++];
- cic[1] = b[index++];
- this.cic = new CircuitIdentificationCodeImpl();
- ((AbstractISUPParameter)this.cic).decode(cic);
-
- } catch (Exception e) {
- // AIOOBE or IllegalArg
- throw new ParameterException("Failed to parse CircuitIdentificationCode due to: ", e);
- }
- try {
- // Message Type
- if (b[index] != this.getMessageType().getCode()) {
- throw new ParameterException("Message code is not: " + this.getMessageType().getCode());
- }
- } catch (Exception e) {
- // AIOOBE or IllegalArg
- throw new ParameterException("Failed to parse MessageCode due to: ", e);
- }
- index++;
-
-
- // return 3;
- return index - localIndex;
- } else {
- throw new IllegalArgumentException("byte[] must have atleast three octets");
- }
- }
-
- /**
- * decodes ptrs and returns offset from passed index value to first optional
- * parameter parameter
- *
- * @param b
- * @param index
- * @return
- * @throws ParameterException
- */
- protected int decodeMandatoryVariableParameters(ISUPParameterFactory parameterFactory,byte[] b, int index) throws ParameterException {
- // FIXME: possibly this should also be per msg, since if msg lacks
- // proper parameter, decoding wotn pick this up and will throw
- // some bad output, which wont give a clue about reason...
- int readCount = 0;
- // int optionalOffset = 0;
-
- if (b.length - index > 0) {
-
- byte extPIndex = -1;
- try {
- int count = getNumberOfMandatoryVariableLengthParameters();
- readCount = count;
- for (int parameterIndex = 0; parameterIndex < count; parameterIndex++) {
- int lengthPointerIndex = index + parameterIndex;
- int parameterLengthIndex = b[lengthPointerIndex] + lengthPointerIndex;
-
- int parameterLength = b[parameterLengthIndex];
- byte[] parameterBody = new byte[parameterLength];
- System.arraycopy(b, parameterLengthIndex + 1, parameterBody, 0, parameterLength);
- decodeMandatoryVariableBody(parameterFactory,parameterBody, parameterIndex);
-
- }
-
- // optionalOffset = b[index + readCount];
- } catch (ArrayIndexOutOfBoundsException aioobe) {
- throw new ParameterException(
- "Failed to read parameter, to few octets in buffer, parameter index: " + extPIndex, aioobe);
- } catch (IllegalArgumentException e) {
- throw new ParameterException("Failed to parse, paramet index: " + extPIndex, e);
- }
- } else {
- throw new ParameterException(
- "To few bytes to decode mandatory variable part. There should be atleast on byte to indicate optional part.");
- }
-
- // return readCount + optionalOffset;
- return readCount;
- }
-
- protected int decodeOptionalParameters(ISUPParameterFactory parameterFactory,byte[] b, int index) throws ParameterException {
-
- int localIndex = index;
-
- int readCount = 0;
- // if not, there are no params.
- if (b.length - index > 0) {
- // let it rip :)
- boolean readParameter = true;
- while (readParameter) {
- if (b.length - localIndex > 0 && b[localIndex] != 0) {
- readParameter = true;
- } else {
- readParameter = false;
- continue;
- }
- byte extPCode = -1;
- byte assumedParameterLength = -1;
- try {
-
- byte parameterCode = b[localIndex++];
- extPCode = parameterCode;
- byte parameterLength = b[localIndex++];
- assumedParameterLength = parameterLength;
- byte[] parameterBody = new byte[parameterLength];
- // This is bad, we will change this
-
- System.arraycopy(b, localIndex, parameterBody, 0, parameterLength);
- localIndex += parameterLength;
- readCount += 2 + parameterLength;
-
- decodeOptionalBody(parameterFactory,parameterBody, parameterCode);
-
- if (b.length - localIndex > 0 && b[localIndex] != 0) {
- readParameter = true;
- } else {
- readParameter = false;
- }
-
- } catch (ArrayIndexOutOfBoundsException aioobe) {
- throw new ParameterException("Failed to read parameter, to few octets in buffer, parameter code: "
- + extPCode + ", assumed length: " + assumedParameterLength, aioobe);
- } catch (IllegalArgumentException e) {
- throw new ParameterException("Failed to parse parameter: " + extPCode, e);
- }
- }
- }
-
- return readCount;
- }
-
- //TODO: add general method to handle decode and "addParam" so we can remove "copy/paste" code to create param and set it in msg.
- /**
- * @param parameterBody
- * @param parameterIndex
- */
- protected abstract void decodeMandatoryVariableBody(ISUPParameterFactory parameterFactory,byte[] parameterBody, int parameterIndex) throws ParameterException;
-
- protected abstract void decodeOptionalBody(ISUPParameterFactory parameterFactory,byte[] parameterBody, byte parameterCode) throws ParameterException;
-
- protected abstract int getNumberOfMandatoryVariableLengthParameters();
-
- protected abstract boolean optionalPartIsPossible();
-
- protected boolean mandatoryVariablePartPossible() {
-
- return getNumberOfMandatoryVariableLengthParameters() != 0;
- }
-
- // ////////////////////////
- // PARAM HANDLE SECTION //
- // ////////////////////////
- //TODO: define set/get methods here, just like diameter does and expose them ONLY via interface?
- // Some thing Oleg wants :)
- public void addParameter(ISUPParameter param) throws ParameterException {
- if (param == null) {
- throw new IllegalArgumentException("Argument must not be null");
- }
- int paramCode = param.getCode();
- if (this.mandatoryCodes.contains(paramCode)) {
- int index = this.mandatoryCodeToIndex.get(paramCode);
- this.f_Parameters.put(index, (AbstractISUPParameter)param);
- return;
- }
-
- if (this.mandatoryVariableCodes.contains(paramCode)) {
- int index = this.mandatoryVariableCodeToIndex.get(paramCode);
- this.v_Parameters.put(index, (AbstractISUPParameter)param);
- return;
- }
- if (this.optionalCodes.contains(paramCode)) {
- int index = this.optionalCodeToIndex.get(paramCode);
- this.o_Parameters.put(index, (AbstractISUPParameter)param);
- return;
- }
-
- throw new ParameterException("Parameter with code: " + paramCode
- + " is not defined in any type: mandatory, mandatory variable or optional");
- }
-
- public ISUPParameter getParameter(int parameterCode) throws ParameterException {
-
- if (this.mandatoryCodes.contains(parameterCode)) {
- int index = this.mandatoryCodeToIndex.get(parameterCode);
- return this.f_Parameters.get(index);
- }
-
- if (this.mandatoryVariableCodes.contains(parameterCode)) {
- int index = this.mandatoryVariableCodeToIndex.get(parameterCode);
- return this.v_Parameters.get(index);
- }
- if (this.optionalCodes.contains(parameterCode)) {
- int index = this.optionalCodeToIndex.get(parameterCode);
- return this.o_Parameters.get(index);
- }
-
- throw new ParameterException("Parameter with code: " + parameterCode
- + " is not defined in any type: mandatory, mandatory variable or optional");
- }
-
- public void removeParameter(int parameterCode) throws ParameterException {
- if (this.mandatoryCodes.contains(parameterCode)) {
- int index = this.mandatoryCodeToIndex.get(parameterCode);
- this.f_Parameters.remove(index);
- }
-
- if (this.mandatoryVariableCodes.contains(parameterCode)) {
- int index = this.mandatoryVariableCodeToIndex.get(parameterCode);
- this.v_Parameters.remove(index);
- }
- if (this.optionalCodes.contains(parameterCode)) {
- int index = this.optionalCodeToIndex.get(parameterCode);
- this.o_Parameters.remove(index);
- }
- throw new ParameterException("Parameter with code: " + parameterCode
- + " is not defined in any type: mandatory, mandatory variable or optional");
- }
-
- public String toString() {
- return super.toString() + "[" + getMessageType().getCode() + "]: F" + this.f_Parameters + ", V" + this.v_Parameters + ", O"
- + this.o_Parameters;
- }
-
- public CircuitIdentificationCode getCircuitIdentificationCode() {
- return this.cic;
- }
-
- public void setCircuitIdentificationCode(CircuitIdentificationCode cic) {
- this.cic = cic;
-
- }
-
- }