PageRenderTime 61ms CodeModel.GetById 2ms app.highlight 53ms RepoModel.GetById 1ms app.codeStats 0ms

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