Browse Source

druid spring调整

10.0
daniel 6 years ago
parent
commit
67fb26b0be
  1. 18
      fine-druid/pom.xml
  2. 2
      fine-druid/src/com/fr/third/alibaba/druid/stat/JdbcStatManagerFactoryBean.java
  3. 4
      fine-druid/src/com/fr/third/alibaba/druid/support/ibatis/SpringIbatisBeanNameAutoProxyCreator.java
  4. 12
      fine-druid/src/com/fr/third/alibaba/druid/support/ibatis/SpringIbatisBeanTypeAutoProxyCreator.java
  5. 2
      fine-druid/src/com/fr/third/alibaba/druid/support/spring/MBeanServerFactoryBean.java
  6. 10
      fine-druid/src/com/fr/third/alibaba/druid/support/spring/mvc/StatHandlerInterceptor.java
  7. 20
      fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/BeanTypeAutoProxyCreator.java
  8. 10
      fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/DruidStatInterceptor.java
  9. 14
      fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/annotation/StatAnnotationAdvisor.java
  10. 8
      fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/annotation/StatAnnotationBeanPostProcessor.java
  11. 16
      fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/config/DruidStatBeanDefinitionParser.java
  12. 2
      fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/config/DruidStatNamespaceHandler.java
  13. 18
      fine-hibernate/pom.xml
  14. 116
      fine-hibernate/src/com/fr/third/org/hibernate/property/access/spi/UnsafeGetterFieldImpl.java
  15. 160
      fine-hibernate/src/com/fr/third/org/hibernate/property/access/spi/UnsafeSetterMethodImpl.java

18
fine-druid/pom.xml

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.fr.third</groupId>
<artifactId>base-third-code</artifactId>
<version>10.0-RELEASE-SNAPSHOT</version>
</parent>
<groupId>com.fr.third</groupId>
<artifactId>fine-druid</artifactId>
<version>10.0-RELEASE-SNAPSHOT</version>
</project>

2
fine-druid/src/com/fr/third/alibaba/druid/stat/JdbcStatManagerFactoryBean.java

