diff --git a/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java b/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java
index be64677..2190cb5 100644
--- a/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java
+++ b/common/src/main/java/cn/skcks/docking/gb28181/common/xml/XmlUtils.java
@@ -28,7 +28,7 @@ public class XmlUtils {
// 大驼峰 (首字母大写)
mapper.setPropertyNamingStrategy(new PropertyNamingStrategies.UpperCamelCaseStrategy());
// 添加 xml 头部声明
- mapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, true);
+ mapper.configure(ToXmlGenerator.Feature.WRITE_XML_DECLARATION, false);
}
public static String toXml(Object obj) {
diff --git a/gb28181-service/pom.xml b/gb28181-service/pom.xml
index e748177..3ff1ba9 100644
--- a/gb28181-service/pom.xml
+++ b/gb28181-service/pom.xml
@@ -46,13 +46,6 @@
1.3.0-91
-
-
- org.dom4j
- dom4j
- 2.1.4
-
-
org.mapstruct
diff --git a/gb28181-sip/.gitignore b/gb28181-sip/.gitignore
new file mode 100644
index 0000000..5ff6309
--- /dev/null
+++ b/gb28181-sip/.gitignore
@@ -0,0 +1,38 @@
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### IntelliJ IDEA ###
+.idea/modules.xml
+.idea/jarRepositories.xml
+.idea/compiler.xml
+.idea/libraries/
+*.iws
+*.iml
+*.ipr
+
+### Eclipse ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+
+### Mac OS ###
+.DS_Store
\ No newline at end of file
diff --git a/gb28181-sip/pom.xml b/gb28181-sip/pom.xml
new file mode 100644
index 0000000..aed1a83
--- /dev/null
+++ b/gb28181-sip/pom.xml
@@ -0,0 +1,44 @@
+
+
+ 4.0.0
+
+ cn.skcks.docking
+ gb28181
+ 0.1.0-SNAPSHOT
+
+
+ cn.skcks.docking.gb28181
+ gb28181-sip
+
+
+ 17
+ 17
+ UTF-8
+
+
+
+
+ cn.skcks.docking.gb28181
+ common
+
+
+
+
+ javax.sip
+ jain-sip-ri
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ org.junit.jupiter
+ junit-jupiter
+ test
+
+
+
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181Description.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181Description.java
new file mode 100644
index 0000000..350425e
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181Description.java
@@ -0,0 +1,39 @@
+package cn.skcks.docking.gb28181.sdp;
+
+import gov.nist.javax.sdp.SessionDescriptionImpl;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.sdp.SdpException;
+import javax.sdp.SessionDescription;
+
+@Slf4j
+@Setter
+@Getter
+public class GB28181Description extends SessionDescriptionImpl implements SessionDescription {
+ private SsrcField ssrcField;
+ private SessionDescriptionImpl sessionDescription;
+
+ public GB28181Description(){
+ super();
+ }
+
+ public GB28181Description(SessionDescription sessionDescription) throws SdpException {
+ super(sessionDescription);
+ }
+
+ @SneakyThrows
+ public static GB28181Description build(SessionDescription sessionDescription){
+ return new GB28181Description(sessionDescription);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder(super.toString());
+ sb.append(getSsrcField() == null ? "" : getSsrcField().toString());
+ // return "+";
+ return sb.toString();
+ }
+}
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParser.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParser.java
new file mode 100644
index 0000000..d384e2e
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParser.java
@@ -0,0 +1,88 @@
+package cn.skcks.docking.gb28181.sdp;
+
+import gov.nist.core.ParserCore;
+import gov.nist.javax.sdp.SessionDescriptionImpl;
+import gov.nist.javax.sdp.fields.SDPField;
+import gov.nist.javax.sdp.parser.Lexer;
+import gov.nist.javax.sdp.parser.SDPParser;
+
+import java.text.ParseException;
+import java.util.Vector;
+
+@SuppressWarnings("all")
+public class GB28181DescriptionParser extends ParserCore {
+ protected Lexer lexer;
+ protected Vector sdpMessage;
+
+ public GB28181DescriptionParser(Vector sdpMessage) {
+ this.sdpMessage = sdpMessage;
+ }
+
+ public GB28181DescriptionParser(String message) {
+ int start = 0;
+ String line = null;
+ // Return trivially if there is no sdp announce message
+ // to be parsed. Bruno Konik noticed this bug.
+ if (message == null) return;
+ sdpMessage = new Vector();
+ // Strip off leading and trailing junk.
+ String sdpAnnounce = message.trim() + "\r\n";
+ // Bug fix by Andreas Bystrom.
+ while (start < sdpAnnounce.length()) {
+ // Major re-write by Ricardo Borba.
+ int lfPos = sdpAnnounce.indexOf("\n", start);
+ int crPos = sdpAnnounce.indexOf("\r", start);
+
+ if (lfPos >= 0 && crPos < 0) {
+ // there are only "\n" separators
+ line = sdpAnnounce.substring(start, lfPos);
+ start = lfPos + 1;
+ } else if (lfPos < 0 && crPos >= 0) {
+ //bug fix: there are only "\r" separators
+ line = sdpAnnounce.substring(start, crPos);
+ start = crPos + 1;
+ } else if (lfPos >= 0 && crPos >= 0) {
+ // there are "\r\n" or "\n\r" (if exists) separators
+ if (lfPos > crPos) {
+ // assume "\r\n" for now
+ line = sdpAnnounce.substring(start, crPos);
+ // Check if the "\r" and "\n" are close together
+ if (lfPos == crPos + 1) {
+ start = lfPos + 1; // "\r\n"
+ } else {
+ start = crPos + 1; // "\r" followed by the next record and a "\n" further away
+ }
+ } else {
+ // assume "\n\r" for now
+ line = sdpAnnounce.substring(start, lfPos);
+ // Check if the "\n" and "\r" are close together
+ if (crPos == lfPos + 1) {
+ start = crPos + 1; // "\n\r"
+ } else {
+ start = lfPos + 1; // "\n" followed by the next record and a "\r" further away
+ }
+ }
+ } else if (lfPos < 0 && crPos < 0) { // end
+ break;
+ }
+ sdpMessage.addElement(line);
+ }
+ }
+
+ public GB28181Description parse() throws ParseException {
+ GB28181Description retval = new GB28181Description();
+ for (int i = 0; i < sdpMessage.size(); i++) {
+ String field = (String) sdpMessage.elementAt(i);
+ SDPParser sdpParser = GB28181DescriptionParserFactory.createParser(field);
+ SDPField sdpField = null;
+ if (sdpParser != null) {
+ sdpField = sdpParser.parse();
+ }
+ retval.addField(sdpField);
+ if (sdpField instanceof SsrcField ssrc) {
+ retval.setSsrcField(ssrc);
+ }
+ }
+ return retval;
+ }
+}
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParserFactory.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParserFactory.java
new file mode 100644
index 0000000..70c85c6
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/GB28181DescriptionParserFactory.java
@@ -0,0 +1,17 @@
+package cn.skcks.docking.gb28181.sdp;
+
+import gov.nist.javax.sdp.parser.Lexer;
+import gov.nist.javax.sdp.parser.ParserFactory;
+import gov.nist.javax.sdp.parser.SDPParser;
+
+import java.text.ParseException;
+
+public class GB28181DescriptionParserFactory {
+ public static SDPParser createParser(String field) throws ParseException {
+ String fieldName = Lexer.getFieldName(field);
+ if(fieldName.equalsIgnoreCase("y")){
+ return new SsrcFieldParser(field);
+ }
+ return ParserFactory.createParser(field);
+ }
+}
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/Gb28181Sdp.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/Gb28181Sdp.java
new file mode 100644
index 0000000..48a7d3f
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/Gb28181Sdp.java
@@ -0,0 +1,14 @@
+package cn.skcks.docking.gb28181.sdp;
+
+import lombok.Builder;
+import lombok.Data;
+
+import javax.sdp.SessionDescription;
+
+@Builder
+@Data
+public class Gb28181Sdp {
+ private SessionDescription baseSdb;
+ private String ssrc;
+ private String mediaDescription;
+}
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcField.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcField.java
new file mode 100644
index 0000000..c9045dc
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcField.java
@@ -0,0 +1,28 @@
+package cn.skcks.docking.gb28181.sdp;
+
+import gov.nist.core.Separators;
+import gov.nist.javax.sdp.fields.SDPField;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@AllArgsConstructor
+public class SsrcField extends SDPField {
+ private static final String SSRC_FIELD = "y=";
+ public SsrcField() {
+ super(SSRC_FIELD);
+ }
+
+ private String ssrc;
+
+ @Override
+ public String encode() {
+ return SSRC_FIELD + ssrc + Separators.NEWLINE;
+ }
+
+ public String toString(){
+ return encode();
+ }
+}
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcFieldParser.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcFieldParser.java
new file mode 100644
index 0000000..f6134c9
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sdp/SsrcFieldParser.java
@@ -0,0 +1,33 @@
+package cn.skcks.docking.gb28181.sdp;
+
+import gov.nist.javax.sdp.fields.SDPField;
+import gov.nist.javax.sdp.parser.Lexer;
+import gov.nist.javax.sdp.parser.SDPParser;
+
+import java.text.ParseException;
+
+public class SsrcFieldParser extends SDPParser {
+ public SsrcFieldParser(String ssrcField) {
+ this.lexer = new Lexer("charLexer", ssrcField);
+ }
+
+ public SsrcField ssrcField() throws ParseException {
+ try {
+ this.lexer.match('y');
+ this.lexer.SPorHT();
+ this.lexer.match('=');
+ this.lexer.SPorHT();
+
+ SsrcField ssrcField = new SsrcField();
+ String rest = lexer.getRest().trim();
+ ssrcField.setSsrc(rest);
+ return ssrcField;
+ } catch (Exception e) {
+ throw lexer.createParseException();
+ }
+ }
+
+ public SDPField parse() throws ParseException {
+ return this.ssrcField();
+ }
+}
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/generic/SipBuilder.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/generic/SipBuilder.java
new file mode 100644
index 0000000..eb23b33
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/generic/SipBuilder.java
@@ -0,0 +1,109 @@
+package cn.skcks.docking.gb28181.sip.generic;
+
+import cn.skcks.docking.gb28181.sip.header.XGBVerHeader;
+import cn.skcks.docking.gb28181.sip.header.impl.XGBVerHeaderImpl;
+import lombok.SneakyThrows;
+import org.apache.commons.lang3.StringUtils;
+
+import javax.sip.SipFactory;
+import javax.sip.address.Address;
+import javax.sip.address.AddressFactory;
+import javax.sip.address.SipURI;
+import javax.sip.header.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+public class SipBuilder {
+ public static SipFactory getSipFactory(){
+ return SipFactory.getInstance();
+ }
+
+ @SneakyThrows
+ public static AddressFactory createAddressFactory() {
+ return getSipFactory().createAddressFactory();
+ }
+
+ @SneakyThrows
+ public static HeaderFactory createHeaderFactory() {
+ return getSipFactory().createHeaderFactory();
+ }
+
+ @SneakyThrows
+ public static String createHostAddress(String ip, int port) {
+ return StringUtils.joinWith(":", ip, port);
+ }
+
+ @SneakyThrows
+ public static SipURI createSipURI(String id, String address) {
+ return createAddressFactory().createSipURI(id, address);
+ }
+
+ @SneakyThrows
+ public static Address createAddress(SipURI uri) {
+ return createAddressFactory().createAddress(uri);
+ }
+
+ @SneakyThrows
+ public static ToHeader createToHeader(Address toAddress, String toTag) {
+ return createHeaderFactory().createToHeader(toAddress, toTag);
+ }
+
+ @SneakyThrows
+ public static FromHeader createFromHeader(Address fromAddress, String fromTag) {
+ return createHeaderFactory().createFromHeader(fromAddress, fromTag);
+ }
+
+ @SneakyThrows
+ public static CSeqHeader createCSeqHeader(long cSeq, String method){
+ return createHeaderFactory().createCSeqHeader(cSeq, method);
+ }
+
+
+ @SneakyThrows
+ public static MaxForwardsHeader createMaxForwardsHeader(int maxForwards) {
+ return createHeaderFactory().createMaxForwardsHeader(maxForwards);
+ }
+
+ @SneakyThrows
+ public static ViaHeader createViaHeader(String ip, int port, String transport, String viaTag){
+ ViaHeader viaHeader = createHeaderFactory().createViaHeader(ip, port, transport, viaTag);
+ viaHeader.setRPort();
+ return viaHeader;
+ }
+
+ @SneakyThrows
+ public static List createViaHeaders(String ip, int port, String transport, String viaTag) {
+ return Collections.singletonList(createViaHeader(ip, port, transport, viaTag));
+ }
+
+
+ @SneakyThrows
+ public static ContactHeader createContactHeader(Address address){
+ return createHeaderFactory().createContactHeader(address);
+ };
+
+ @SneakyThrows
+ public static ContentTypeHeader createContentTypeHeader(String contentType, String subType){
+ return createHeaderFactory().createContentTypeHeader(contentType, subType);
+ }
+
+ @SneakyThrows
+ public static ContentTypeHeader createSDPContentTypeHeader(){
+ return createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP");
+ }
+
+ public static UserAgentHeader createUserAgentHeader(String userAgent){
+ return createUserAgentHeader(Arrays.stream(StringUtils.split(userAgent, StringUtils.SPACE)).toList());
+ }
+
+ @SneakyThrows
+ public static UserAgentHeader createUserAgentHeader(List product){
+ return createHeaderFactory().createUserAgentHeader(product);
+ }
+
+ @SneakyThrows
+ public static XGBVerHeader createUserAgentHeader(int m,int n){
+ return new XGBVerHeaderImpl(m,n);
+ }
+}
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/XGBVerHeader.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/XGBVerHeader.java
new file mode 100644
index 0000000..544b070
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/XGBVerHeader.java
@@ -0,0 +1,11 @@
+package cn.skcks.docking.gb28181.sip.header;
+
+import javax.sip.header.Header;
+
+public interface XGBVerHeader extends Header {
+ public void setVersion(int m, int n);
+
+ public String getVersion();
+
+ public final static String NAME = "X-GB-Ver";
+}
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/impl/XGBVerHeaderImpl.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/impl/XGBVerHeaderImpl.java
new file mode 100644
index 0000000..c76e83d
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/header/impl/XGBVerHeaderImpl.java
@@ -0,0 +1,31 @@
+package cn.skcks.docking.gb28181.sip.header.impl;
+
+import cn.skcks.docking.gb28181.sip.header.XGBVerHeader;
+import gov.nist.javax.sip.header.SIPHeader;
+
+public class XGBVerHeaderImpl extends SIPHeader implements XGBVerHeader {
+ private String version;
+
+ @Override
+ public void setVersion(int m, int n) {
+ this.version = String.format("%d.%d", m, n);
+ }
+
+ @Override
+ public String getVersion() {
+ return version;
+ }
+
+ public XGBVerHeaderImpl(int m, int n) {
+ super(NAME);
+ setVersion(m, n);
+ }
+
+ public XGBVerHeaderImpl() {
+ super(NAME);
+ }
+
+ protected StringBuilder encodeBody(StringBuilder buffer) {
+ return buffer.append(version);
+ }
+}
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/request/SipRequestBuilder.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/request/SipRequestBuilder.java
new file mode 100644
index 0000000..2caf882
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/request/SipRequestBuilder.java
@@ -0,0 +1,7 @@
+package cn.skcks.docking.gb28181.sip.request;
+
+import javax.sip.SipFactory;
+
+public class SipRequestBuilder {
+
+}
diff --git a/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/response/SipResponseBuilder.java b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/response/SipResponseBuilder.java
new file mode 100644
index 0000000..83d8ec1
--- /dev/null
+++ b/gb28181-sip/src/main/java/cn/skcks/docking/gb28181/sip/response/SipResponseBuilder.java
@@ -0,0 +1,7 @@
+package cn.skcks.docking.gb28181.sip.response;
+
+import javax.sip.SipFactory;
+
+public class SipResponseBuilder {
+
+}
diff --git a/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sdp/SdpTest.java b/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sdp/SdpTest.java
new file mode 100644
index 0000000..9ebd8e1
--- /dev/null
+++ b/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sdp/SdpTest.java
@@ -0,0 +1,46 @@
+package cn.skcks.docking.gb28181.sdp;
+
+import gov.nist.javax.sdp.SessionDescriptionImpl;
+import gov.nist.javax.sdp.fields.TimeField;
+import lombok.SneakyThrows;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Test;
+
+import javax.sdp.SdpFactory;
+import javax.sdp.TimeDescription;
+import java.util.Vector;
+
+@Slf4j
+public class SdpTest {
+ @Test
+ @SneakyThrows
+ public void test(){
+ String domain = "4405010000";
+ TimeField timeField = new TimeField();
+ timeField.setZero();
+ TimeDescription timeDescription = SdpFactory.getInstance().createTimeDescription(timeField);
+ GB28181Description gb28181Description = new GB28181Description();
+ gb28181Description.setTimeDescriptions(new Vector<>() {{
+ add(timeDescription);
+ }});
+ gb28181Description.setSsrcField(new SsrcField(String.format("%s%04d", domain.substring(3, 8), 1)));
+ log.info("gb28181 sdp \n{}", gb28181Description);
+
+ GB28181DescriptionParser gb28181DescriptionParser = new GB28181DescriptionParser(gb28181Description.toString());
+ GB28181Description parse = gb28181DescriptionParser.parse();
+ log.info("gb28181 sdp 解析\n{}",parse);
+
+ SessionDescriptionImpl sessionDescription = gb28181Description;
+ sessionDescription.setTimeDescriptions(new Vector<>() {{
+ add(timeDescription);
+ }});
+ gb28181Description = new GB28181Description(sessionDescription);
+ gb28181Description.setTimeDescriptions(new Vector<>() {{
+ add(timeDescription);
+ }});
+ log.info("从 SessionDescriptionImpl 转换为 gb28181 sdp \n{}", gb28181Description);
+ gb28181DescriptionParser = new GB28181DescriptionParser(gb28181Description.toString());
+ parse = gb28181DescriptionParser.parse();
+ log.info("从 SessionDescriptionImpl 转换为 gb28181 sdp 解析\n{}",parse);
+ }
+}
diff --git a/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sip/SipTest.java b/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sip/SipTest.java
new file mode 100644
index 0000000..0edc093
--- /dev/null
+++ b/gb28181-sip/src/test/java/cn/skcks/docking/gb28181/sip/SipTest.java
@@ -0,0 +1,23 @@
+package cn.skcks.docking.gb28181.sip;
+
+import cn.skcks.docking.gb28181.sip.generic.SipBuilder;
+import lombok.extern.slf4j.Slf4j;
+import org.junit.jupiter.api.Test;
+
+import javax.sip.address.Address;
+import javax.sip.address.SipURI;
+
+@Slf4j
+public class SipTest {
+ @Test
+ public void test(){
+ String localIp = "127.0.0.1";
+ int localPort = 5060;
+ String localId = "12345678901234567890";
+ String localHostAddress = SipBuilder.createHostAddress(localIp,localPort);
+ SipURI localSipUri = SipBuilder.createSipURI(localId, localHostAddress);
+
+ Address localAddress = SipBuilder.createAddress(localSipUri);
+ log.info("{}", localAddress);
+ }
+}
diff --git a/pom.xml b/pom.xml
index 8d4f551..b34f256 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,6 +22,7 @@
gb28181-service
orm
zlmediakit-service
+ gb28181-sip
@@ -59,6 +60,8 @@
1.4.13
+
+ 1.3.0-91
@@ -97,6 +100,12 @@
${project.version}
+
+ cn.skcks.docking.gb28181
+ gb28181-sip
+ ${project.version}
+
+
cn.skcks.docking.gb28181
annotation
@@ -121,6 +130,13 @@
${project.version}
+
+
+ javax.sip
+ jain-sip-ri
+ ${jain-sip.version}
+
+
org.mapstruct