对比视图

在下面输入commit id(如: 4eedf23 ) 或者分支/标签名(如: master
Diff

23 文件发生了变化, 影响行数: +1105 -40

frame-core/pom.xml
@@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
13 <packaging>jar</packaging> 13 <packaging>jar</packaging>
14 14
15 <dependencies> 15 <dependencies>
16 + <!-- spring -->
16 <dependency> 17 <dependency>
17 <groupId>org.springframework</groupId> 18 <groupId>org.springframework</groupId>
18 <artifactId>spring-aspects</artifactId> 19 <artifactId>spring-aspects</artifactId>
@@ -29,6 +30,15 @@ @@ -29,6 +30,15 @@
29 <groupId>org.springframework</groupId> 30 <groupId>org.springframework</groupId>
30 <artifactId>spring-beans</artifactId> 31 <artifactId>spring-beans</artifactId>
31 </dependency> 32 </dependency>
33 + <dependency>
34 + <groupId>org.springframework</groupId>
35 + <artifactId>spring-context</artifactId>
36 + </dependency>
37 + <dependency>
38 + <groupId>org.springframework.data</groupId>
39 + <artifactId>spring-data-redis</artifactId>
40 + </dependency>
41 + <!-- spring end -->
32 42
33 <!-- 数据库操作相关依赖包 --> 43 <!-- 数据库操作相关依赖包 -->
34 <dependency> 44 <dependency>
@@ -43,6 +53,10 @@ @@ -43,6 +53,10 @@
43 <groupId>mysql</groupId> 53 <groupId>mysql</groupId>
44 <artifactId>mysql-connector-java</artifactId> 54 <artifactId>mysql-connector-java</artifactId>
45 </dependency> 55 </dependency>
56 + <dependency>
57 + <groupId>redis.clients</groupId>
58 + <artifactId>jedis</artifactId>
59 + </dependency>
46 <!-- mybatis --> 60 <!-- mybatis -->
47 <dependency> 61 <dependency>
48 <groupId>org.mybatis</groupId> 62 <groupId>org.mybatis</groupId>
@@ -143,14 +157,6 @@ @@ -143,14 +157,6 @@
143 <groupId>info.cukes</groupId> 157 <groupId>info.cukes</groupId>
144 <artifactId>cucumber-java</artifactId> 158 <artifactId>cucumber-java</artifactId>
145 </dependency> 159 </dependency>
146 - <dependency>
147 - <groupId>org.springframework</groupId>
148 - <artifactId>spring-context</artifactId>
149 - </dependency>
150 - <dependency>
151 - <groupId>org.springframework</groupId>
152 - <artifactId>spring-context</artifactId>
153 - </dependency>
154 </dependencies> 160 </dependencies>
155 161
156 </project> 162 </project>
frame-core/src/main/java/cn/platform/agent/frame/core/aop/ServiceAspect.java
@@ -6,41 +6,45 @@ import org.slf4j.Logger; @@ -6,41 +6,45 @@ import org.slf4j.Logger;
6 import org.slf4j.LoggerFactory; 6 import org.slf4j.LoggerFactory;
7 7
8 /** 8 /**
9 + * @author Bob Lee
9 * @ClassName: ServiceAspect 10 * @ClassName: ServiceAspect
10 * @Description: 业务切面,对业务逻辑的公共处理 11 * @Description: 业务切面,对业务逻辑的公共处理
11 - * @author Bob Lee
12 * @date 2017年3月29日 下午2:22:00 12 * @date 2017年3月29日 下午2:22:00
13 */ 13 */
14 public class ServiceAspect { 14 public class ServiceAspect {
15 15
16 - private IExceptionConverter exceptionConverter; 16 + private IExceptionConverter exceptionConverter;
17 - 17 +
18 - private static final Logger logger = LoggerFactory.getLogger(ServiceAspect.class); 18 + private static final Logger logger = LoggerFactory.getLogger(ServiceAspect.class);
19 - 19 +
20 - public Object around(ProceedingJoinPoint pjp) throws Throwable { 20 + public Object around(ProceedingJoinPoint pjp) throws Throwable {
21 - 21 +
22 - try { 22 + try {
23 - return pjp.proceed(pjp.getArgs()); 23 + return pjp.proceed(pjp.getArgs());
24 - } catch (DaoException e) { 24 + } catch (DaoException e) {
25 - return this.exceptionConverter.convert(e); 25 + logger.error(e.getMessage(), e);
26 - } catch (ValidationException e) { 26 + return this.exceptionConverter.convert(e);
27 - return this.exceptionConverter.convert(e); 27 + } catch (ValidationException e) {
28 - } catch (ServiceException e) { 28 + //logger.error(e.getMessage(), e);
29 - return this.exceptionConverter.convert(e); 29 + return this.exceptionConverter.convert(e);
30 - } catch (ComponentException e) { 30 + } catch (ServiceException e) {
31 - return this.exceptionConverter.convert(e); 31 + logger.error(e.getMessage(), e);
32 - } catch (Throwable e) { 32 + return this.exceptionConverter.convert(e);
33 - logger.error(e.getMessage(), e); 33 + } catch (ComponentException e) {
34 - return this.exceptionConverter.convert(e); 34 + logger.error(e.getMessage(), e);
35 - } 35 + return this.exceptionConverter.convert(e);
36 - } 36 + } catch (Throwable e) {
37 - 37 + logger.error(e.getMessage(), e);
38 - public IExceptionConverter getExceptionConverter() { 38 + return this.exceptionConverter.convert(e);
39 - return exceptionConverter; 39 + }
40 - } 40 + }
41 - 41 +
42 - public void setExceptionConverter(IExceptionConverter exceptionConverter) { 42 + public IExceptionConverter getExceptionConverter() {
43 - this.exceptionConverter = exceptionConverter; 43 + return exceptionConverter;
44 - } 44 + }
45 +
46 + public void setExceptionConverter(IExceptionConverter exceptionConverter) {
47 + this.exceptionConverter = exceptionConverter;
48 + }
45 49
46 } 50 }
frame-core/src/main/java/cn/platform/agent/frame/core/common/ResultBean.java
@@ -45,14 +45,15 @@ public class ResultBean&lt;E&gt; { @@ -45,14 +45,15 @@ public class ResultBean&lt;E&gt; {
45 this.messageBeans = MessageBeanHelper.buildForOne(messageBean); 45 this.messageBeans = MessageBeanHelper.buildForOne(messageBean);
46 } 46 }
47 47
48 - public ResultBean(boolean success, MessageBean messageBean, E resultData) { 48 +
49 + public ResultBean(boolean success, MessageBean messageBean, E resultData) {
49 super(); 50 super();
50 this.success = success; 51 this.success = success;
51 this.messageBeans = MessageBeanHelper.buildForOne(messageBean); 52 this.messageBeans = MessageBeanHelper.buildForOne(messageBean);
52 this.resultData = resultData; 53 this.resultData = resultData;
53 } 54 }
54 55
55 - public boolean isSuccess() { 56 + public boolean isSuccess() {
56 return success; 57 return success;
57 } 58 }
58 59
frame-core/src/main/java/cn/platform/agent/frame/core/common/ResultBeanHelper.java
1 package cn.platform.agent.frame.core.common; 1 package cn.platform.agent.frame.core.common;
2 2
3 +import java.util.List;
4 +
3 public class ResultBeanHelper { 5 public class ResultBeanHelper {
4 6
5 public static ResultBean success() { 7 public static ResultBean success() {
@@ -17,4 +19,11 @@ public class ResultBeanHelper { @@ -17,4 +19,11 @@ public class ResultBeanHelper {
17 return result; 19 return result;
18 } 20 }
19 21
22 + public static ResultBean error(MessageBean messageBean){
23 + return new ResultBean(false, messageBean);
24 + }
25 +
26 + public static ResultBean error(List<MessageBean> messageBean){
27 + return new ResultBean(false, messageBean , null , null);
28 + }
20 } 29 }
frame-core/src/main/java/cn/platform/agent/frame/core/config/PropertyPlaceholder.java 0 → 100644
1 +package cn.platform.agent.frame.core.config;
2 +
3 +import org.springframework.beans.BeansException;
4 +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
5 +import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;
6 +
7 +import java.util.Properties;
8 +
9 +/**
10 + * <p>标题: 读取属性文件Property </p>
11 + * <p>功能描述: </p>
12 + * <p>创建时间:17/4/13 </p>
13 + * <p>作者:SIVEN</p>
14 + * <p>修改历史记录:</p>
15 + * ============================================================<br>
16 + */
17 +public class PropertyPlaceholder extends PropertyPlaceholderConfigurer {
18 + private Properties props; // 存取properties配置文件key-value结果
19 +
20 + @Override
21 + protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
22 + throws BeansException {
23 + super.processProperties(beanFactoryToProcess, props);
24 + this.props = props;
25 + }
26 +
27 + public String getValue(String key){
28 + return this.props.getProperty(key);
29 + }
30 +
31 + public String getValue(String key, String defaultValue) {
32 + return this.props.getProperty(key, defaultValue);
33 + }
34 +
35 + public Object setValue(String key, String value) {
36 + return this.props.setProperty(key, value);
37 + }
38 +}
frame-core/src/main/java/cn/platform/agent/frame/core/exception/AuthorizationException.java 0 → 100644
1 +package cn.platform.agent.frame.core.exception;
2 +
3 +/**
4 + * <p>标题: 定义权限异常 </p>
5 + * <p>功能描述: </p>
6 + * <p>创建时间:17/4/21 </p>
7 + * <p>作者:SIVEN</p>
8 + * <p>修改历史记录:</p>
9 + * ============================================================<br>
10 + */
11 +public class AuthorizationException extends Exception {
12 +
13 + public AuthorizationException() {
14 + super("系统拦截到用户尚未登录或登录已过期! >| 跳转到登录页面..");
15 + }
16 +
17 +
18 +}
frame-core/src/main/java/cn/platform/agent/frame/core/helper/DesHelper.java 0 → 100644
1 +package cn.platform.agent.frame.core.helper;
2 +
3 +import javax.crypto.Cipher;
4 +import java.security.Key;
5 +import java.security.Security;
6 +
7 +/**
8 + * DES加密和解密工具,可以对字符串进行加密和解密操作 。
9 + */
10 +public class DesHelper {
11 +
12 + /** 字符串默认键值 */
13 + private static String strDefaultKey = "national";
14 +
15 + /** 加密工具 */
16 + private Cipher encryptCipher = null;
17 +
18 + /** 解密工具 */
19 + private Cipher decryptCipher = null;
20 +
21 + /**
22 + * 将byte数组转换为表示16进制值的字符串, 如:byte[]{8,18}转换为:0813, 和public static byte[]
23 + * hexStr2ByteArr(String strIn) 互为可逆的转换过程
24 + *
25 + * @param arrB
26 + * 需要转换的byte数组
27 + * @return 转换后的字符串
28 + * @throws Exception
29 + * 本方法不处理任何异常,所有异常全部抛出
30 + */
31 + public static String byteArr2HexStr(byte[] arrB) throws Exception {
32 + int iLen = arrB.length;
33 + // 每个byte用两个字符才能表示,所以字符串的长度是数组长度的两倍
34 + StringBuffer sb = new StringBuffer(iLen * 2);
35 + for (int i = 0; i < iLen; i++) {
36 + int intTmp = arrB[i];
37 + // 把负数转换为正数
38 + while (intTmp < 0) {
39 + intTmp = intTmp + 256;
40 + }
41 + // 小于0F的数需要在前面补0
42 + if (intTmp < 16) {
43 + sb.append("0");
44 + }
45 + sb.append(Integer.toString(intTmp, 16));
46 + }
47 + return sb.toString();
48 + }
49 +
50 + /**
51 + * 将表示16进制值的字符串转换为byte数组, 和public static String byteArr2HexStr(byte[] arrB)
52 + * 互为可逆的转换过程
53 + *
54 + * @param strIn
55 + * 需要转换的字符串
56 + * @return 转换后的byte数组
57 + * @throws Exception
58 + * 本方法不处理任何异常,所有异常全部抛出
59 + * @author <a href="mailto:leo841001@163.com">LiGuoQing</a>
60 + */
61 + public static byte[] hexStr2ByteArr(String strIn) throws Exception {
62 + byte[] arrB = strIn.getBytes();
63 + int iLen = arrB.length;
64 +
65 + // 两个字符表示一个字节,所以字节数组长度是字符串长度除以2
66 + byte[] arrOut = new byte[iLen / 2];
67 + for (int i = 0; i < iLen; i = i + 2) {
68 + String strTmp = new String(arrB, i, 2);
69 + arrOut[i / 2] = (byte) Integer.parseInt(strTmp, 16);
70 + }
71 + return arrOut;
72 + }
73 +
74 + /**
75 + * 默认构造方法,使用默认密钥
76 + *
77 + * @throws Exception
78 + */
79 + public DesHelper() throws Exception {
80 + this(strDefaultKey);
81 + }
82 +
83 + /**
84 + * 指定密钥构造方法
85 + *
86 + * @param strKey
87 + * 指定的密钥
88 + * @throws Exception
89 + */
90 + public DesHelper(String strKey) throws Exception {
91 + Security.addProvider(new com.sun.crypto.provider.SunJCE());
92 + Key key = getKey(strKey.getBytes());
93 +
94 + encryptCipher = Cipher.getInstance("DES");
95 + encryptCipher.init(Cipher.ENCRYPT_MODE, key);
96 +
97 + decryptCipher = Cipher.getInstance("DES");
98 + decryptCipher.init(Cipher.DECRYPT_MODE, key);
99 + }
100 +
101 + /**
102 + * 加密字节数组
103 + *
104 + * @param arrB
105 + * 需加密的字节数组
106 + * @return 加密后的字节数组
107 + * @throws Exception
108 + */
109 + public byte[] encrypt(byte[] arrB) throws Exception {
110 + return encryptCipher.doFinal(arrB);
111 + }
112 +
113 + /**
114 + * 加密字符串
115 + *
116 + * @param strIn
117 + * 需加密的字符串
118 + * @return 加密后的字符串
119 + * @throws Exception
120 + */
121 + public String encrypt(String strIn) throws Exception {
122 + return byteArr2HexStr(encrypt(strIn.getBytes()));
123 + }
124 +
125 + /**
126 + * 解密字节数组
127 + *
128 + * @param arrB
129 + * 需解密的字节数组
130 + * @return 解密后的字节数组
131 + * @throws Exception
132 + */
133 + public byte[] decrypt(byte[] arrB) throws Exception {
134 + return decryptCipher.doFinal(arrB);
135 + }
136 +
137 + /**
138 + * 解密字符串
139 + *
140 + * @param strIn
141 + * 需解密的字符串
142 + * @return 解密后的字符串
143 + * @throws Exception
144 + */
145 + public String decrypt(String strIn) throws Exception {
146 + return new String(decrypt(hexStr2ByteArr(strIn)));
147 + }
148 +
149 + /**
150 + * 从指定字符串生成密钥,密钥所需的字节数组长度为8位 不足8位时后面补0,超出8位只取前8位
151 + *
152 + * @param arrBTmp
153 + * 构成该字符串的字节数组
154 + * @return 生成的密钥
155 + * @throws java.lang.Exception
156 + */
157 + private Key getKey(byte[] arrBTmp) throws Exception {
158 + // 创建一个空的8位字节数组(默认值为0)
159 + byte[] arrB = new byte[8];
160 +
161 + // 将原始字节数组转换为8位
162 + for (int i = 0; i < arrBTmp.length && i < arrB.length; i++) {
163 + arrB[i] = arrBTmp[i];
164 + }
165 +
166 + // 生成密钥
167 + Key key = new javax.crypto.spec.SecretKeySpec(arrB, "DES");
168 +
169 + return key;
170 + }
171 +
172 + /**
173 + * main方法 。
174 + * @author 刘尧兴
175 + * @param args
176 + */
177 + public static void main(String[] args) {
178 + try {
179 + String test = "123456789";
180 + DesHelper des = new DesHelper("smp");//自定义密钥
181 + System.out.println("加密前的字符:" + test);
182 + System.out.println("加密后的字符:" + des.encrypt(test));
183 + System.out.println("解密后的字符:" + des.decrypt(des.encrypt(test)));
184 + }
185 + catch (Exception e) {
186 + e.printStackTrace();
187 + }
188 + }
189 +}
frame-core/src/main/java/cn/platform/agent/frame/core/helper/ProtobuffSerializationHelper.java 0 → 100644
1 +package cn.platform.agent.frame.core.helper;
2 +
3 +import java.io.ByteArrayInputStream;
4 +import java.io.ByteArrayOutputStream;
5 +import java.util.List;
6 +import java.util.Map;
7 +import java.util.concurrent.ConcurrentHashMap;
8 +
9 +import org.springframework.objenesis.Objenesis;
10 +import org.springframework.objenesis.ObjenesisStd;
11 +
12 +
13 +/**
14 + * <p>标题: </p>
15 + * <p>功能描述: </p>
16 + * <p>创建时间:17/4/9 </p>
17 + * <p>作者:SIVEN</p>
18 + * <p>修改历史记录:</p>
19 + * ============================================================<br>
20 + */
21 +public class ProtobuffSerializationHelper {
22 +}
frame-core/src/main/java/cn/platform/agent/frame/core/helper/StackTraceHelper.java 0 → 100644
1 +package cn.platform.agent.frame.core.helper;
2 +
3 +import java.io.PrintWriter;
4 +import java.io.StringWriter;
5 +
6 +/**
7 + * <p>标题: 异常堆栈信息工具类</p>
8 + * <p>功能描述: </p>
9 + * <p>版权: 税友软件集团股份限公司</p>
10 + * <p>创建时间:16/12/22 </p>
11 + * <p>作者:SIVEN</p>
12 + * <p>修改历史记录:</p>
13 + * ============================================================<br>
14 + */
15 +public class StackTraceHelper {
16 +
17 + public static String getMessage(String message , Exception e){
18 + try {
19 + StringWriter sw = new StringWriter();
20 + PrintWriter pw = new PrintWriter(sw);
21 + e.printStackTrace(pw);
22 + String exceptionInfo = sw.toString();
23 + return message + " ,Error Message: " +exceptionInfo;
24 + } catch (Exception e1) {
25 + return "get StackTrace Message abnormal!!";
26 + }
27 + }
28 +
29 + /**
30 + * 将捕获的异常信息转String
31 + *
32 + * @param e
33 + * @return
34 + */
35 + public static String getMessage(Exception e) {
36 + try {
37 + StringWriter sw = new StringWriter();
38 + PrintWriter pw = new PrintWriter(sw);
39 + e.printStackTrace(pw);
40 + String exceptionInfo = sw.toString();
41 + return exceptionInfo;
42 + } catch (Exception e1) {
43 + return "get StackTrace Message abnormal!!";
44 + }
45 + }
46 +
47 +}
frame-core/src/main/java/cn/platform/agent/frame/core/helper/ThrowableInfoHelper.java 0 → 100644
1 +package cn.platform.agent.frame.core.helper;
2 +
3 +import org.apache.commons.logging.Log;
4 +import org.apache.commons.logging.LogFactory;
5 +
6 +import java.io.*;
7 +import java.util.ArrayList;
8 +
9 +/**
10 + * <p>标题: </p>
11 + * <p>功能描述: </p>
12 + * <p>创建时间:17/4/12 </p>
13 + * <p>作者:SIVEN</p>
14 + * <p>修改历史记录:</p>
15 + * ============================================================<br>
16 + */
17 +public class ThrowableInfoHelper {
18 +
19 + protected final Log logger = LogFactory.getLog(getClass());
20 + /**
21 + * =======================================================================
22 + * 获得操作系统中,换行字符串。如Micorsoft Windows XP专业版中换行符"\r\n"
23 + * =======================================================================
24 + */
25 + // Note that the line.separator property can be looked up even by applets.
26 + // 参考org.apache.log4j.Layout
27 + public final static String LINE_SEP = System.getProperty("line.separator");
28 + // 参考java.io.BufferedWriter
29 + public final static String LINE_SEP2 = (String) java.security.AccessController
30 + .doPrivileged(new sun.security.action.GetPropertyAction(
31 + "line.separator"));
32 +
33 + /**
34 + * @param throwable 异常对象
35 + * @return
36 + * @Description:将Throwable对象的错误堆栈内容形成字符串<br> 参考
37 + * {@link org.apache.log4j.spi.ThrowableInformation}
38 + * 代码
39 + * @author mahh
40 + * @since:2014-9-30 下午02:32:51
41 + */
42 + public static String[] getThrowableStrRep(Throwable throwable) {
43 + if (throwable == null) {
44 + return new String[0];
45 + }
46 + StringWriter sw = new StringWriter();
47 + PrintWriter pw = new PrintWriter(sw);
48 + throwable.printStackTrace(pw);
49 + pw.flush();
50 + LineNumberReader reader = new LineNumberReader(new StringReader(
51 + sw.toString()));
52 + ArrayList lines = new ArrayList();
53 + try {
54 + String line = reader.readLine();
55 + while (line != null) {
56 + lines.add(line);
57 + line = reader.readLine();
58 + }
59 + } catch (IOException ex) {
60 + lines.add(ex.toString());
61 + }
62 + String[] rep = new String[lines.size()];
63 + lines.toArray(rep);
64 + return rep;
65 + }
66 +
67 +
68 + public static String getThrowableStr(Throwable throwable) {
69 + StringBuffer throwableStr = new StringBuffer();
70 + String[] errorStrArray = getThrowableStrRep(throwable);
71 + for (int i = 0; i < errorStrArray.length; i++) {
72 + throwableStr.append(errorStrArray[i]).append(LINE_SEP2);
73 + }
74 + return throwableStr.toString();
75 + }
76 +
77 + // 测试代码
78 + public static void main(String[] args) {
79 + RuntimeException e1 = new RuntimeException("aaa");
80 + RuntimeException e2 = new RuntimeException("e2", e1);
81 + RuntimeException e3 = new RuntimeException(e2);
82 +
83 + System.out.println("=====");
84 + String throwableStr = getThrowableStr(e3);
85 + System.out.println(throwableStr);
86 + }
87 +}
frame-core/src/main/java/cn/platform/agent/frame/core/redis/AbstractBaseRedis.java 0 → 100644
1 +package cn.platform.agent.frame.core.redis;
2 +
3 +import org.springframework.beans.factory.annotation.Autowired;
4 +import org.springframework.data.redis.core.RedisTemplate;
5 +import org.springframework.data.redis.serializer.RedisSerializer;
6 +
7 +/**
8 + * <p>标题: </p>
9 + * <p>功能描述: </p>
10 + * <p>创建时间:17/4/9 </p>
11 + * <p>作者:SIVEN</p>
12 + * <p>修改历史记录:</p>
13 + * ============================================================<br>
14 + */
15 +public abstract class AbstractBaseRedis<K, V> {
16 +
17 + @Autowired
18 + protected RedisTemplate<K, V> redisTemplate;
19 +
20 + /**
21 + * 设置redisTemplate
22 + * @param redisTemplate the redisTemplate to set
23 + */
24 + public void setRedisTemplate(RedisTemplate<K, V> redisTemplate) {
25 + this.redisTemplate = redisTemplate;
26 + }
27 +
28 + /**
29 + * 获取 String RedisSerializer
30 + * <br>------------------------------<br>
31 + */
32 + protected RedisSerializer<String> getStringSerializer() {
33 + return redisTemplate.getStringSerializer();
34 + }
35 +
36 +}
frame-core/src/main/java/cn/platform/agent/frame/core/redis/RedisClient.java 0 → 100755
frame-web/pom.xml 0 → 100644
1 +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
2 + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
3 + <modelVersion>4.0.0</modelVersion>
4 +
5 + <parent>
6 + <groupId>cn.platform.agent.frame</groupId>
7 + <artifactId>frame-main</artifactId>
8 + <version>1.0.0-SNAPSHOT</version>
9 + </parent>
10 +
11 + <artifactId>frame-web</artifactId>
12 + <name>frame-web</name>
13 + <packaging>jar</packaging>
14 +
15 + <dependencies>
16 +
17 + <dependency>
18 + <groupId>${project.groupId}</groupId>
19 + <artifactId>frame-core</artifactId>
20 + </dependency>
21 +
22 + <dependency>
23 + <groupId>javax.servlet</groupId>
24 + <artifactId>servlet-api</artifactId>
25 + </dependency>
26 + <!-- web 相关依赖 -->
27 + <dependency>
28 + <groupId>org.springframework</groupId>
29 + <artifactId>spring-web</artifactId>
30 + <exclusions>
31 + <exclusion>
32 + <artifactId>commons-logging</artifactId>
33 + <groupId>commons-logging</groupId>
34 + </exclusion>
35 + </exclusions>
36 + </dependency>
37 + <dependency>
38 + <groupId>org.springframework</groupId>
39 + <artifactId>spring-context-support</artifactId>
40 + </dependency>
41 + <dependency>
42 + <artifactId>spring-webmvc</artifactId>
43 + <groupId>org.springframework</groupId>
44 + </dependency>
45 +
46 + <!-- jackson -->
47 + <dependency>
48 + <groupId>com.fasterxml.jackson.core</groupId>
49 + <artifactId>jackson-databind</artifactId>
50 + </dependency>
51 + <dependency>
52 + <groupId>junit</groupId>
53 + <artifactId>junit</artifactId>
54 + <scope>test</scope>
55 + </dependency>
56 + </dependencies>
57 +
58 +</project>
frame-web/src/main/java/cn/platform/agent/frame/web/BufferedResponseWrapper.java 0 → 100644
1 +package cn.platform.agent.frame.web;
2 +
3 +import javax.servlet.ServletOutputStream;
4 +import javax.servlet.http.HttpServletResponse;
5 +import javax.servlet.http.HttpServletResponseWrapper;
6 +import java.io.ByteArrayOutputStream;
7 +import java.io.IOException;
8 +import java.io.OutputStream;
9 +import java.io.PrintWriter;
10 +
11 +public class BufferedResponseWrapper extends HttpServletResponseWrapper {
12 +
13 + private TeeServletOutputStream mTeeOutputStream;
14 +
15 + private static class TeeOutputStream extends OutputStream {
16 + private OutputStream mChainStream;
17 + private OutputStream mTeeStream;
18 +
19 + public TeeOutputStream(OutputStream chainStream, OutputStream teeStream) {
20 + mChainStream = chainStream;
21 + mTeeStream = teeStream;
22 + }
23 +
24 + @Override
25 + public void write(int b) throws IOException {
26 + mChainStream.write(b);
27 + mTeeStream.write(b);
28 + mTeeStream.flush();
29 + }
30 +
31 + @Override
32 + public void close() throws IOException {
33 + flush();
34 + mChainStream.close();
35 + mTeeStream.close();
36 + }
37 +
38 + @Override
39 + public void flush() throws IOException {
40 + mChainStream.close();
41 + }
42 + }
43 +
44 + public class TeeServletOutputStream extends ServletOutputStream {
45 + private final TeeOutputStream targetStream;
46 +
47 + public TeeServletOutputStream(OutputStream one, OutputStream two) {
48 + targetStream = new TeeOutputStream(one, two);
49 + }
50 +
51 + @Override
52 + public void write(int b) throws IOException {
53 + this.targetStream.write(b);
54 + }
55 +
56 + @Override
57 + public void flush() throws IOException {
58 + super.flush();
59 + this.targetStream.flush();
60 + }
61 +
62 + @Override
63 + public void close() throws IOException {
64 + super.close();
65 + this.targetStream.close();
66 + }
67 + }
68 +
69 + private ByteArrayOutputStream mByteArrayOutputStream;
70 +
71 + public BufferedResponseWrapper(HttpServletResponse response) throws IOException {
72 + super(response);
73 + mByteArrayOutputStream = new ByteArrayOutputStream();
74 + mTeeOutputStream = new TeeServletOutputStream(super.getResponse().getOutputStream(), mByteArrayOutputStream);
75 + }
76 +
77 + @Override
78 + public PrintWriter getWriter() throws IOException {
79 + return super.getResponse().getWriter();
80 + }
81 +
82 + @Override
83 + public ServletOutputStream getOutputStream() throws IOException {
84 + return mTeeOutputStream;
85 + }
86 +
87 + public String getResponseBody() {
88 + return mByteArrayOutputStream.toString();
89 + }
90 +
91 +}
frame-web/src/main/java/cn/platform/agent/frame/web/LogHttpServletReuqestWrapper.java 0 → 100644
1 +package cn.platform.agent.frame.web;
2 +
3 +
4 +import cn.platform.agent.frame.core.helper.IOHelper;
5 +
6 +import javax.servlet.ServletInputStream;
7 +import javax.servlet.http.HttpServletRequest;
8 +import javax.servlet.http.HttpServletRequestWrapper;
9 +import java.io.BufferedReader;
10 +import java.io.ByteArrayInputStream;
11 +import java.io.IOException;
12 +import java.io.InputStreamReader;
13 +
14 +public class LogHttpServletReuqestWrapper extends HttpServletRequestWrapper {
15 +
16 + private String _body;
17 +
18 + public LogHttpServletReuqestWrapper(HttpServletRequest request) throws IOException {
19 + super(request);
20 + _body = IOHelper.toString(request.getReader());
21 + }
22 +
23 + @Override
24 + public ServletInputStream getInputStream() throws IOException {
25 + final ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(_body.getBytes());
26 + return new ServletInputStream() {
27 + @Override
28 + public int read() throws IOException {
29 + return byteArrayInputStream.read();
30 + }
31 + };
32 + }
33 +
34 + @Override
35 + public BufferedReader getReader() throws IOException {
36 + return new BufferedReader(new InputStreamReader(this.getInputStream()));
37 + }
38 +
39 + public String getBody() {
40 + return this._body;
41 + }
42 +
43 +}
frame-web/src/main/java/cn/platform/agent/frame/web/WebExceptionHandler.java 0 → 100644
1 +package cn.platform.agent.frame.web;
2 +
3 +import cn.platform.agent.frame.core.common.MessageBean;
4 +import cn.platform.agent.frame.core.common.MessageBeanHelper;
5 +import cn.platform.agent.frame.core.common.ResultBean;
6 +import cn.platform.agent.frame.core.common.ResultType;
7 +import cn.platform.agent.frame.core.exception.DaoException;
8 +import cn.platform.agent.frame.core.exception.ValidationException;
9 +import org.slf4j.Logger;
10 +import org.slf4j.LoggerFactory;
11 +import org.springframework.http.converter.HttpMessageConversionException;
12 +import org.springframework.web.bind.annotation.ControllerAdvice;
13 +import org.springframework.web.bind.annotation.ExceptionHandler;
14 +import org.springframework.web.bind.annotation.ResponseBody;
15 +
16 +import javax.servlet.http.HttpServletRequest;
17 +
18 +/**
19 + * @ClassName: GlobalExceptionHandler
20 + * @Description: 全局的异常处理类,用于处理所有的异常信息
21 + * @author Bob Lee
22 + * @date 2017年3月14日 下午2:46:39
23 + */
24 +@ControllerAdvice
25 +public class WebExceptionHandler {
26 +
27 + private static final Logger logger = LoggerFactory.getLogger(WebExceptionHandler.class);
28 +
29 + @ExceptionHandler(value = HttpMessageConversionException.class)
30 + @ResponseBody
31 + public ResultBean exceptionHandler(HttpServletRequest req, HttpMessageConversionException e) throws Exception {
32 + ResultBean resultBean = new ResultBean();
33 + resultBean.setSuccess(false);
34 + resultBean.setType(ResultType.MESSAGE_CONVERT_EXCEPTION.toString());
35 + resultBean.setMessageBeans(MessageBeanHelper.buildForOne(new MessageBean(e.getMessage())));
36 + return resultBean;
37 + }
38 +
39 + @ExceptionHandler(value = ValidationException.class)
40 + @ResponseBody
41 + public ResultBean exceptionHandler(HttpServletRequest req, ValidationException e) throws Exception {
42 + ResultBean<ValidationException> result = new ResultBean<>();
43 + result.setSuccess(false);
44 + result.setType(ResultType.VALIDATION_EXCEPTION.toString());
45 + result.setMessageBeans(e.getMessageBeans());
46 + return result;
47 + }
48 +
49 + @ExceptionHandler(value = DaoException.class)
50 + @ResponseBody
51 + public ResultBean exceptionHandler(HttpServletRequest req, DaoException e) throws Exception {
52 + logger.error("DaoException", e);
53 + ResultBean<DaoException> result = new ResultBean<>();
54 + result.setSuccess(false);
55 + result.setType(ResultType.DAO_EXCEPTION.toString());
56 + result.setMessageBeans(MessageBeanHelper.buildForOne(e.getMessageBean()));
57 + return result;
58 + }
59 +
60 + @ExceptionHandler(value = Exception.class)
61 + @ResponseBody
62 + public ResultBean exceptionHandler(HttpServletRequest req, Exception e) throws Exception {
63 + logger.error(e.getMessage(), e);
64 + ResultBean resultBean = new ResultBean();
65 + resultBean.setSuccess(false);
66 + resultBean.setType(ResultType.UNKWON_EXCEPTION.toString());
67 + resultBean.setMessageBeans(MessageBeanHelper.buildForOne(new MessageBean(e.getMessage())));
68 + return resultBean;
69 + }
70 +
71 +}
frame-web/src/main/java/cn/platform/agent/frame/web/controller/BaseController.java 0 → 100644
1 +package cn.platform.agent.frame.web.controller;
2 +
3 +import org.slf4j.Logger;
4 +import org.slf4j.LoggerFactory;
5 +import org.springframework.web.bind.annotation.ModelAttribute;
6 +
7 +import javax.servlet.ServletRequest;
8 +import javax.servlet.ServletResponse;
9 +import javax.servlet.http.HttpServletRequest;
10 +import javax.servlet.http.HttpServletResponse;
11 +import javax.servlet.http.HttpSession;
12 +
13 +/**
14 + * <p>标题: </p>
15 + * <p>功能描述: </p>
16 + * <p>创建时间:17/4/21 </p>
17 + * <p>作者:SIVEN</p>
18 + * <p>修改历史记录:</p>
19 + * ============================================================<br>
20 + */
21 +public class BaseController {
22 +
23 + private Logger logger = LoggerFactory.getLogger(getClass());
24 + /**
25 + * ThreadLocal确保高并发下每个请求的request,response都是独立的
26 + */
27 + private static ThreadLocal<ServletRequest> currentRequest = new ThreadLocal<ServletRequest>();
28 + private static ThreadLocal<ServletResponse> currentResponse = new ThreadLocal<ServletResponse>();
29 +
30 + /**
31 + * 线程安全初始化reque,respose对象
32 + *
33 + * @param request
34 + * @param response
35 + */
36 + @ModelAttribute
37 + public void initReqAndRep(HttpServletRequest request, HttpServletResponse response) {
38 + currentRequest.set(request);
39 + currentResponse.set(response);
40 + }
41 +
42 + /**
43 + * 线程安全
44 + *
45 + * @return
46 + */
47 + public HttpServletRequest request() {
48 + return (HttpServletRequest) currentRequest.get();
49 + }
50 +
51 + /**
52 + * 线程安全
53 + *
54 + * @return
55 + */
56 + public HttpServletResponse response() {
57 + return (HttpServletResponse) currentResponse.get();
58 + }
59 +
60 + /**
61 + * 获取Session对象
62 + * @return
63 + */
64 + public HttpSession session() {
65 + return this.request().getSession();
66 + }
67 +
68 +}
frame-web/src/main/java/cn/platform/agent/frame/web/filter/ExceptionFilter.java 0 → 100644
1 +package cn.platform.agent.frame.web.filter;
2 +
3 +import org.slf4j.Logger;
4 +import org.slf4j.LoggerFactory;
5 +
6 +import javax.servlet.*;
7 +import java.io.IOException;
8 +
9 +public class ExceptionFilter implements Filter {
10 +
11 + private static final Logger logger = LoggerFactory.getLogger(ExceptionFilter.class);
12 +
13 + private IExceptionHandler exceptionHandler;
14 +
15 + @Override
16 + public void init(FilterConfig filterConfig) throws ServletException {
17 + }
18 +
19 + @Override
20 + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
21 + try {
22 + chain.doFilter(request, response);
23 + } catch (Throwable e) {
24 + logger.error("exception filter throwable:" + e.getMessage());
25 + this.exceptionHandler.handle(request, response, e);
26 + }
27 +
28 + }
29 +
30 + @Override
31 + public void destroy() {
32 + }
33 +
34 + public IExceptionHandler getExceptionHandler() {
35 + return exceptionHandler;
36 + }
37 +
38 + public void setExceptionHandler(IExceptionHandler exceptionHandler) {
39 + this.exceptionHandler = exceptionHandler;
40 + }
41 +
42 +}
frame-web/src/main/java/cn/platform/agent/frame/web/filter/IExceptionHandler.java 0 → 100644
1 +package cn.platform.agent.frame.web.filter;
2 +
3 +import javax.servlet.ServletRequest;
4 +import javax.servlet.ServletResponse;
5 +
6 +/**
7 + * @ClassName: IExceptionHandler
8 + * @Description: 异常处理类,用于web端异常过滤器使用,用于处理controller的业务异常或未处理异常。
9 + * @author Bob Lee
10 + * @date 2017年3月14日 上午9:11:22
11 + */
12 +public interface IExceptionHandler {
13 +
14 + void handle(ServletRequest request, ServletResponse response, Throwable e);
15 +
16 +}
frame-web/src/main/java/cn/platform/agent/frame/web/filter/RestfulLogFilter.java 0 → 100644
1 +package cn.platform.agent.frame.web.filter;
2 +
3 +import cn.platform.agent.frame.core.helper.UUIDHelper;
4 +import cn.platform.agent.frame.web.BufferedResponseWrapper;
5 +import cn.platform.agent.frame.web.LogHttpServletReuqestWrapper;
6 +import org.slf4j.Logger;
7 +import org.slf4j.LoggerFactory;
8 +import org.slf4j.MDC;
9 +
10 +import javax.servlet.*;
11 +import javax.servlet.http.HttpServletRequest;
12 +import javax.servlet.http.HttpServletResponse;
13 +import java.io.IOException;
14 +
15 +/**
16 + * 日志记录过滤器,使用logback来记录日志,通过logback来配置日志的文件路径等
17 + *
18 + * @author libx
19 + */
20 +public class RestfulLogFilter implements Filter {
21 +
22 + private static final Logger logger = LoggerFactory.getLogger(RestfulLogFilter.class);
23 +
24 + /**
25 + * 是否记录日志,当为false时,不记录日志
26 + */
27 + private boolean actlog = true;
28 +
29 + /**
30 + * 日志的分隔符
31 + */
32 + private String seperate = "|";
33 +
34 + @Override
35 + public void destroy() {
36 + }
37 +
38 + @Override
39 + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
40 + throws IOException, ServletException {
41 +
42 + MDC.put("reqSerial", UUIDHelper.getUuid());
43 +
44 + if (actlog) {
45 +
46 + LogHttpServletReuqestWrapper httpRequest = new LogHttpServletReuqestWrapper((HttpServletRequest) request);
47 + BufferedResponseWrapper httpResponse = new BufferedResponseWrapper((HttpServletResponse) response);
48 + logger.debug("===============filter begin, Url[{}]===============", httpRequest.getRequestURI());
49 + Long requestTime = System.currentTimeMillis();
50 + try {
51 + chain.doFilter(httpRequest, httpResponse);
52 + } catch (Exception e) {
53 + logger.error(e.getMessage(), e);
54 + }
55 + Long responseTime = System.currentTimeMillis();
56 + StringBuilder sb = new StringBuilder();
57 + sb.append(httpRequest.getRequestURI());// 请求的URL
58 + sb.append(seperate);
59 + sb.append(requestTime); // 请求时间
60 + sb.append(seperate);
61 + sb.append(httpRequest.getBody()); // 请求的内容
62 + sb.append(seperate);
63 + sb.append(responseTime); // 响应时间
64 + sb.append(seperate);
65 + sb.append(httpResponse.getResponseBody()); // 请求的响应内容
66 + sb.append(seperate);
67 + sb.append(responseTime - requestTime);// 整个请求的使用时长
68 + logger.info(sb.toString());
69 + } else {
70 + // 不记录日志
71 + chain.doFilter(request, response);
72 + }
73 + }
74 +
75 + @Override
76 + public void init(FilterConfig filterConfig) throws ServletException {
77 + }
78 +
79 + public void setActlog(boolean actlog) {
80 + this.actlog = actlog;
81 + }
82 +
83 + public void setSeperate(String seperate) {
84 + this.seperate = seperate;
85 + }
86 +
87 +}
frame-web/src/main/java/cn/platform/agent/frame/web/filter/UnknowExceptionHandler.java 0 → 100644
1 +package cn.platform.agent.frame.web.filter;
2 +
3 +import cn.platform.agent.frame.core.common.MessageBean;
4 +import cn.platform.agent.frame.core.common.MessageBeanHelper;
5 +import cn.platform.agent.frame.core.common.ResultBean;
6 +import cn.platform.agent.frame.core.common.ResultType;
7 +import cn.platform.agent.frame.core.helper.JsonHelper;
8 +import org.slf4j.Logger;
9 +import org.slf4j.LoggerFactory;
10 +
11 +import javax.servlet.ServletRequest;
12 +import javax.servlet.ServletResponse;
13 +import java.io.IOException;
14 +import java.io.OutputStream;
15 +
16 +/**
17 + * @ClassName: UnknowExceptionHandler
18 + * @Description: 未知异常处理,当controller发生未知异常时,则异常转成{@code ResultBean},并在
19 + * {@code ServletResponse}中直接以JSON字节流输出
20 + * @author Bob Lee
21 + * @date 2017年3月15日 下午2:18:01
22 + */
23 +public class UnknowExceptionHandler implements IExceptionHandler {
24 +
25 + private static final Logger logger = LoggerFactory.getLogger(UnknowExceptionHandler.class);
26 +
27 + @Override
28 + public void handle(ServletRequest request, ServletResponse response, Throwable e) {
29 + ResultBean<Throwable> result = new ResultBean<>();
30 + result.setSuccess(false);
31 + result.setType(ResultType.UNKWON_EXCEPTION.toString());
32 + result.setMessageBeans(MessageBeanHelper.buildForOne(new MessageBean(e.getMessage())));
33 + result.setResultData(e);
34 + writeResult(response, result);
35 + }
36 +
37 + private void writeResult(ServletResponse response, ResultBean<?> result) {
38 + try (OutputStream os = response.getOutputStream();) {
39 + os.write(JsonHelper.toJSONString(result).getBytes());
40 + os.flush();
41 + os.close();
42 + } catch (IOException e) {
43 + logger.error(e.getMessage(), e);
44 + }
45 + }
46 +
47 +}
frame-web/src/main/java/cn/platform/agent/frame/web/interceptor/SecurityInterceptor.java 0 → 100644
1 +package cn.platform.agent.frame.web.interceptor;
2 +
3 +import cn.platform.agent.frame.core.exception.AuthorizationException;
4 +import org.springframework.util.StringUtils;
5 +import org.springframework.web.servlet.HandlerInterceptor;
6 +import org.springframework.web.servlet.ModelAndView;
7 +
8 +import javax.servlet.http.HttpServletRequest;
9 +import javax.servlet.http.HttpServletResponse;
10 +import javax.servlet.http.HttpSession;
11 +import java.util.List;
12 +
13 +/**
14 + * <p>标题: 登录认证拦截器</p>
15 + * <p>功能描述: </p>
16 + * <p>创建时间:17/4/21 </p>
17 + * <p>作者:SIVEN</p>
18 + * <p>修改历史记录:</p>
19 + * ============================================================<br>
20 + */
21 +public class SecurityInterceptor implements HandlerInterceptor {
22 +
23 + private String sessionKey;
24 + private List<String> excludedUrls;
25 +
26 + public void setSessionKey(String sessionKey) {
27 + this.sessionKey = sessionKey;
28 + }
29 +
30 + public void setExcludedUrls(List<String> excludedUrls) {
31 + this.excludedUrls = excludedUrls;
32 + }
33 +
34 + @Override
35 + public boolean preHandle(HttpServletRequest request,
36 + HttpServletResponse response, Object handler) throws Exception {
37 + // excluded URLs:
38 + // see http://stackoverflow.com/questions/9908124/spring-mvc-3-interceptor-on-all-excluding-some-defined-paths
39 + String requestUri = request.getRequestURI();
40 + if (null!=excludedUrls)
41 + for (String url : excludedUrls) {
42 + if (requestUri.endsWith(url)) {
43 + return true;
44 + }
45 + }
46 +
47 + // intercept
48 + HttpSession session = request.getSession();
49 + if (session.getAttribute(sessionKey) == null) {
50 + // see http://stackoverflow.com/questions/12713873/spring-3-1-how-do-you-send-all-exception-to-one-page
51 + throw new AuthorizationException();
52 + } else {
53 + return true;
54 + }
55 + }
56 +
57 + @Override
58 + public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
59 + }
60 +
61 + @Override
62 + public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
63 + }
64 +}
@@ -13,6 +13,7 @@ @@ -13,6 +13,7 @@
13 13
14 <!-- spring相关 --> 14 <!-- spring相关 -->
15 <spring.version>4.3.5.RELEASE</spring.version> 15 <spring.version>4.3.5.RELEASE</spring.version>
16 + <spring.data.redis.version>1.6.0.RELEASE</spring.data.redis.version>
16 17
17 <!-- 数据库操作相关 --> 18 <!-- 数据库操作相关 -->
18 <spring.data.commons.version>1.13.0.RELEASE</spring.data.commons.version> 19 <spring.data.commons.version>1.13.0.RELEASE</spring.data.commons.version>
@@ -22,6 +23,7 @@ @@ -22,6 +23,7 @@
22 <mysql.version>5.1.36</mysql.version> 23 <mysql.version>5.1.36</mysql.version>
23 <mybatis-spring.version>1.3.1</mybatis-spring.version> 24 <mybatis-spring.version>1.3.1</mybatis-spring.version>
24 <mybatis.version>3.4.2</mybatis.version> 25 <mybatis.version>3.4.2</mybatis.version>
26 + <redis.clients.version>2.7.3</redis.clients.version>
25 27
26 <!-- 分布式事务处理 --> 28 <!-- 分布式事务处理 -->
27 <atomikos.version>4.0.2</atomikos.version> 29 <atomikos.version>4.0.2</atomikos.version>
@@ -65,6 +67,12 @@ @@ -65,6 +67,12 @@
65 <version>${project.version}</version> 67 <version>${project.version}</version>
66 </dependency> 68 </dependency>
67 69
70 + <dependency>
71 + <groupId>${project.groupId}</groupId>
72 + <artifactId>frame-web</artifactId>
73 + <version>${project.version}</version>
74 + </dependency>
75 +
68 <dependency> 76 <dependency>
69 <groupId>javax.servlet</groupId> 77 <groupId>javax.servlet</groupId>
70 <artifactId>servlet-api</artifactId> 78 <artifactId>servlet-api</artifactId>
@@ -160,6 +168,12 @@ @@ -160,6 +168,12 @@
160 <artifactId>spring-data-commons</artifactId> 168 <artifactId>spring-data-commons</artifactId>
161 <version>${spring.data.commons.version}</version> 169 <version>${spring.data.commons.version}</version>
162 </dependency> 170 </dependency>
171 +
172 + <dependency>
173 + <groupId>org.springframework.data</groupId>
174 + <artifactId>spring-data-redis</artifactId>
175 + <version>${spring.data.redis.version}</version>
176 + </dependency>
163 <!-- spring end --> 177 <!-- spring end -->
164 178
165 <!-- commons begin --> 179 <!-- commons begin -->
@@ -287,6 +301,12 @@ @@ -287,6 +301,12 @@
287 <artifactId>mysql-connector-java</artifactId> 301 <artifactId>mysql-connector-java</artifactId>
288 <version>${mysql.version}</version> 302 <version>${mysql.version}</version>
289 </dependency> 303 </dependency>
304 +
305 + <dependency>
306 + <groupId>redis.clients</groupId>
307 + <artifactId>jedis</artifactId>
308 + <version>${redis.clients.version}</version>
309 + </dependency>
290 <!-- 数据连接池和数据库驱动end --> 310 <!-- 数据连接池和数据库驱动end -->
291 <!-- mybatis --> 311 <!-- mybatis -->
292 <dependency> 312 <dependency>
@@ -436,5 +456,6 @@ @@ -436,5 +456,6 @@
436 </build> 456 </build>
437 <modules> 457 <modules>
438 <module>frame-core</module> 458 <module>frame-core</module>
459 + <module>frame-web</module>
439 </modules> 460 </modules>
440 </project> 461 </project>
\ No newline at end of file \ No newline at end of file