From 87acd73efcde49cb5630c266db4ebd3f6b1b9dfd Mon Sep 17 00:00:00 2001
From: panlinlin <648540858@qq.com>
Date: 星期二, 06 四月 2021 15:13:40 +0800
Subject: [PATCH] Merge remote-tracking branch 'origin/wvp-28181-2.0' into wvp-28181-2.0
---
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java | 400 ++++++++++++++++++++++++++++++++++++--------------------
1 files changed, 254 insertions(+), 146 deletions(-)
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
index ac19d6f..9db7a29 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/request/impl/InviteRequestProcessor.java
@@ -7,18 +7,12 @@
import javax.sip.SipFactory;
import javax.sip.address.Address;
import javax.sip.address.SipURI;
-import javax.sip.header.ContentTypeHeader;
-import javax.sip.header.FromHeader;
-import javax.sip.header.HeaderFactory;
-import javax.sip.header.SubjectHeader;
+import javax.sip.header.*;
import javax.sip.message.Request;
import javax.sip.message.Response;
-import com.alibaba.fastjson.JSONObject;
import com.genersoft.iot.vmp.conf.MediaServerConfig;
-import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
-import com.genersoft.iot.vmp.gb28181.bean.SendRtpItem;
+import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommanderFroPlatform;
import com.genersoft.iot.vmp.gb28181.transmit.request.SIPRequestAbstractProcessor;
@@ -26,18 +20,13 @@
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorager;
import com.genersoft.iot.vmp.vmanager.play.bean.PlayResult;
-import com.genersoft.iot.vmp.vmanager.service.IPlayService;
-import gov.nist.javax.sdp.fields.SDPFormat;
+import com.genersoft.iot.vmp.service.IPlayService;
import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.SipUri;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.springframework.beans.factory.annotation.Autowired;
-import java.io.IOException;
import java.text.ParseException;
-import java.util.List;
-import java.util.UUID;
import java.util.Vector;
/**
@@ -45,6 +34,7 @@
* @author: panll
* @date: 2021骞�1鏈�14鏃�
*/
+@SuppressWarnings("rawtypes")
public class InviteRequestProcessor extends SIPRequestAbstractProcessor {
private final static Logger logger = LoggerFactory.getLogger(MessageRequestProcessor.class);
@@ -82,155 +72,272 @@
Request request = evt.getRequest();
SipURI sipURI = (SipURI) request.getRequestURI();
String channelId = sipURI.getUser();
- String platformId = null;
-// SubjectHeader subjectHeader = (SubjectHeader)request.getHeader(SubjectHeader.NAME);
-// // 鏌ヨ閫氶亾鏄惁瀛樺湪 涓嶅瓨鍦ㄥ洖澶�404
-// if (subjectHeader != null) { // 瀛樺湪鍒欎粠subjectHeader 鑾峰彇骞冲彴淇℃伅
-// String subject = subjectHeader.getSubject();
-// if (subject != null) {
-// String[] info1 = subject.split(",");
-// if (info1 != null && info1 .length == 2) {
-// String[] info2 = info1[1].split(":");
-// if (info2 != null && info2.length == 2) {
-// platformId = info2[0];
-// }
-// }
-// }
-// }
+ String requesterId = null;
FromHeader fromHeader = (FromHeader)request.getHeader(FromHeader.NAME);
AddressImpl address = (AddressImpl) fromHeader.getAddress();
SipUri uri = (SipUri) address.getURI();
- platformId = uri.getUser();
+ requesterId = uri.getUser();
- if (platformId == null || channelId == null) {
- logger.info("鏃犳硶浠嶧romHeader鐨凙ddress涓幏鍙栧埌骞冲彴id锛岃繑鍥�404");
+ if (requesterId == null || channelId == null) {
+ logger.info("鏃犳硶浠嶧romHeader鐨凙ddress涓幏鍙栧埌骞冲彴id锛岃繑鍥�400");
responseAck(evt, Response.BAD_REQUEST); // 鍙傛暟涓嶅叏锛� 鍙�400锛岃姹傞敊璇�
return;
}
- // 鏌ヨ骞冲彴涓嬫槸鍚︽湁璇ラ�氶亾
- DeviceChannel channel = storager.queryChannelInParentPlatform(platformId, channelId);
- if (channel == null) {
- logger.info("閫氶亾涓嶅瓨鍦紝杩斿洖404");
- responseAck(evt, Response.NOT_FOUND); // 閫氶亾涓嶅瓨鍦紝鍙�404锛岃祫婧愪笉瀛樺湪
- return;
- }else {
- responseAck(evt, Response.TRYING); // 閫氶亾瀛樺湪锛屽彂100锛宼rying
- }
- // 瑙f瀽sdp娑堟伅, 浣跨敤jainsip 鑷甫鐨剆dp瑙f瀽鏂瑰紡
- String contentString = new String(request.getRawContent());
- // jainSip涓嶆敮鎸亂=瀛楁锛� 绉婚櫎绉婚櫎浠ヨВ鏋愩��
- int ssrcIndex = contentString.indexOf("y=");
- String ssrc = contentString.substring(ssrcIndex + 2, contentString.length())
- .replace("\r\n", "").replace("\n", "");
+ // 鏌ヨ璇锋眰鏂规槸鍚︿笂绾у钩鍙�
+ ParentPlatform platform = storager.queryParentPlatById(requesterId);
+ if (platform != null) {
+ // 鏌ヨ骞冲彴涓嬫槸鍚︽湁璇ラ�氶亾
+ DeviceChannel channel = storager.queryChannelInParentPlatform(requesterId, channelId);
+ GbStream gbStream = storager.queryStreamInParentPlatform(requesterId, channelId);
+ // 涓嶆槸閫氶亾鍙兘鏄洿鎾祦
+ if (channel != null || gbStream != null ) {
+ responseAck(evt, Response.CALL_IS_BEING_FORWARDED); // 閫氶亾瀛樺湪锛屽彂181锛屽懠鍙浆鎺ヤ腑
+ }else {
+ logger.info("閫氶亾涓嶅瓨鍦紝杩斿洖404");
+ responseAck(evt, Response.NOT_FOUND); // 閫氶亾涓嶅瓨鍦紝鍙�404锛岃祫婧愪笉瀛樺湪
+ return;
+ }
+ // 瑙f瀽sdp娑堟伅, 浣跨敤jainsip 鑷甫鐨剆dp瑙f瀽鏂瑰紡
+ String contentString = new String(request.getRawContent());
- String substring = contentString.substring(0, contentString.indexOf("y="));
- SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
+ // jainSip涓嶆敮鎸亂=瀛楁锛� 绉婚櫎绉婚櫎浠ヨВ鏋愩��
+ int ssrcIndex = contentString.indexOf("y=");
+ //ssrc瑙勫畾闀垮害涓�10瀛楄妭锛屼笉鍙栦綑涓嬮暱搴︿互閬垮厤鍚庣画杩樻湁鈥渇=鈥濆瓧娈�
+ String ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+ String substring = contentString.substring(0, contentString.indexOf("y="));
+ SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
- // 鑾峰彇鏀寔鐨勬牸寮�
- Vector mediaDescriptions = sdp.getMediaDescriptions(true);
- // 鏌ョ湅鏄惁鏀寔PS 璐熻浇96
- String ip = null;
- int port = -1;
- boolean recvonly = false;
- boolean mediaTransmissionTCP = false;
- Boolean tcpActive = null;
- for (int i = 0; i < mediaDescriptions.size(); i++) {
- MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);
- Media media = mediaDescription.getMedia();
+ // 鑾峰彇鏀寔鐨勬牸寮�
+ Vector mediaDescriptions = sdp.getMediaDescriptions(true);
+ // 鏌ョ湅鏄惁鏀寔PS 璐熻浇96
+ //String ip = null;
+ int port = -1;
+ //boolean recvonly = false;
+ boolean mediaTransmissionTCP = false;
+ Boolean tcpActive = null;
+ for (int i = 0; i < mediaDescriptions.size(); i++) {
+ MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);
+ Media media = mediaDescription.getMedia();
- Vector mediaFormats = media.getMediaFormats(false);
- if (mediaFormats.contains("96")) {
- port = media.getMediaPort();
- String mediaType = media.getMediaType();
- String protocol = media.getProtocol();
+ Vector mediaFormats = media.getMediaFormats(false);
+ if (mediaFormats.contains("96")) {
+ port = media.getMediaPort();
+ //String mediaType = media.getMediaType();
+ String protocol = media.getProtocol();
- // 鍖哄垎TCP鍙戞祦杩樻槸udp锛� 褰撳墠榛樿udp
- if ("TCP/RTP/AVP".equals(protocol)) {
- String setup = mediaDescription.getAttribute("setup");
- if (setup != null) {
- mediaTransmissionTCP = true;
- if ("active".equals(setup)) {
- tcpActive = true;
- }else if ("passive".equals(setup)) {
- tcpActive = false;
+ // 鍖哄垎TCP鍙戞祦杩樻槸udp锛� 褰撳墠榛樿udp
+ if ("TCP/RTP/AVP".equals(protocol)) {
+ String setup = mediaDescription.getAttribute("setup");
+ if (setup != null) {
+ mediaTransmissionTCP = true;
+ if ("active".equals(setup)) {
+ tcpActive = true;
+ }else if ("passive".equals(setup)) {
+ tcpActive = false;
+ }
}
}
+ break;
}
- break;
+ }
+ if (port == -1) {
+ logger.info("涓嶆敮鎸佺殑濯掍綋鏍煎紡锛岃繑鍥�415");
+ // 鍥炲涓嶆敮鎸佺殑鏍煎紡
+ responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 涓嶆敮鎸佺殑鏍煎紡锛屽彂415
+ return;
+ }
+ String username = sdp.getOrigin().getUsername();
+ String addressStr = sdp.getOrigin().getAddress();
+ //String sessionName = sdp.getSessionName().getValue();
+ logger.info("[涓婄骇鐐规挱]鐢ㄦ埛锛歿}锛� 鍦板潃锛歿}:{}锛� ssrc锛歿}", username, addressStr, port, ssrc);
+ Device device = null;
+ // 閫氳繃 channel 鍜� gbStream 鏄惁涓簄ull 鍊煎垽鏂潵婧愭槸鐩存挱娴佸悎閫傚浗鏍�
+ if (channel != null) {
+ device = storager.queryVideoDeviceByPlatformIdAndChannelId(requesterId, channelId);
+ if (device == null) {
+ logger.warn("鐐规挱骞冲彴{}鐨勯�氶亾{}鏃舵湭鎵惧埌璁惧淇℃伅", requesterId, channel);
+ responseAck(evt, Response.SERVER_INTERNAL_ERROR);
+ return;
+ }
+ SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(addressStr, port, ssrc, requesterId,
+ device.getDeviceId(), channelId,
+ mediaTransmissionTCP);
+ if (tcpActive != null) {
+ sendRtpItem.setTcpActive(tcpActive);
+ }
+ if (sendRtpItem == null) {
+ logger.warn("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
+ responseAck(evt, Response.BUSY_HERE);
+ return;
+ }
+
+ // 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
+ redisCatchStorage.updateSendRTPSever(sendRtpItem);
+ // 閫氱煡涓嬬骇鎺ㄦ祦锛�
+ PlayResult playResult = playService.play(device.getDeviceId(), channelId, (responseJSON)->{
+ // 鏀跺埌鎺ㄦ祦锛� 鍥炲200OK, 绛夊緟ack
+ if (sendRtpItem == null) return;
+ sendRtpItem.setStatus(1);
+ redisCatchStorage.updateSendRTPSever(sendRtpItem);
+ // TODO 娣诲姞瀵箃cp鐨勬敮鎸�
+ MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
+ StringBuffer content = new StringBuffer(200);
+ content.append("v=0\r\n");
+ content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n");
+ content.append("s=Play\r\n");
+ content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n");
+ content.append("t=0 0\r\n");
+ content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
+ content.append("a=sendonly\r\n");
+ content.append("a=rtpmap:96 PS/90000\r\n");
+ content.append("y="+ ssrc + "\r\n");
+ content.append("f=\r\n");
+
+ try {
+ responseAck(evt, content.toString());
+ } catch (SipException e) {
+ e.printStackTrace();
+ } catch (InvalidArgumentException e) {
+ e.printStackTrace();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ } ,(event -> {
+ // 鏈煡閿欒銆傜洿鎺ヨ浆鍙戣澶囩偣鎾殑閿欒
+ Response response = null;
+ try {
+ response = getMessageFactory().createResponse(event.getResponse().getStatusCode(), evt.getRequest());
+ getServerTransaction(evt).sendResponse(response);
+ } catch (ParseException | SipException | InvalidArgumentException e) {
+ e.printStackTrace();
+ }
+ }));
+ if (logger.isDebugEnabled()) {
+ logger.debug(playResult.getResult().toString());
+ }
+
+ }else if (gbStream != null) {
+ SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(addressStr, port, ssrc, requesterId,
+ gbStream.getApp(), gbStream.getStream(), channelId,
+ mediaTransmissionTCP);
+
+ if (tcpActive != null) {
+ sendRtpItem.setTcpActive(tcpActive);
+ }
+ if (sendRtpItem == null) {
+ logger.warn("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
+ responseAck(evt, Response.BUSY_HERE);
+ return;
+ }
+
+ // 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
+ redisCatchStorage.updateSendRTPSever(sendRtpItem);
+
+ // 妫�娴嬬洿鎾祦鏄惁鍦ㄧ嚎
+ Boolean streamReady = zlmrtpServerFactory.isStreamReady(gbStream.getApp(), gbStream.getStream());
+ if (streamReady) {
+ sendRtpItem.setStatus(1);
+ redisCatchStorage.updateSendRTPSever(sendRtpItem);
+ // TODO 娣诲姞瀵箃cp鐨勬敮鎸�
+ MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
+ StringBuffer content = new StringBuffer(200);
+ content.append("v=0\r\n");
+ content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n");
+ content.append("s=Play\r\n");
+ content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n");
+ content.append("t=0 0\r\n");
+ content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
+ content.append("a=sendonly\r\n");
+ content.append("a=rtpmap:96 PS/90000\r\n");
+ content.append("y="+ ssrc + "\r\n");
+ content.append("f=\r\n");
+
+ try {
+ responseAck(evt, content.toString());
+ } catch (SipException e) {
+ e.printStackTrace();
+ } catch (InvalidArgumentException e) {
+ e.printStackTrace();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ } else {
+ // 闈炰笂绾у钩鍙拌姹傦紝鏌ヨ鏄惁璁惧璇锋眰锛堥�氬父涓烘帴鏀惰闊冲箍鎾殑璁惧锛�
+ Device device = storager.queryVideoDevice(requesterId);
+ if (device != null) {
+ logger.info("鏀跺埌璁惧" + requesterId + "鐨勮闊冲箍鎾璉nvite璇锋眰");
+ responseAck(evt, Response.TRYING);
+
+ String contentString = new String(request.getRawContent());
+ // jainSip涓嶆敮鎸亂=瀛楁锛� 绉婚櫎绉婚櫎浠ヨВ鏋愩��
+ String substring = contentString;
+ String ssrc = "0000000404";
+ int ssrcIndex = contentString.indexOf("y=");
+ if (ssrcIndex > 0) {
+ substring = contentString.substring(0, ssrcIndex);
+ ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+ }
+ ssrcIndex = substring.indexOf("f=");
+ if (ssrcIndex > 0) {
+ substring = contentString.substring(0, ssrcIndex);
+ }
+ SessionDescription sdp = SdpFactory.getInstance().createSessionDescription(substring);
+
+ // 鑾峰彇鏀寔鐨勬牸寮�
+ Vector mediaDescriptions = sdp.getMediaDescriptions(true);
+ // 鏌ョ湅鏄惁鏀寔PS 璐熻浇96
+ int port = -1;
+ //boolean recvonly = false;
+ boolean mediaTransmissionTCP = false;
+ Boolean tcpActive = null;
+ for (int i = 0; i < mediaDescriptions.size(); i++) {
+ MediaDescription mediaDescription = (MediaDescription)mediaDescriptions.get(i);
+ Media media = mediaDescription.getMedia();
+
+ Vector mediaFormats = media.getMediaFormats(false);
+ if (mediaFormats.contains("8")) {
+ port = media.getMediaPort();
+ String protocol = media.getProtocol();
+ // 鍖哄垎TCP鍙戞祦杩樻槸udp锛� 褰撳墠榛樿udp
+ if ("TCP/RTP/AVP".equals(protocol)) {
+ String setup = mediaDescription.getAttribute("setup");
+ if (setup != null) {
+ mediaTransmissionTCP = true;
+ if ("active".equals(setup)) {
+ tcpActive = true;
+ } else if ("passive".equals(setup)) {
+ tcpActive = false;
+ }
+ }
+ }
+ break;
+ }
+ }
+ if (port == -1) {
+ logger.info("涓嶆敮鎸佺殑濯掍綋鏍煎紡锛岃繑鍥�415");
+ // 鍥炲涓嶆敮鎸佺殑鏍煎紡
+ responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 涓嶆敮鎸佺殑鏍煎紡锛屽彂415
+ return;
+ }
+ String username = sdp.getOrigin().getUsername();
+ String addressStr = sdp.getOrigin().getAddress();
+ logger.info("璁惧{}璇锋眰璇煶娴侊紝鍦板潃锛歿}:{}锛宻src锛歿}", username, addressStr, port, ssrc);
+
+
+
+
+
+
+ } else {
+ logger.warn("鏉ヨ嚜鏃犳晥璁惧/骞冲彴鐨勮姹�");
+ responseAck(evt, Response.BAD_REQUEST);
}
}
- if (port == -1) {
- logger.info("涓嶆敮鎸佺殑濯掍綋鏍煎紡锛岃繑鍥�415");
- // 鍥炲涓嶆敮鎸佺殑鏍煎紡
- responseAck(evt, Response.UNSUPPORTED_MEDIA_TYPE); // 涓嶆敮鎸佺殑鏍煎紡锛屽彂415
- return;
- }
- String username = sdp.getOrigin().getUsername();
- String addressStr = sdp.getOrigin().getAddress();
- String sessionName = sdp.getSessionName().getValue();
- logger.info("[涓婄骇鐐规挱]鐢ㄦ埛锛歿}锛� 鍦板潃锛歿}:{}锛� ssrc锛歿}", username, addressStr, port, ssrc);
-
- Device device = storager.queryVideoDeviceByPlatformIdAndChannelId(platformId, channelId);
- if (device == null) {
- logger.warn("鐐规挱骞冲彴{}鐨勯�氶亾{}鏃舵湭鎵惧埌璁惧淇℃伅", platformId, channel);
- responseAck(evt, Response.SERVER_INTERNAL_ERROR);
- return;
- }
- SendRtpItem sendRtpItem = zlmrtpServerFactory.createSendRtpItem(ip, port, platformId, ssrc, channelId,
- mediaTransmissionTCP);
- if (tcpActive != null) {
- sendRtpItem.setTcpActive(tcpActive);
- }
- if (sendRtpItem == null) {
- logger.warn("鏈嶅姟鍣ㄧ鍙h祫婧愪笉瓒�");
- responseAck(evt, Response.BUSY_HERE);
- return;
- }
-
- // 鍐欏叆redis锛� 瓒呮椂鏃跺洖澶�
- redisCatchStorage.updateSendRTPSever(sendRtpItem);
- // 閫氱煡涓嬬骇鎺ㄦ祦锛�
- PlayResult playResult = playService.play(device.getDeviceId(), channelId, (responseJSON)->{
- // 鏀跺埌鎺ㄦ祦锛� 鍥炲200OK, 绛夊緟ack
- sendRtpItem.setStatus(1);
- redisCatchStorage.updateSendRTPSever(sendRtpItem);
- // TODO 娣诲姞瀵箃cp鐨勬敮鎸�
- MediaServerConfig mediaInfo = redisCatchStorage.getMediaInfo();
- StringBuffer content = new StringBuffer(200);
- content.append("v=0\r\n");
- content.append("o="+"00000"+" 0 0 IN IP4 "+mediaInfo.getWanIp()+"\r\n");
- content.append("s=Play\r\n");
- content.append("c=IN IP4 "+mediaInfo.getWanIp()+"\r\n");
- content.append("t=0 0\r\n");
- content.append("m=video "+ sendRtpItem.getLocalPort()+" RTP/AVP 96\r\n");
- content.append("a=sendonly\r\n");
- content.append("a=rtpmap:96 PS/90000\r\n");
- content.append("y="+ ssrc + "\r\n");
- content.append("f=\r\n");
-
- try {
- responseAck(evt, content.toString());
- } catch (SipException e) {
- e.printStackTrace();
- } catch (InvalidArgumentException e) {
- e.printStackTrace();
- } catch (ParseException e) {
- e.printStackTrace();
- }
- },(event -> {
- // 鏈煡閿欒銆傜洿鎺ヨ浆鍙戣澶囩偣鎾殑閿欒
- Response response = null;
- try {
- response = getMessageFactory().createResponse(event.getResponse().getStatusCode(), evt.getRequest());
- getServerTransaction(evt).sendResponse(response);
-
- } catch (ParseException | SipException | InvalidArgumentException e) {
- e.printStackTrace();
- }
- }));
- playResult.getResult();
} catch (SipException | InvalidArgumentException | ParseException e) {
e.printStackTrace();
@@ -242,6 +349,7 @@
e.printStackTrace();
}
}
+
/***
* 鍥炲鐘舵�佺爜
@@ -255,7 +363,7 @@
* @throws ParseException
*/
private void responseAck(RequestEvent evt, int statusCode) throws SipException, InvalidArgumentException, ParseException {
- Response response = getMessageFactory().createResponse(Response.TRYING, evt.getRequest());
+ Response response = getMessageFactory().createResponse(statusCode, evt.getRequest());
getServerTransaction(evt).sendResponse(response);
}
--
Gitblit v1.8.0