@ -15,7 +15,7 @@
*/ */
package com.fr.third.alibaba.druid.stat; package com.fr.third.alibaba.druid.stat;
import org.springframework.beans.factory.FactoryBean; import com.fr.third.springframework.beans.factory.FactoryBean;
public class JdbcStatManagerFactoryBean implements FactoryBean<JdbcStatManager> { public class JdbcStatManagerFactoryBean implements FactoryBean<JdbcStatManager> {

4
fine-druid/src/com/fr/third/alibaba/druid/support/ibatis/SpringIbatisBeanNameAutoProxyCreator.java

@ -20,8 +20,8 @@ import java.util.List;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.aop.TargetSource; import com.fr.third.springframework.aop.TargetSource;
import org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator; import com.fr.third.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator;
import com.ibatis.sqlmap.client.SqlMapClient; import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient; import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;

12
fine-druid/src/com/fr/third/alibaba/druid/support/ibatis/SpringIbatisBeanTypeAutoProxyCreator.java

@ -19,11 +19,11 @@ import com.ibatis.sqlmap.client.SqlMapClient;
import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient; import com.ibatis.sqlmap.engine.impl.ExtendedSqlMapClient;
import org.apache.commons.logging.Log; import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.springframework.aop.TargetSource; import com.fr.third.springframework.aop.TargetSource;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator; import com.fr.third.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.beans.factory.BeanFactory; import com.fr.third.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean; import com.fr.third.springframework.beans.factory.FactoryBean;
import org.springframework.util.PatternMatchUtils; import com.fr.third.springframework.util.PatternMatchUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -95,7 +95,7 @@ public class SpringIbatisBeanTypeAutoProxyCreator extends AbstractAutoProxyCreat
* @param beanName the bean name to check * @param beanName the bean name to check
* @param mappedName the name in the configured list of names * @param mappedName the name in the configured list of names
* @return if the names match * @return if the names match
* @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) * @see com.fr.third.springframework.util.PatternMatchUtils#simpleMatch(String, String)
*/ */
protected boolean isMatch(String beanName, String mappedName) { protected boolean isMatch(String beanName, String mappedName) {
return PatternMatchUtils.simpleMatch(mappedName, beanName); return PatternMatchUtils.simpleMatch(mappedName, beanName);

2
fine-druid/src/com/fr/third/alibaba/druid/support/spring/MBeanServerFactoryBean.java

@ -19,7 +19,7 @@ import java.lang.management.ManagementFactory;
import javax.management.MBeanServer; import javax.management.MBeanServer;
import org.springframework.beans.factory.FactoryBean; import com.fr.third.springframework.beans.factory.FactoryBean;
public class MBeanServerFactoryBean implements FactoryBean<MBeanServer> { public class MBeanServerFactoryBean implements FactoryBean<MBeanServer> {

10
fine-druid/src/com/fr/third/alibaba/druid/support/spring/mvc/StatHandlerInterceptor.java

@ -24,11 +24,11 @@ import javax.servlet.http.HttpServletResponse;
import com.fr.third.alibaba.druid.filter.stat.StatFilterContext; import com.fr.third.alibaba.druid.filter.stat.StatFilterContext;
import com.fr.third.alibaba.druid.support.http.stat.WebSessionStat; import com.fr.third.alibaba.druid.support.http.stat.WebSessionStat;
import com.fr.third.alibaba.druid.support.http.stat.WebURIStat; import com.fr.third.alibaba.druid.support.http.stat.WebURIStat;
import org.springframework.beans.factory.DisposableBean; import com.fr.third.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean; import com.fr.third.springframework.beans.factory.InitializingBean;
import org.springframework.web.servlet.HandlerInterceptor; import com.fr.third.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping; import com.fr.third.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView; import com.fr.third.springframework.web.servlet.ModelAndView;
import com.fr.third.alibaba.druid.support.http.AbstractWebStatImpl; import com.fr.third.alibaba.druid.support.http.AbstractWebStatImpl;
import com.fr.third.alibaba.druid.support.http.stat.WebAppStat; import com.fr.third.alibaba.druid.support.http.stat.WebAppStat;

20
fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/BeanTypeAutoProxyCreator.java

@ -15,15 +15,15 @@
*/ */
package com.fr.third.alibaba.druid.support.spring.stat; package com.fr.third.alibaba.druid.support.spring.stat;
import org.springframework.aop.TargetSource; import com.fr.third.springframework.aop.TargetSource;
import org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator; import com.fr.third.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator;
import org.springframework.beans.factory.BeanFactory; import com.fr.third.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.FactoryBean; import com.fr.third.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.InitializingBean; import com.fr.third.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext; import com.fr.third.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware; import com.fr.third.springframework.context.ApplicationContextAware;
import org.springframework.util.Assert; import com.fr.third.springframework.util.Assert;
import org.springframework.util.PatternMatchUtils; import com.fr.third.springframework.util.PatternMatchUtils;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
@ -83,7 +83,7 @@ public class BeanTypeAutoProxyCreator extends AbstractAutoProxyCreator implement
* @param beanName the bean name to check * @param beanName the bean name to check
* @param mappedName the name in the configured list of names * @param mappedName the name in the configured list of names
* @return if the names match * @return if the names match
* @see org.springframework.util.PatternMatchUtils#simpleMatch(String, String) * @see com.fr.third.springframework.util.PatternMatchUtils#simpleMatch(String, String)
*/ */
protected boolean isMatch(String beanName, String mappedName) { protected boolean isMatch(String beanName, String mappedName) {
return PatternMatchUtils.simpleMatch(mappedName, beanName); return PatternMatchUtils.simpleMatch(mappedName, beanName);

10
fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/DruidStatInterceptor.java

@ -21,9 +21,9 @@ import com.fr.third.alibaba.druid.support.logging.Log;
import com.fr.third.alibaba.druid.support.logging.LogFactory; import com.fr.third.alibaba.druid.support.logging.LogFactory;
import org.aopalliance.intercept.MethodInterceptor; import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation; import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.TargetSource; import com.fr.third.springframework.aop.TargetSource;
import org.springframework.beans.factory.DisposableBean; import com.fr.third.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean; import com.fr.third.springframework.beans.factory.InitializingBean;
import java.lang.reflect.Method; import java.lang.reflect.Method;
@ -123,8 +123,8 @@ public class DruidStatInterceptor implements MethodInterceptor, InitializingBean
try { try {
// 最多支持10层代理 // 最多支持10层代理
for (int i = 0; i < 10; ++i) { for (int i = 0; i < 10; ++i) {
if (thisObject instanceof org.springframework.aop.framework.Advised) { if (thisObject instanceof com.fr.third.springframework.aop.framework.Advised) {
TargetSource targetSource = ((org.springframework.aop.framework.Advised) thisObject).getTargetSource(); TargetSource targetSource = ((com.fr.third.springframework.aop.framework.Advised) thisObject).getTargetSource();
if (targetSource == null) { if (targetSource == null) {
break; break;

14
fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/annotation/StatAnnotationAdvisor.java

@ -17,13 +17,13 @@ package com.fr.third.alibaba.druid.support.spring.stat.annotation;
import com.fr.third.alibaba.druid.support.spring.stat.DruidStatInterceptor; import com.fr.third.alibaba.druid.support.spring.stat.DruidStatInterceptor;
import org.aopalliance.aop.Advice; import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut; import com.fr.third.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor; import com.fr.third.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.ComposablePointcut; import com.fr.third.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.annotation.AnnotationMatchingPointcut; import com.fr.third.springframework.aop.support.annotation.AnnotationMatchingPointcut;
import org.springframework.beans.BeansException; import com.fr.third.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory; import com.fr.third.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware; import com.fr.third.springframework.beans.factory.BeanFactoryAware;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class StatAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware { public class StatAnnotationAdvisor extends AbstractPointcutAdvisor implements BeanFactoryAware {

8
fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/annotation/StatAnnotationBeanPostProcessor.java

@ -18,10 +18,10 @@ package com.fr.third.alibaba.druid.support.spring.stat.annotation;
import javax.annotation.Resource; import javax.annotation.Resource;
import com.fr.third.alibaba.druid.support.spring.stat.DruidStatInterceptor; import com.fr.third.alibaba.druid.support.spring.stat.DruidStatInterceptor;
import org.springframework.aop.framework.AbstractAdvisingBeanPostProcessor; import com.fr.third.springframework.aop.framework.AbstractAdvisingBeanPostProcessor;
import org.springframework.beans.BeansException; import com.fr.third.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory; import com.fr.third.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware; import com.fr.third.springframework.beans.factory.BeanFactoryAware;
@SuppressWarnings("serial") @SuppressWarnings("serial")
public class StatAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware { public class StatAnnotationBeanPostProcessor extends AbstractAdvisingBeanPostProcessor implements BeanFactoryAware {

16
fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/config/DruidStatBeanDefinitionParser.java

@ -15,14 +15,14 @@
*/ */
package com.fr.third.alibaba.druid.support.spring.stat.config; package com.fr.third.alibaba.druid.support.spring.stat.config;
import org.springframework.beans.factory.config.BeanDefinition; import com.fr.third.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder; import com.fr.third.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.parsing.BeanComponentDefinition; import com.fr.third.springframework.beans.factory.parsing.BeanComponentDefinition;
import org.springframework.beans.factory.parsing.CompositeComponentDefinition; import com.fr.third.springframework.beans.factory.parsing.CompositeComponentDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder; import com.fr.third.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry; import com.fr.third.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.xml.BeanDefinitionParser; import com.fr.third.springframework.beans.factory.xml.BeanDefinitionParser;
import org.springframework.beans.factory.xml.ParserContext; import com.fr.third.springframework.beans.factory.xml.ParserContext;
import org.w3c.dom.Element; import org.w3c.dom.Element;
public class DruidStatBeanDefinitionParser implements BeanDefinitionParser { public class DruidStatBeanDefinitionParser implements BeanDefinitionParser {

2
fine-druid/src/com/fr/third/alibaba/druid/support/spring/stat/config/DruidStatNamespaceHandler.java

@ -15,7 +15,7 @@
*/ */
package com.fr.third.alibaba.druid.support.spring.stat.config; package com.fr.third.alibaba.druid.support.spring.stat.config;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport; import com.fr.third.springframework.beans.factory.xml.NamespaceHandlerSupport;
public class DruidStatNamespaceHandler extends NamespaceHandlerSupport { public class DruidStatNamespaceHandler extends NamespaceHandlerSupport {

18
fine-hibernate/pom.xml

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.fr.third</groupId>
<artifactId>base-third-code</artifactId>
<version>10.0-RELEASE-SNAPSHOT</version>
</parent>
<groupId>com.fr.third</groupId>
<artifactId>fine-hibernate</artifactId>
<version>10.0-RELEASE-SNAPSHOT</version>
</project>

116
fine-hibernate/src/com/fr/third/org/hibernate/property/access/spi/UnsafeGetterFieldImpl.java

@ -0,0 +1,116 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package com.fr.third.org.hibernate.property.access.spi;
import com.fr.third.org.hibernate.engine.spi.SessionImplementor;
import com.fr.third.org.hibernate.property.access.internal.AbstractFieldSerialForm;
import sun.misc.Unsafe;
import sun.reflect.FieldAccessor;
import sun.reflect.ReflectionFactory;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.security.AccessController;
import java.util.Locale;
import java.util.Map;
/**
* Field-based implementation of Getter
*
* @author Steve Ebersole
*/
public class UnsafeGetterFieldImpl implements Getter {
private final Class containerClass;
private final String propertyName;
private final Field field;
private final FieldAccessor accessor;
static ReflectionFactory reflectionFactory =
AccessController.doPrivileged(
new sun.reflect.ReflectionFactory.GetReflectionFactoryAction());
public UnsafeGetterFieldImpl(Class containerClass, String propertyName, Field field) {
this.containerClass = containerClass;
this.propertyName = propertyName;
this.field = field;
accessor = reflectionFactory.newFieldAccessor(field, false);
}
@Override
public Object get(Object owner) {
if(accessor != null){
return accessor.get(owner);
}
try {
return field.get( owner );
}
catch (Exception e) {
throw new PropertyAccessException(
String.format(
Locale.ROOT,
"Error accessing field [%s] by reflection for persistent property [%s#%s] : %s",
field.toGenericString(),
containerClass.getName(),
propertyName,
owner
),
e
);
}
}
@Override
public Object getForInsert(Object owner, Map mergeMap, SessionImplementor session) {
return get( owner );
}
@Override
public Class getReturnType() {
return field.getType();
}
@Override
public Member getMember() {
return field;
}
@Override
public String getMethodName() {
return null;
}
@Override
public Method getMethod() {
return null;
}
private Object writeReplace() throws ObjectStreamException {
return new SerialForm( containerClass, propertyName, field );
}
private static class SerialForm extends AbstractFieldSerialForm implements Serializable {
private final Class containerClass;
private final String propertyName;
private SerialForm(Class containerClass, String propertyName, Field field) {
super( field );
this.containerClass = containerClass;
this.propertyName = propertyName;
}
private Object readResolve() {
return new UnsafeGetterFieldImpl( containerClass, propertyName, resolveField() );
}
}
}

160
fine-hibernate/src/com/fr/third/org/hibernate/property/access/spi/UnsafeSetterMethodImpl.java

@ -0,0 +1,160 @@
/*
* Hibernate, Relational Persistence for Idiomatic Java
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later.
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package com.fr.third.org.hibernate.property.access.spi;
import com.fr.third.org.hibernate.PropertyAccessException;
import com.fr.third.org.hibernate.PropertySetterAccessException;
import com.fr.third.org.hibernate.engine.spi.SessionFactoryImplementor;
import com.fr.third.org.hibernate.internal.CoreMessageLogger;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static com.fr.third.org.hibernate.internal.CoreLogging.messageLogger;
/**
* @author Steve Ebersole
*/
public class UnsafeSetterMethodImpl implements Setter {
private static final CoreMessageLogger LOG = messageLogger( UnsafeSetterMethodImpl.class );
private final Class containerClass;
private final String propertyName;
private final Method setterMethod;
private final boolean isPrimitive;
public UnsafeSetterMethodImpl(Class containerClass, String propertyName, Method setterMethod) {
this.containerClass = containerClass;
this.propertyName = propertyName;
this.setterMethod = setterMethod;
this.isPrimitive = setterMethod.getParameterTypes()[0].isPrimitive();
}
@Override
public void set(Object target, Object value, SessionFactoryImplementor factory) {
try {
setterMethod.invoke( target, value );
}
catch (NullPointerException npe) {
if ( value == null && isPrimitive ) {
throw new PropertyAccessException(
npe,
"Null value was assigned to a property of primitive type",
true,
containerClass,
propertyName
);
}
else {
throw new PropertyAccessException(
npe,
"NullPointerException occurred while calling",
true,
containerClass,
propertyName
);
}
}
catch (InvocationTargetException ite) {
throw new PropertyAccessException(
ite,
"Exception occurred inside",
true,
containerClass,
propertyName
);
}
catch (IllegalAccessException iae) {
throw new PropertyAccessException(
iae,
"IllegalAccessException occurred while calling",
true,
containerClass,
propertyName
);
//cannot occur
}
catch (IllegalArgumentException iae) {
if ( value == null && isPrimitive ) {
throw new PropertyAccessException(
iae,
"Null value was assigned to a property of primitive type",
true,
containerClass,
propertyName
);
}
else {
final Class expectedType = setterMethod.getParameterTypes()[0];
LOG.illegalPropertySetterArgument( containerClass.getName(), propertyName );
LOG.expectedType( expectedType.getName(), value == null ? null : value.getClass().getName() );
throw new PropertySetterAccessException(
iae,
containerClass,
propertyName,
expectedType,
target,
value
);
}
}
}
@Override
public String getMethodName() {
return setterMethod.getName();
}
@Override
public Method getMethod() {
return setterMethod;
}
private Object writeReplace() throws ObjectStreamException {
return new SerialForm( containerClass, propertyName, setterMethod );
}
private static class SerialForm implements Serializable {
private final Class containerClass;
private final String propertyName;
private final Class declaringClass;
private final String methodName;
private final Class argumentType;
private SerialForm(Class containerClass, String propertyName, Method method) {
this.containerClass = containerClass;
this.propertyName = propertyName;
this.declaringClass = method.getDeclaringClass();
this.methodName = method.getName();
this.argumentType = method.getParameterTypes()[0];
}
private Object readResolve() {
return new UnsafeSetterMethodImpl( containerClass, propertyName, resolveMethod() );
}
@SuppressWarnings("unchecked")
private Method resolveMethod() {
try {
final Method method = declaringClass.getDeclaredMethod( methodName, argumentType );
method.setAccessible( true );
return method;
}
catch (NoSuchMethodException e) {
throw new PropertyAccessSerializationException(
"Unable to resolve setter method on deserialization : " + declaringClass.getName() + "#"
+ methodName + "(" + argumentType.getName() + ")"
);
}
}
}
}
Loading…
Cancel
Save