2017-12-19 14:55:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
*@Title: SaveLogThread.java
|
|
|
|
|
|
*@Package com.nis.web.service.logthread
|
|
|
|
|
|
*@Description TODO
|
|
|
|
|
|
*@author dell
|
|
|
|
|
|
*@date 2016年10月14日 下午6:26:41
|
|
|
|
|
|
*@version 版本号
|
|
|
|
|
|
*/
|
|
|
|
|
|
package com.nis.web.service;
|
|
|
|
|
|
|
|
|
|
|
|
import java.io.BufferedReader;
|
2018-07-10 11:30:37 +08:00
|
|
|
|
import java.io.ByteArrayInputStream;
|
|
|
|
|
|
import java.io.ByteArrayOutputStream;
|
2017-12-19 14:55:52 +08:00
|
|
|
|
import java.io.IOException;
|
2018-07-10 11:30:37 +08:00
|
|
|
|
import java.io.InputStream;
|
|
|
|
|
|
import java.io.InputStreamReader;
|
|
|
|
|
|
import java.nio.charset.Charset;
|
2017-12-19 14:55:52 +08:00
|
|
|
|
import java.util.Date;
|
|
|
|
|
|
|
2018-07-10 11:30:37 +08:00
|
|
|
|
import javax.servlet.ServletInputStream;
|
|
|
|
|
|
import javax.servlet.ServletRequest;
|
2017-12-19 14:55:52 +08:00
|
|
|
|
import javax.servlet.http.HttpServletRequest;
|
|
|
|
|
|
|
|
|
|
|
|
import org.apache.log4j.Logger;
|
|
|
|
|
|
|
|
|
|
|
|
import com.nis.datasource.CustomerContextHolder;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
2018-06-10 16:18:34 +08:00
|
|
|
|
* 审计日志工作线程
|
2017-12-19 14:55:52 +08:00
|
|
|
|
* @ClassName: SaveLogThread.java
|
2018-06-10 16:18:34 +08:00
|
|
|
|
* @Description: 用于记录业务操作日志,后期用于审计及相关联调验证工作
|
2017-12-19 14:55:52 +08:00
|
|
|
|
* @author (dell)
|
|
|
|
|
|
* @date 2016年10月14日 下午6:26:41
|
|
|
|
|
|
* @version V1.0
|
|
|
|
|
|
*/
|
|
|
|
|
|
public class SaveRequestLogThread implements Runnable {
|
2018-07-10 11:30:37 +08:00
|
|
|
|
private Logger logger = Logger.getLogger(SaveRequestLogThread.class);
|
2017-12-19 14:55:52 +08:00
|
|
|
|
private ServicesRequestLogService service;
|
|
|
|
|
|
private String remoteAddr;
|
|
|
|
|
|
private String requestURI;
|
|
|
|
|
|
private String queryString;
|
|
|
|
|
|
private String contextPath;
|
|
|
|
|
|
private String operator;
|
|
|
|
|
|
private String version;
|
|
|
|
|
|
private int opAction;
|
|
|
|
|
|
private Date opTime;
|
|
|
|
|
|
private Object content;
|
|
|
|
|
|
private Date requestTime;
|
|
|
|
|
|
private long consumerTime;
|
|
|
|
|
|
private int businessCode;
|
|
|
|
|
|
private String exceptionInfo;
|
2018-05-19 11:30:50 +08:00
|
|
|
|
private String traceCode;
|
2018-07-10 11:30:37 +08:00
|
|
|
|
/*
|
|
|
|
|
|
* (non-Javadoc)
|
|
|
|
|
|
*
|
2017-12-19 14:55:52 +08:00
|
|
|
|
* @see java.lang.Runnable#run()
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
@Override
|
|
|
|
|
|
public void run() {
|
|
|
|
|
|
logger.info("线程开始执行!");
|
2018-07-10 11:30:37 +08:00
|
|
|
|
// 新开线程切换数据源,不影响action中的数据源
|
|
|
|
|
|
CustomerContextHolder.setCustomerType(CustomerContextHolder.DATA_SOURCE_B);// 开启数据源B
|
2017-12-19 14:55:52 +08:00
|
|
|
|
// TODO Auto-generated method stub
|
2018-07-10 11:30:37 +08:00
|
|
|
|
if (service != null) {
|
|
|
|
|
|
service.saveRequestLog(remoteAddr, requestURI, queryString, contextPath, operator, version, opAction,
|
|
|
|
|
|
opTime, content, requestTime, consumerTime, businessCode, exceptionInfo, traceCode);
|
|
|
|
|
|
} else {
|
2017-12-19 14:55:52 +08:00
|
|
|
|
logger.error("service 为空!");
|
|
|
|
|
|
}
|
|
|
|
|
|
CustomerContextHolder.clearCustomerType();
|
|
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
|
|
|
|
|
public SaveRequestLogThread() {
|
2017-12-19 14:55:52 +08:00
|
|
|
|
super();
|
|
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 创建一个新的实例 SaveRequestLogThread.
|
|
|
|
|
|
*
|
|
|
|
|
|
* @param service
|
|
|
|
|
|
* @param requestURI
|
|
|
|
|
|
* @param queryString
|
|
|
|
|
|
* @param contextPath
|
|
|
|
|
|
* @param operator
|
|
|
|
|
|
* @param version
|
|
|
|
|
|
* @param opAction
|
|
|
|
|
|
* @param opTime
|
|
|
|
|
|
* @param content
|
|
|
|
|
|
* @param requestTime
|
|
|
|
|
|
* @param consumerTime
|
|
|
|
|
|
*/
|
|
|
|
|
|
public SaveRequestLogThread(ServicesRequestLogService service, String remoteAddr, String requestURI,
|
|
|
|
|
|
String queryString, String contextPath, String operator, String version, int opAction, Date opTime,
|
2018-07-10 11:30:37 +08:00
|
|
|
|
Object content, Date requestTime, long consumerTime, String exceptionInfo) {
|
2017-12-19 14:55:52 +08:00
|
|
|
|
super();
|
|
|
|
|
|
this.service = service;
|
|
|
|
|
|
this.remoteAddr = remoteAddr;
|
|
|
|
|
|
this.requestURI = requestURI;
|
|
|
|
|
|
this.queryString = queryString;
|
|
|
|
|
|
this.contextPath = contextPath;
|
|
|
|
|
|
this.operator = operator;
|
|
|
|
|
|
this.version = version;
|
|
|
|
|
|
this.opAction = opAction;
|
|
|
|
|
|
this.opTime = opTime;
|
|
|
|
|
|
this.content = content;
|
|
|
|
|
|
this.requestTime = requestTime;
|
|
|
|
|
|
this.consumerTime = consumerTime;
|
2018-07-10 11:30:37 +08:00
|
|
|
|
this.exceptionInfo = exceptionInfo;
|
2017-12-19 14:55:52 +08:00
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
|
|
|
|
|
public SaveRequestLogThread(ServicesRequestLogService service, String operator, String version, int opAction,
|
|
|
|
|
|
Date opTime, Object content, Date requestTime, long consumerTime, String exceptionInfo) {
|
2017-12-19 14:55:52 +08:00
|
|
|
|
super();
|
|
|
|
|
|
this.service = service;
|
|
|
|
|
|
this.operator = operator;
|
|
|
|
|
|
this.version = version;
|
|
|
|
|
|
this.opAction = opAction;
|
|
|
|
|
|
this.opTime = opTime;
|
|
|
|
|
|
this.content = content;
|
|
|
|
|
|
this.requestTime = requestTime;
|
|
|
|
|
|
this.consumerTime = consumerTime;
|
2018-07-10 11:30:37 +08:00
|
|
|
|
this.exceptionInfo = exceptionInfo;
|
2017-12-19 14:55:52 +08:00
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
|
|
|
|
|
public static InputStream cloneInputStream(ServletInputStream inputStream) {
|
|
|
|
|
|
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
|
|
|
|
|
|
byte[] buffer = new byte[1024];
|
|
|
|
|
|
int len;
|
|
|
|
|
|
try {
|
|
|
|
|
|
while ((len = inputStream.read(buffer)) > -1) {
|
|
|
|
|
|
byteArrayOutputStream.write(buffer, 0, len);
|
|
|
|
|
|
}
|
|
|
|
|
|
byteArrayOutputStream.flush();
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
}
|
|
|
|
|
|
InputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
|
|
|
|
|
|
return byteArrayInputStream;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取非get请求的参数
|
|
|
|
|
|
* @param request
|
|
|
|
|
|
* @return
|
|
|
|
|
|
*/
|
|
|
|
|
|
private static String getBodyString(ServletRequest request) {
|
|
|
|
|
|
StringBuilder sb = new StringBuilder();
|
|
|
|
|
|
InputStream inputStream = null;
|
|
|
|
|
|
BufferedReader reader = null;
|
|
|
|
|
|
try {
|
|
|
|
|
|
inputStream = cloneInputStream(request.getInputStream());
|
|
|
|
|
|
reader = new BufferedReader(new InputStreamReader(inputStream, Charset.forName("UTF-8")));
|
|
|
|
|
|
String line = "";
|
|
|
|
|
|
while ((line = reader.readLine()) != null) {
|
|
|
|
|
|
sb.append(line);
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
} finally {
|
|
|
|
|
|
if (inputStream != null) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
inputStream.close();
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (reader != null) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
reader.close();
|
|
|
|
|
|
} catch (IOException e) {
|
|
|
|
|
|
e.printStackTrace();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return sb.toString();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static SaveRequestLogThread getNewSaveRequestLogThread(HttpServletRequest request) {
|
|
|
|
|
|
SaveRequestLogThread thread = new SaveRequestLogThread();
|
2017-12-19 14:55:52 +08:00
|
|
|
|
thread.setRemoteAddr(request.getRemoteAddr());
|
|
|
|
|
|
thread.setRequestURI(request.getRequestURI());
|
2018-07-10 11:30:37 +08:00
|
|
|
|
if (request.getMethod().toLowerCase().equals("get")) {
|
|
|
|
|
|
thread.setQueryString(request.getQueryString());
|
2018-07-18 11:05:54 +08:00
|
|
|
|
thread.setContent(request.getQueryString());
|
2018-07-10 11:30:37 +08:00
|
|
|
|
} else {
|
2018-07-10 17:37:41 +08:00
|
|
|
|
String contentType = request.getContentType();
|
2018-07-18 11:05:54 +08:00
|
|
|
|
if(!contentType.contains("multipart/form-data")) {
|
|
|
|
|
|
String bodyString = getBodyString(request);
|
|
|
|
|
|
thread.setQueryString(bodyString);
|
|
|
|
|
|
thread.setContent(bodyString);
|
|
|
|
|
|
}else {
|
|
|
|
|
|
//String bodyString = getBodyString(request);
|
|
|
|
|
|
thread.setQueryString("contentTyp="+contentType+",一般是上传文件,此种请求不记录请求内容");
|
|
|
|
|
|
thread.setContent("contentTyp="+contentType+",一般是上传文件,此种请求不记录请求内容");
|
2018-07-10 17:37:41 +08:00
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
}
|
2017-12-19 14:55:52 +08:00
|
|
|
|
thread.setContextPath(request.getContextPath());
|
|
|
|
|
|
thread.setRequestTime(new Date());
|
|
|
|
|
|
return thread;
|
|
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* service
|
|
|
|
|
|
* @return service
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public ServicesRequestLogService getService() {
|
|
|
|
|
|
return service;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param service the service to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setService(ServicesRequestLogService service) {
|
|
|
|
|
|
this.service = service;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* remoteAddr
|
|
|
|
|
|
* @return remoteAddr
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public String getRemoteAddr() {
|
|
|
|
|
|
return remoteAddr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param remoteAddr the remoteAddr to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setRemoteAddr(String remoteAddr) {
|
|
|
|
|
|
this.remoteAddr = remoteAddr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* requestURI
|
|
|
|
|
|
* @return requestURI
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public String getRequestURI() {
|
|
|
|
|
|
return requestURI;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param requestURI the requestURI to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setRequestURI(String requestURI) {
|
|
|
|
|
|
this.requestURI = requestURI;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* queryString
|
|
|
|
|
|
* @return queryString
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public String getQueryString() {
|
|
|
|
|
|
return queryString;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param queryString the queryString to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setQueryString(String queryString) {
|
|
|
|
|
|
this.queryString = queryString;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* contextPath
|
|
|
|
|
|
* @return contextPath
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public String getContextPath() {
|
|
|
|
|
|
return contextPath;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param contextPath the contextPath to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setContextPath(String contextPath) {
|
|
|
|
|
|
this.contextPath = contextPath;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* operator
|
|
|
|
|
|
* @return operator
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public String getOperator() {
|
|
|
|
|
|
return operator;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param operator the operator to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setOperator(String operator) {
|
|
|
|
|
|
this.operator = operator;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* version
|
|
|
|
|
|
* @return version
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public String getVersion() {
|
|
|
|
|
|
return version;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param version the version to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setVersion(String version) {
|
|
|
|
|
|
this.version = version;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* opAction
|
|
|
|
|
|
* @return opAction
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public int getOpAction() {
|
|
|
|
|
|
return opAction;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param opAction the opAction to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setOpAction(int opAction) {
|
|
|
|
|
|
this.opAction = opAction;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* opTime
|
|
|
|
|
|
* @return opTime
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public Date getOpTime() {
|
|
|
|
|
|
return opTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param opTime the opTime to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setOpTime(Date opTime) {
|
|
|
|
|
|
this.opTime = opTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* content
|
|
|
|
|
|
* @return content
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public Object getContent() {
|
|
|
|
|
|
return content;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param content the content to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setContent(Object content) {
|
|
|
|
|
|
this.content = content;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* requestTime
|
|
|
|
|
|
* @return requestTime
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public Date getRequestTime() {
|
|
|
|
|
|
return requestTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param requestTime the requestTime to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setRequestTime(Date requestTime) {
|
|
|
|
|
|
this.requestTime = requestTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* consumerTime
|
|
|
|
|
|
* @return consumerTime
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public long getConsumerTime() {
|
|
|
|
|
|
return consumerTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* @param consumerTime the consumerTime to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setConsumerTime(long consumerTime) {
|
|
|
|
|
|
this.consumerTime = consumerTime;
|
|
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* businessCode
|
|
|
|
|
|
* @return businessCode
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public int getBusinessCode() {
|
|
|
|
|
|
return businessCode;
|
|
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @param businessCode the businessCode to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setBusinessCode(int businessCode) {
|
|
|
|
|
|
this.businessCode = businessCode;
|
|
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* exceptionInfo
|
|
|
|
|
|
* @return exceptionInfo
|
|
|
|
|
|
*/
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
public String getExceptionInfo() {
|
|
|
|
|
|
return exceptionInfo;
|
|
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* @param exceptionInfo the exceptionInfo to set
|
|
|
|
|
|
*/
|
|
|
|
|
|
public void setExceptionInfo(String exceptionInfo) {
|
|
|
|
|
|
this.exceptionInfo = exceptionInfo;
|
|
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2018-05-19 11:30:50 +08:00
|
|
|
|
public String getTraceCode() {
|
|
|
|
|
|
return traceCode;
|
|
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2018-05-19 11:30:50 +08:00
|
|
|
|
public void setTraceCode(String traceCode) {
|
|
|
|
|
|
this.traceCode = traceCode;
|
|
|
|
|
|
}
|
2018-07-10 11:30:37 +08:00
|
|
|
|
|
2017-12-19 14:55:52 +08:00
|
|
|
|
}
|