diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4329286
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,57 @@
+HELP.md
+target/
+!.mvn/wrapper/maven-wrapper.jar
+!**/src/main/**/target/
+!**/src/test/**/target/
+
+### STS ###
+.apt_generated
+.classpath
+.factorypath
+.project
+.settings
+.springBeans
+.sts4-cache
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
+
+### NetBeans ###
+/nbproject/private/
+/nbbuild/
+/dist/
+/nbdist/
+/.nb-gradle/
+build/
+!**/src/main/**/build/
+!**/src/test/**/build/
+
+### VS Code ###
+.vscode/
+target
+.svn
+*.log
+*/*.log
+.DS_Store
+*/velocity.log.*
+/out
+/.idea/*
+/.idea/ant.xml
+/.idea/misc.xml
+/.idea/workspace.xml
+/.idea/compiler.xml
+/.idea/modules.xml
+/.idea/libraries/Maven*.xml
+/.idea/artifacts/*.xml
+/*.iws
+/ais-hardware.eml
+tiangong-js
+rebel.xml
+.husx
+/.husx/docs/bash/
+/.husx/docs/bash/*/*.sh
+/.husx/docs/bash/*/*/*.sh
+/web/src/test/java/com/cmcc/coc/ummp/check/Test.java
diff --git a/.mvn/wrapper/MavenWrapperDownloader.java b/.mvn/wrapper/MavenWrapperDownloader.java
new file mode 100644
index 0000000..a45eb6b
--- /dev/null
+++ b/.mvn/wrapper/MavenWrapperDownloader.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2007-present the original author or authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.*;
+import java.io.*;
+import java.nio.channels.*;
+import java.util.Properties;
+
+public class MavenWrapperDownloader {
+
+ private static final String WRAPPER_VERSION = "0.5.6";
+ /**
+ * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided.
+ */
+ private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/"
+ + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar";
+
+ /**
+ * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to
+ * use instead of the default one.
+ */
+ private static final String MAVEN_WRAPPER_PROPERTIES_PATH =
+ ".mvn/wrapper/maven-wrapper.properties";
+
+ /**
+ * Path where the maven-wrapper.jar will be saved to.
+ */
+ private static final String MAVEN_WRAPPER_JAR_PATH =
+ ".mvn/wrapper/maven-wrapper.jar";
+
+ /**
+ * Name of the property which should be used to override the default download url for the wrapper.
+ */
+ private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl";
+
+ public static void main(String args[]) {
+ System.out.println("- Downloader started");
+ File baseDirectory = new File(args[0]);
+ System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath());
+
+ // If the maven-wrapper.properties exists, read it and check if it contains a custom
+ // wrapperUrl parameter.
+ File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH);
+ String url = DEFAULT_DOWNLOAD_URL;
+ if (mavenWrapperPropertyFile.exists()) {
+ FileInputStream mavenWrapperPropertyFileInputStream = null;
+ try {
+ mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile);
+ Properties mavenWrapperProperties = new Properties();
+ mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream);
+ url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url);
+ } catch (IOException e) {
+ System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'");
+ } finally {
+ try {
+ if (mavenWrapperPropertyFileInputStream != null) {
+ mavenWrapperPropertyFileInputStream.close();
+ }
+ } catch (IOException e) {
+ // Ignore ...
+ }
+ }
+ }
+ System.out.println("- Downloading from: " + url);
+
+ File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH);
+ if (!outputFile.getParentFile().exists()) {
+ if (!outputFile.getParentFile().mkdirs()) {
+ System.out.println(
+ "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'");
+ }
+ }
+ System.out.println("- Downloading to: " + outputFile.getAbsolutePath());
+ try {
+ downloadFileFromURL(url, outputFile);
+ System.out.println("Done");
+ System.exit(0);
+ } catch (Throwable e) {
+ System.out.println("- Error downloading");
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private static void downloadFileFromURL(String urlString, File destination) throws Exception {
+ if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) {
+ String username = System.getenv("MVNW_USERNAME");
+ char[] password = System.getenv("MVNW_PASSWORD").toCharArray();
+ Authenticator.setDefault(new Authenticator() {
+ @Override
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(username, password);
+ }
+ });
+ }
+ URL website = new URL(urlString);
+ ReadableByteChannel rbc;
+ rbc = Channels.newChannel(website.openStream());
+ FileOutputStream fos = new FileOutputStream(destination);
+ fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE);
+ fos.close();
+ rbc.close();
+ }
+
+}
diff --git a/.mvn/wrapper/maven-wrapper.jar b/.mvn/wrapper/maven-wrapper.jar
new file mode 100644
index 0000000..2cc7d4a
Binary files /dev/null and b/.mvn/wrapper/maven-wrapper.jar differ
diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties
new file mode 100644
index 0000000..ffdc10e
--- /dev/null
+++ b/.mvn/wrapper/maven-wrapper.properties
@@ -0,0 +1,2 @@
+distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip
+wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar
diff --git a/README.md b/README.md
deleted file mode 100644
index dc2971f..0000000
--- a/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# javaEE
-基i础 test is git
diff --git a/gateway/pom.xml b/gateway/pom.xml
new file mode 100644
index 0000000..7ddac45
--- /dev/null
+++ b/gateway/pom.xml
@@ -0,0 +1,96 @@
+
+
+
+ test
+ com.xgh
+ 0.0.1-SNAPSHOT
+
+ 4.0.0
+
+ gateway
+
+
+ 8
+ 8
+ UTF-8
+
+
+
+ io.netty
+ netty-all
+ 4.1.77.Final
+
+
+ com.alibaba
+ fastjson
+ 2.0.7
+
+
+ org.slf4j
+ slf4j-api
+ 1.7.36
+ jar
+ compile
+
+
+ ch.qos.logback
+ logback-core
+ 1.2.11
+ jar
+
+
+ ch.qos.logback
+ logback-classic
+ 1.2.11
+ jar
+
+
+ junit
+ junit
+ 4.13.2
+ test
+
+
+ org.apache.dubbo
+ dubbo
+ 2.7.5
+
+
+ org.apache.zookeeper
+ zookeeper
+ 3.4.13
+
+
+ org.apache.curator
+ curator-framework
+ 4.0.1
+
+
+ org.apache.curator
+ curator-recipes
+ 4.0.1
+
+
+ cglib
+ cglib
+ 3.3.0
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.3
+
+ ${maven.compiler.source}
+ ${maven.compiler.target}
+ ${project.build.sourceEncoding}
+
+
+
+
+
\ No newline at end of file
diff --git a/gateway/src/main/java/com/xgh/gateway/day01/BaseHandler.java b/gateway/src/main/java/com/xgh/gateway/day01/BaseHandler.java
new file mode 100644
index 0000000..b583402
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day01/BaseHandler.java
@@ -0,0 +1,19 @@
+package com.xgh.gateway.day01;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * 数据处理器 基类
+ * @param
+ */
+public abstract class BaseHandler extends SimpleChannelInboundHandler {
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, T msg) throws Exception {
+ session(ctx,ctx.channel(),msg);
+ }
+
+ protected abstract void session(ChannelHandlerContext ctx, final Channel channel, T request);
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day01/SessionChannelInitializer.java b/gateway/src/main/java/com/xgh/gateway/day01/SessionChannelInitializer.java
new file mode 100644
index 0000000..c8f3477
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day01/SessionChannelInitializer.java
@@ -0,0 +1,24 @@
+package com.xgh.gateway.day01;
+
+import com.xgh.gateway.day01.handlers.SessionServerHandler;
+import io.netty.channel.ChannelHandler;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelPipeline;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+
+
+public class SessionChannelInitializer extends ChannelInitializer {
+
+ @Override
+ protected void initChannel(SocketChannel channel) throws Exception {
+ ChannelPipeline line = channel.pipeline();
+ line.addLast(new HttpRequestDecoder())
+ .addLast(new HttpResponseEncoder())
+ .addLast(new HttpObjectAggregator(1024*1024))
+ .addLast(new SessionServerHandler());
+ }
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day01/SessionServer.java b/gateway/src/main/java/com/xgh/gateway/day01/SessionServer.java
new file mode 100644
index 0000000..5c4e0a4
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day01/SessionServer.java
@@ -0,0 +1,54 @@
+package com.xgh.gateway.day01;
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+
+import java.util.concurrent.Callable;
+
+
+/**
+ * 网关会话服务
+ * @author xgh
+ */
+public class SessionServer implements Callable {
+
+ private final Logger logger = LoggerFactory.getLogger(SessionServer.class);
+
+ private final EventLoopGroup boss = new NioEventLoopGroup(1);
+
+ private final EventLoopGroup work = new NioEventLoopGroup();
+
+ private Channel channel;
+
+
+ @Override
+ public Channel call() throws Exception {
+ ChannelFuture channelFuture = null;
+ try {
+ ServerBootstrap b = new ServerBootstrap();
+ b.group(boss,work)
+ .channel(NioServerSocketChannel.class)
+ .option(ChannelOption.SO_BACKLOG,128)
+ .childHandler(new SessionChannelInitializer());
+ channelFuture = b.bind(7387).syncUninterruptibly();
+ this.channel = channelFuture.channel();
+ }catch (Exception e){
+ logger.error("socket server start error.",e);
+ }finally {
+ if(null != channelFuture || channelFuture.isSuccess()){
+ logger.error("socket server start done.");
+ }else {
+ logger.error("socket server start error.");
+ }
+ }
+ return channel;
+ }
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day01/handlers/SessionServerHandler.java b/gateway/src/main/java/com/xgh/gateway/day01/handlers/SessionServerHandler.java
new file mode 100644
index 0000000..2ef899d
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day01/handlers/SessionServerHandler.java
@@ -0,0 +1,42 @@
+package com.xgh.gateway.day01.handlers;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.xgh.gateway.day01.BaseHandler;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 会话处理器
+ */
+public class SessionServerHandler extends BaseHandler {
+
+ private final Logger logger = LoggerFactory.getLogger(SessionServerHandler.class);
+
+ @Override
+ protected void session(ChannelHandlerContext ctx, Channel channel, FullHttpRequest request) {
+ logger.info("网关接收请求uri:{} method:{}",request.uri(),request.method());
+ //返回信息处理
+ DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
+ //返回信息控制
+ response.content().writeBytes(JSON.toJSONBytes("你访问的路径被网关管理了uri:"+request.uri(), SerializerFeature.PrettyFormat));
+ //设置头部信息
+ HttpHeaders headers = response.headers();
+ //返回内容类型
+ headers.add(HttpHeaderNames.CONTENT_TYPE,HttpHeaderValues.APPLICATION_JSON+";charset=UTF-8");
+ //响应体长度
+ headers.add(HttpHeaderNames.CONTENT_LENGTH,response.content().readableBytes());
+ //配置链接持久
+ headers.add(HttpHeaderNames.CONNECTION,HttpHeaderValues.KEEP_ALIVE);
+ //配置跨域
+ headers.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN,"*");
+ headers.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS,"*");
+ headers.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS,"GET, POST, PUT, DELETE");
+ headers.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS,"true");
+
+ channel.writeAndFlush(response);
+ }
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/bind/GenericReferenceProxy.java b/gateway/src/main/java/com/xgh/gateway/day02/bind/GenericReferenceProxy.java
new file mode 100644
index 0000000..92c8abb
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/bind/GenericReferenceProxy.java
@@ -0,0 +1,45 @@
+package com.xgh.gateway.day02.bind;
+
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+import org.apache.dubbo.rpc.service.GenericService;
+
+import java.lang.reflect.Method;
+
+/**
+ * 泛化调用静态代理:方便做一些拦截处理,给http对应rpc调用,做一层代理控制
+ * 每调用到一个http对应的网关方法,就会代理的方式调用到rpc对应的泛化方法上
+ */
+public class GenericReferenceProxy implements MethodInterceptor {
+
+ /**
+ * rpc泛化调用服务
+ */
+ private final GenericService genericService;
+
+ /**
+ * rpc 泛化调用方法
+ */
+ private final String methodName;
+
+ public GenericReferenceProxy(GenericService genericService, String methodName) {
+ this.genericService = genericService;
+ this.methodName = methodName;
+ }
+
+
+ /**
+ *
+ * 做一层代理控制,后续不止是可以使用 Dubbo 泛化调用,也可以是其他服务的泛化调用
+ * 泛化调用文档:https://dubbo.apache.org/zh/docsv2.7/user/examples/generic-reference/
+ */
+ @Override
+ public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
+ Class>[] parameterTypes = method.getParameterTypes();
+ String[] parameters = new String[parameterTypes.length];
+ for (int i = 0; i < parameterTypes.length; i++) {
+ parameters[i] = parameterTypes[i].getName();
+ }
+ return genericService.$invoke(methodName,parameters,args);
+ }
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/bind/GenericReferenceProxyFactory.java b/gateway/src/main/java/com/xgh/gateway/day02/bind/GenericReferenceProxyFactory.java
new file mode 100644
index 0000000..62fe409
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/bind/GenericReferenceProxyFactory.java
@@ -0,0 +1,51 @@
+package com.xgh.gateway.day02.bind;
+
+
+import net.sf.cglib.core.Signature;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.InterfaceMaker;
+import org.apache.dubbo.rpc.service.GenericService;
+import org.objectweb.asm.Type;
+
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 泛化调用静态代理工厂
+ */
+public class GenericReferenceProxyFactory {
+
+ /**
+ * rpc 泛化调用服务
+ */
+ private final GenericService genericService;
+
+
+ private final Map genericReferenceCache = new ConcurrentHashMap<>();
+
+
+ public GenericReferenceProxyFactory(GenericService genericService) {
+ this.genericService = genericService;
+ }
+
+ public IGenericReference newInstance(String method){
+ return genericReferenceCache.computeIfAbsent(method,k->{
+ //泛化调用
+ GenericReferenceProxy genericReferenceProxy = new GenericReferenceProxy(genericService, method);
+ //创建接口
+ InterfaceMaker interfaceMaker = new InterfaceMaker();
+ interfaceMaker.add(new Signature(method, Type.getType(String.class),new Type[]{ Type.getType(String.class)}),null);
+ Class> interfaceClass = interfaceMaker.create();
+ //代理对象
+ Enhancer enhancer = new Enhancer();
+ enhancer.setSuperclass(Object.class);
+ //IGenericReference 统一泛化接口
+ //interfaceClass 根据泛化调用注册信息创建的接口,建立http->rpc关联
+ enhancer.setInterfaces(new Class[]{IGenericReference.class,interfaceClass});
+ enhancer.setCallback(genericReferenceProxy);
+ return (IGenericReference) enhancer.create();
+ });
+ }
+
+
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/bind/GenericReferenceRegistry.java b/gateway/src/main/java/com/xgh/gateway/day02/bind/GenericReferenceRegistry.java
new file mode 100644
index 0000000..c569d31
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/bind/GenericReferenceRegistry.java
@@ -0,0 +1,52 @@
+package com.xgh.gateway.day02.bind;
+
+import com.xgh.gateway.day02.session.Configuration;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.config.utils.ReferenceConfigCache;
+import org.apache.dubbo.rpc.service.GenericService;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 泛化调用注册器
+ */
+public class GenericReferenceRegistry {
+
+ private final Configuration configuration;
+
+
+ public GenericReferenceRegistry(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ private final Map knownGenericReferences = new HashMap<>();
+
+ public IGenericReference getGenericReference(String methodName){
+ GenericReferenceProxyFactory genericReferenceProxyFactory = knownGenericReferences.get(methodName);
+ if(genericReferenceProxyFactory == null){
+ throw new RuntimeException("Type "+methodName +"is not know to the GenericReferenceRegistry.");
+ }
+ return genericReferenceProxyFactory.newInstance(methodName);
+ }
+
+
+ public void addGenericReference(String application,String interfaceName,String methodName){
+ //获取基础服务
+ ApplicationConfig applicationConfig = configuration.getApplicationConfig(application);
+ RegistryConfig registryConfig = configuration.getRegistryConfig(application);
+ ReferenceConfig referenceConfig = configuration.getReferenceConfig(interfaceName);
+ //构建dubbo服务
+ DubboBootstrap bootstrap = DubboBootstrap.getInstance();
+ bootstrap.application(applicationConfig).registry(registryConfig).reference(referenceConfig).start();
+ //调用泛化服务
+ ReferenceConfigCache cache = ReferenceConfigCache.getCache();
+ GenericService genericService = cache.get(referenceConfig);
+ //创建并保存到工厂
+ knownGenericReferences.put(methodName,new GenericReferenceProxyFactory(genericService));
+ }
+
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/bind/IGenericReference.java b/gateway/src/main/java/com/xgh/gateway/day02/bind/IGenericReference.java
new file mode 100644
index 0000000..a71538f
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/bind/IGenericReference.java
@@ -0,0 +1,9 @@
+package com.xgh.gateway.day02.bind;
+
+/**
+ * 统一泛化接口
+ */
+public interface IGenericReference {
+
+ String $invoke(String args);
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/session/BaseHandler.java b/gateway/src/main/java/com/xgh/gateway/day02/session/BaseHandler.java
new file mode 100644
index 0000000..3253e05
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/session/BaseHandler.java
@@ -0,0 +1,20 @@
+package com.xgh.gateway.day02.session;
+
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.SimpleChannelInboundHandler;
+
+/**
+ * 数据处理基类
+ * @param
+ */
+public abstract class BaseHandler extends SimpleChannelInboundHandler {
+
+
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, T msg) throws Exception {
+ session(ctx,ctx.channel(),msg);
+ }
+
+ protected abstract void session(ChannelHandlerContext ctx, Channel channel, T request);
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/session/Configuration.java b/gateway/src/main/java/com/xgh/gateway/day02/session/Configuration.java
new file mode 100644
index 0000000..9790beb
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/session/Configuration.java
@@ -0,0 +1,75 @@
+package com.xgh.gateway.day02.session;
+
+
+import com.xgh.gateway.day02.bind.GenericReferenceRegistry;
+import com.xgh.gateway.day02.bind.IGenericReference;
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.rpc.service.GenericService;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 会话生命周期配置类
+ */
+public class Configuration {
+
+ private final GenericReferenceRegistry registry = new GenericReferenceRegistry(this);
+
+ //rpc 应用服务配置项api-gateway-test
+ private final Map applicationConfigMap = new HashMap<>();
+
+ /**
+ * 注册中心配置项 zookeeper:127.0.0.1:2181
+ */
+ private final Map registryConfigMap = new HashMap<>();
+
+ /**
+ * rpc 泛化服务配置项目 cn.gateway.rpc.IActivityBooth
+ */
+ private final Map> referenceConfigMap = new HashMap<>();
+
+
+ public Configuration(){
+ ApplicationConfig application = new ApplicationConfig();
+ application.setName("api-gateway-test");
+ application.setQosEnable(false);
+
+ RegistryConfig registryConfig = new RegistryConfig();
+ registryConfig.setAddress("zookeeper://127.0.0.1:2181");
+ registryConfig.setRegister(false);
+
+ ReferenceConfig reference = new ReferenceConfig<>();
+ reference.setInterface("");
+ reference.setVersion("1.0.1");
+ reference.setGeneric("true");
+
+ applicationConfigMap.put("api-gateway-test",application);
+ registryConfigMap.put("api-gateway-test",registryConfig);
+ referenceConfigMap.put("",reference);
+ }
+
+ public ApplicationConfig getApplicationConfig(String applicationName){
+ return applicationConfigMap.get(applicationName);
+ }
+
+ public RegistryConfig getRegistryConfig(String applicationName){
+ return registryConfigMap.get(applicationName);
+ }
+
+ public ReferenceConfig getReferenceConfig(String interfaceName){
+ return referenceConfigMap.get(interfaceName);
+ }
+
+
+ public void addGenericReference(String application,String interfaceName,String methodName){
+ registry.addGenericReference(application,interfaceName,methodName);
+ }
+
+ public IGenericReference getGenericReference(String methodName){
+ return registry.getGenericReference(methodName);
+ }
+
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/session/GenericReferenceSessionFactoryBuilder.java b/gateway/src/main/java/com/xgh/gateway/day02/session/GenericReferenceSessionFactoryBuilder.java
new file mode 100644
index 0000000..5f9165b
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/session/GenericReferenceSessionFactoryBuilder.java
@@ -0,0 +1,25 @@
+package com.xgh.gateway.day02.session;
+
+
+import com.xgh.gateway.day02.session.defaults.GenericReferenceSessionFactory;
+import io.netty.channel.Channel;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+/**
+ * 会话工厂构建类
+ */
+public class GenericReferenceSessionFactoryBuilder {
+
+ public Future build(Configuration configuration) {
+ IGenericReferenceSessionFactory genericReferenceSessionFactory = new GenericReferenceSessionFactory(configuration);
+ try {
+ return genericReferenceSessionFactory.openSession();
+ } catch (ExecutionException | InterruptedException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/session/IGenericReferenceSessionFactory.java b/gateway/src/main/java/com/xgh/gateway/day02/session/IGenericReferenceSessionFactory.java
new file mode 100644
index 0000000..d0cc1b0
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/session/IGenericReferenceSessionFactory.java
@@ -0,0 +1,17 @@
+package com.xgh.gateway.day02.session;
+
+import io.netty.channel.Channel;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+
+/**
+ * 泛化调用工厂接口
+ */
+public interface IGenericReferenceSessionFactory {
+
+ Future openSession() throws ExecutionException, InterruptedException;
+
+
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/session/SessionChannelInitializer.java b/gateway/src/main/java/com/xgh/gateway/day02/session/SessionChannelInitializer.java
new file mode 100644
index 0000000..e0e9141
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/session/SessionChannelInitializer.java
@@ -0,0 +1,28 @@
+package com.xgh.gateway.day02.session;
+
+import com.xgh.gateway.day02.session.handlers.SessionServerHandler;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.handler.codec.http.HttpObjectAggregator;
+import io.netty.handler.codec.http.HttpRequestDecoder;
+import io.netty.handler.codec.http.HttpResponseEncoder;
+
+/**
+ * 会话管道初始化类
+ */
+public class SessionChannelInitializer extends ChannelInitializer {
+
+ private final Configuration configuration;
+
+ public SessionChannelInitializer(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ protected void initChannel(SocketChannel channel) throws Exception {
+ channel.pipeline().addLast(new HttpRequestDecoder())
+ .addLast(new HttpResponseEncoder())
+ .addLast(new HttpObjectAggregator(1024*1024))
+ .addLast(new SessionServerHandler(configuration));
+ }
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/session/SessionServer.java b/gateway/src/main/java/com/xgh/gateway/day02/session/SessionServer.java
new file mode 100644
index 0000000..71fda8e
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/session/SessionServer.java
@@ -0,0 +1,59 @@
+package com.xgh.gateway.day02.session;
+
+
+import io.netty.bootstrap.ServerBootstrap;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.nio.NioServerSocketChannel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.Callable;
+
+/**
+ * 网关会话服务
+ */
+public class SessionServer implements Callable {
+
+ private final Logger logger = LoggerFactory.getLogger(SessionServer.class);
+
+ private final Configuration configuration;
+
+ private final EventLoopGroup boss = new NioEventLoopGroup(1);
+
+ private final EventLoopGroup work = new NioEventLoopGroup();
+
+ private Channel channel;
+
+ public SessionServer(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+
+ @Override
+ public Channel call() throws Exception {
+ ChannelFuture channelFuture = null;
+ try {
+ ServerBootstrap b =new ServerBootstrap();
+ b.group(boss,work)
+ .channel(NioServerSocketChannel.class)
+ .option(ChannelOption.SO_BACKLOG,128)
+ .childHandler(new SessionChannelInitializer(configuration));
+ channelFuture = b.bind(new InetSocketAddress(7397)).syncUninterruptibly();
+ this.channel = channelFuture.channel();
+ }catch (Exception e){
+ logger.error("socket server start error .",e);
+ }finally {
+ if(null != channelFuture && channelFuture.isSuccess()){
+ logger.info("socket server start done.");
+ }else {
+ logger.error("socket server start error.");
+ }
+ }
+ return channel;
+ }
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/session/defaults/GenericReferenceSessionFactory.java b/gateway/src/main/java/com/xgh/gateway/day02/session/defaults/GenericReferenceSessionFactory.java
new file mode 100644
index 0000000..92135d6
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/session/defaults/GenericReferenceSessionFactory.java
@@ -0,0 +1,39 @@
+package com.xgh.gateway.day02.session.defaults;
+
+import com.xgh.gateway.day02.session.Configuration;
+import com.xgh.gateway.day02.session.IGenericReferenceSessionFactory;
+import com.xgh.gateway.day02.session.SessionServer;
+import io.netty.channel.Channel;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+public class GenericReferenceSessionFactory implements IGenericReferenceSessionFactory {
+
+ private final Logger logger = LoggerFactory.getLogger(GenericReferenceSessionFactory.class);
+
+ private final Configuration configuration;
+
+ public GenericReferenceSessionFactory(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+ @Override
+ public Future openSession() throws ExecutionException, InterruptedException {
+ SessionServer server = new SessionServer(configuration);
+ Future future = Executors.newFixedThreadPool(2).submit(server);
+ Channel channel = future.get();
+ if(null == channel){
+ throw new RuntimeException("netty server start error channel is null");
+ }
+ while (!channel.isActive()){
+ logger.info("netty server gateway start ing ...");
+ Thread.sleep(500);
+ }
+ logger.info("netty server gateway start Done:{}",channel.localAddress());
+ return future;
+ }
+}
diff --git a/gateway/src/main/java/com/xgh/gateway/day02/session/handlers/SessionServerHandler.java b/gateway/src/main/java/com/xgh/gateway/day02/session/handlers/SessionServerHandler.java
new file mode 100644
index 0000000..b29226a
--- /dev/null
+++ b/gateway/src/main/java/com/xgh/gateway/day02/session/handlers/SessionServerHandler.java
@@ -0,0 +1,58 @@
+package com.xgh.gateway.day02.session.handlers;
+
+import com.alibaba.fastjson.JSON;
+import com.alibaba.fastjson.serializer.SerializerFeature;
+import com.xgh.gateway.day02.bind.IGenericReference;
+import com.xgh.gateway.day02.session.BaseHandler;
+import com.xgh.gateway.day02.session.Configuration;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.handler.codec.http.*;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * 会话服务处理器
+ */
+public class SessionServerHandler extends BaseHandler {
+
+ private final Logger logger = LoggerFactory.getLogger(SessionServerHandler.class);
+
+ private final Configuration configuration;
+
+ public SessionServerHandler(Configuration configuration) {
+ this.configuration = configuration;
+ }
+
+
+ @Override
+ protected void session(ChannelHandlerContext ctx, Channel channel, FullHttpRequest request) {
+ logger.info("网关接收请求uri:{},method:{}",request.uri(),request.method());
+ //返回信息控制
+ String methodName = request.uri().substring(1);
+ if(methodName.equals("favicon.ico")){return;}
+ //返回信息处理
+ DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1, HttpResponseStatus.OK);
+ //服务泛化调用
+ IGenericReference reference = configuration.getGenericReference("sayHi");
+ String result = reference.$invoke("test)" + " " + System.currentTimeMillis());
+
+ //设置回写数据
+ response.content().writeBytes(JSON.toJSONBytes(result, SerializerFeature.PrettyFormat));
+ //头部信息设置
+ HttpHeaders heads = response.headers();
+ //返回内容类型
+ heads.add(HttpHeaderNames.CONTENT_TYPE,HttpHeaderValues.APPLICATION_JSON+";charset=UTF-8");
+ //响应体长度
+ heads.add(HttpHeaderNames.CONTENT_LENGTH,response.content().readableBytes());
+ //配置持久化
+ heads.add(HttpHeaderNames.CONNECTION,HttpHeaderValues.KEEP_ALIVE);
+ //配置跨域
+ heads.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
+ heads.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_HEADERS, "*");
+ heads.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_METHODS, "GET, POST, PUT, DELETE");
+ heads.add(HttpHeaderNames.ACCESS_CONTROL_ALLOW_CREDENTIALS, "true");
+
+ channel.writeAndFlush(response);
+ }
+}
diff --git a/gateway/src/test/java/com/xgh/gateway/day01/ApiTest.java b/gateway/src/test/java/com/xgh/gateway/day01/ApiTest.java
new file mode 100644
index 0000000..d2b54f0
--- /dev/null
+++ b/gateway/src/test/java/com/xgh/gateway/day01/ApiTest.java
@@ -0,0 +1,37 @@
+package com.xgh.gateway.day01;
+
+import io.netty.channel.Channel;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.Future;
+
+/**
+ * @description 测试
+ */
+public class ApiTest {
+
+ private final Logger logger = LoggerFactory.getLogger(ApiTest.class);
+
+ @Test
+ public void test() throws ExecutionException, InterruptedException {
+ SessionServer server = new SessionServer();
+
+ Future future = Executors.newFixedThreadPool(2).submit(server);
+ Channel channel = future.get();
+
+ if (null == channel) throw new RuntimeException("netty server start error channel is null");
+
+ while (!channel.isActive()) {
+ logger.info("NettyServer启动服务 ...");
+ Thread.sleep(500);
+ }
+ logger.info("NettyServer启动服务完成 {}", channel.localAddress());
+
+ Thread.sleep(Long.MAX_VALUE);
+ }
+
+}
\ No newline at end of file
diff --git a/gateway/src/test/java/com/xgh/gateway/day02/ApiTest.java b/gateway/src/test/java/com/xgh/gateway/day02/ApiTest.java
new file mode 100644
index 0000000..aa59e98
--- /dev/null
+++ b/gateway/src/test/java/com/xgh/gateway/day02/ApiTest.java
@@ -0,0 +1,30 @@
+package com.xgh.gateway.day02;
+
+import com.xgh.gateway.day02.session.Configuration;
+import com.xgh.gateway.day02.session.GenericReferenceSessionFactoryBuilder;
+import io.netty.channel.Channel;
+import org.junit.Test;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Future;
+
+
+public class ApiTest {
+
+ private final Logger logger = LoggerFactory.getLogger(ApiTest.class);
+
+ @Test
+ public void test_GenericReference() throws InterruptedException, ExecutionException {
+ Configuration configuration = new Configuration();
+ configuration.addGenericReference("api-gateway-test", "cn.bugstack.gateway.rpc.IActivityBooth", "sayHi");
+
+ GenericReferenceSessionFactoryBuilder builder = new GenericReferenceSessionFactoryBuilder();
+ Future future = builder.build(configuration);
+
+ logger.info("服务启动完成 {}", future.get().id());
+
+ Thread.sleep(Long.MAX_VALUE);
+ }
+}
\ No newline at end of file
diff --git a/gateway/src/test/java/com/xgh/gateway/day02/CglibTest.java b/gateway/src/test/java/com/xgh/gateway/day02/CglibTest.java
new file mode 100644
index 0000000..98b4361
--- /dev/null
+++ b/gateway/src/test/java/com/xgh/gateway/day02/CglibTest.java
@@ -0,0 +1,43 @@
+package com.xgh.gateway.day02;
+
+import com.alibaba.fastjson.JSON;
+import net.sf.cglib.core.Signature;
+import net.sf.cglib.proxy.Enhancer;
+import net.sf.cglib.proxy.InterfaceMaker;
+import net.sf.cglib.proxy.MethodInterceptor;
+import net.sf.cglib.proxy.MethodProxy;
+import org.junit.Test;
+import org.objectweb.asm.Type;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+public class CglibTest implements MethodInterceptor {
+
+ @Override
+ public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
+ return JSON.toJSONString(objects);
+ }
+
+ @Test
+ public void test_cglib() throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
+ // 定义接口
+ InterfaceMaker interfaceMaker = new InterfaceMaker();
+ interfaceMaker.add(new Signature("sayHi", Type.getType(String.class), new Type[]{Type.getType(String.class)}), null);
+ Class> interfaceClass = interfaceMaker.create();
+
+ // 创建代理
+ Enhancer enhancer = new Enhancer();
+ enhancer.setSuperclass(Object.class);
+ enhancer.setInterfaces(new Class[]{interfaceClass});
+ enhancer.setCallback(this);
+ Object obj = enhancer.create();
+
+ // 调用方法
+ Method method = obj.getClass().getMethod("sayHi", String.class);
+ Object result = method.invoke(obj,"hi xiaofuge");
+
+ System.out.println(result);
+ }
+
+}
\ No newline at end of file
diff --git a/gateway/src/test/java/com/xgh/gateway/day02/RPCTest.java b/gateway/src/test/java/com/xgh/gateway/day02/RPCTest.java
new file mode 100644
index 0000000..c74b0b0
--- /dev/null
+++ b/gateway/src/test/java/com/xgh/gateway/day02/RPCTest.java
@@ -0,0 +1,47 @@
+package com.xgh.gateway.day02;
+
+import org.apache.dubbo.config.ApplicationConfig;
+import org.apache.dubbo.config.ReferenceConfig;
+import org.apache.dubbo.config.RegistryConfig;
+import org.apache.dubbo.config.bootstrap.DubboBootstrap;
+import org.apache.dubbo.config.utils.ReferenceConfigCache;
+import org.apache.dubbo.rpc.service.GenericService;
+import org.junit.Test;
+
+/**
+ * 泛化调用测试
+ * 官网案例:https://dubbo.apache.org/zh/docs/advanced/generic-reference/
+ */
+public class RPCTest {
+
+ @Test
+ public void test_rpc() {
+
+ ApplicationConfig application = new ApplicationConfig();
+ application.setName("api-gateway-test");
+ application.setQosEnable(false);
+
+ RegistryConfig registry = new RegistryConfig();
+ registry.setAddress("zookeeper://127.0.0.1:2181");
+ registry.setRegister(false);
+
+ ReferenceConfig reference = new ReferenceConfig<>();
+ reference.setInterface("cn.bugstack.gateway.rpc.IActivityBooth");
+ reference.setVersion("1.0.0");
+ reference.setGeneric("true");
+
+ DubboBootstrap bootstrap = DubboBootstrap.getInstance();
+ bootstrap.application(application)
+ .registry(registry)
+ .reference(reference)
+ .start();
+
+ ReferenceConfigCache cache = ReferenceConfigCache.getCache();
+ GenericService genericService = cache.get(reference);
+
+ Object result = genericService.$invoke("sayHi", new String[]{"java.lang.String"}, new Object[]{"world"});
+
+ System.out.println(result);
+ }
+
+}
diff --git a/mvnw b/mvnw
new file mode 100644
index 0000000..3c8a553
--- /dev/null
+++ b/mvnw
@@ -0,0 +1,322 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+# JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+# M2_HOME - location of maven2's installed home dir
+# MAVEN_OPTS - parameters passed to the Java VM when running Maven
+# e.g. to debug Maven itself, use
+# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+# MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ]; then
+
+ if [ -f /etc/mavenrc ]; then
+ . /etc/mavenrc
+ fi
+
+ if [ -f "$HOME/.mavenrc" ]; then
+ . "$HOME/.mavenrc"
+ fi
+
+fi
+
+# OS specific support. $var _must_ be set to either true or false.
+cygwin=false
+darwin=false
+mingw=false
+case "$(uname)" in
+CYGWIN*) cygwin=true ;;
+MINGW*) mingw=true ;;
+Darwin*)
+ darwin=true
+ # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+ # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+ if [ -z "$JAVA_HOME" ]; then
+ if [ -x "/usr/libexec/java_home" ]; then
+ export JAVA_HOME="$(/usr/libexec/java_home)"
+ else
+ export JAVA_HOME="/Library/Java/Home"
+ fi
+ fi
+ ;;
+esac
+
+if [ -z "$JAVA_HOME" ]; then
+ if [ -r /etc/gentoo-release ]; then
+ JAVA_HOME=$(java-config --jre-home)
+ fi
+fi
+
+if [ -z "$M2_HOME" ]; then
+ ## resolve links - $0 may be a link to maven's home
+ PRG="$0"
+
+ # need this for relative symlinks
+ while [ -h "$PRG" ]; do
+ ls=$(ls -ld "$PRG")
+ link=$(expr "$ls" : '.*-> \(.*\)$')
+ if expr "$link" : '/.*' >/dev/null; then
+ PRG="$link"
+ else
+ PRG="$(dirname "$PRG")/$link"
+ fi
+ done
+
+ saveddir=$(pwd)
+
+ M2_HOME=$(dirname "$PRG")/..
+
+ # make it fully qualified
+ M2_HOME=$(cd "$M2_HOME" && pwd)
+
+ cd "$saveddir"
+ # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=$(cygpath --unix "$M2_HOME")
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME="$( (
+ cd "$M2_HOME"
+ pwd
+ ))"
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME="$( (
+ cd "$JAVA_HOME"
+ pwd
+ ))"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ javaExecutable="$(which javac)"
+ if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then
+ # readlink(1) is not available as standard on Solaris 10.
+ readLink=$(which readlink)
+ if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then
+ if $darwin; then
+ javaHome="$(dirname \"$javaExecutable\")"
+ javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac"
+ else
+ javaExecutable="$(readlink -f \"$javaExecutable\")"
+ fi
+ javaHome="$(dirname \"$javaExecutable\")"
+ javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+ JAVA_HOME="$javaHome"
+ export JAVA_HOME
+ fi
+ fi
+fi
+
+if [ -z "$JAVACMD" ]; then
+ if [ -n "$JAVA_HOME" ]; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ else
+ JAVACMD="$(which java)"
+ fi
+fi
+
+if [ ! -x "$JAVACMD" ]; then
+ echo "Error: JAVA_HOME is not defined correctly." >&2
+ echo " We cannot execute $JAVACMD" >&2
+ exit 1
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+ echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+ if [ -z "$1" ]; then
+ echo "Path not specified to find_maven_basedir"
+ return 1
+ fi
+
+ basedir="$1"
+ wdir="$1"
+ while [ "$wdir" != '/' ]; do
+ if [ -d "$wdir"/.mvn ]; then
+ basedir=$wdir
+ break
+ fi
+ # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+ if [ -d "${wdir}" ]; then
+ wdir=$(
+ cd "$wdir/.."
+ pwd
+ )
+ fi
+ # end of workaround
+ done
+ echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+ if [ -f "$1" ]; then
+ echo "$(tr -s '\n' ' ' <"$1")"
+ fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(pwd)")
+if [ -z "$BASE_DIR" ]; then
+ exit 1
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found .mvn/wrapper/maven-wrapper.jar"
+ fi
+else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+ fi
+ if [ -n "$MVNW_REPOURL" ]; then
+ jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ else
+ jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ fi
+ while IFS="=" read key value; do
+ case "$key" in wrapperUrl)
+ jarUrl="$value"
+ break
+ ;;
+ esac
+ done <"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Downloading from: $jarUrl"
+ fi
+ wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+ if $cygwin; then
+ wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+ fi
+
+ if command -v wget >/dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found wget ... using wget"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ wget "$jarUrl" -O "$wrapperJarPath"
+ else
+ wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+ fi
+ elif command -v curl >/dev/null; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Found curl ... using curl"
+ fi
+ if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+ curl -o "$wrapperJarPath" "$jarUrl" -f
+ else
+ curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+ fi
+
+ else
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo "Falling back to using Java to download"
+ fi
+ javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+ # For Cygwin, switch paths to Windows format before running javac
+ if $cygwin; then
+ javaClass=$(cygpath --path --windows "$javaClass")
+ fi
+ if [ -e "$javaClass" ]; then
+ if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Compiling MavenWrapperDownloader.java ..."
+ fi
+ # Compiling the Java class
+ ("$JAVA_HOME/bin/javac" "$javaClass")
+ fi
+ if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+ # Running the downloader
+ if [ "$MVNW_VERBOSE" = true ]; then
+ echo " - Running MavenWrapperDownloader.java ..."
+ fi
+ ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+ fi
+ fi
+ fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+ echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+ [ -n "$M2_HOME" ] &&
+ M2_HOME=$(cygpath --path --windows "$M2_HOME")
+ [ -n "$JAVA_HOME" ] &&
+ JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+ [ -n "$CLASSPATH" ] &&
+ CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+ [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+ MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+ $MAVEN_OPTS \
+ -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+ "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+ ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"
diff --git a/mvnw.cmd b/mvnw.cmd
new file mode 100644
index 0000000..c8d4337
--- /dev/null
+++ b/mvnw.cmd
@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements. See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership. The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License. You may obtain a copy of the License at
+@REM
+@REM https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied. See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+ IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Found %WRAPPER_JAR%
+ )
+) else (
+ if not "%MVNW_REPOURL%" == "" (
+ SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+ )
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Couldn't find %WRAPPER_JAR%, downloading it ...
+ echo Downloading from: %DOWNLOAD_URL%
+ )
+
+ powershell -Command "&{"^
+ "$webclient = new-object System.Net.WebClient;"^
+ "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+ "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+ "}"^
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+ "}"
+ if "%MVNW_VERBOSE%" == "true" (
+ echo Finished downloading %WRAPPER_JAR%
+ )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..9e0ee98
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,109 @@
+
+
+ 4.0.0
+ pom
+
+ gateway
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.4.5
+
+
+ com.xgh
+ test
+ 0.0.1-SNAPSHOT
+ test
+ Demo project for Spring Boot
+
+ 1.8
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+ org.springframework.restdocs
+ spring-restdocs-mockmvc
+ test
+
+
+ org.springframework.boot
+ spring-boot
+ 2.4.5
+
+
+ org.projectlombok
+ lombok
+
+
+ org.assertj
+ assertj-core
+
+
+ junit
+ junit
+
+
+
+ com.google.guava
+ guava-testlib
+ 30.1.1-jre
+ test
+
+
+ cn.hutool
+ hutool-core
+ 5.6.3
+
+
+ redis.clients
+ jedis
+ 2.7.1
+
+
+
+
+
+
+ org.asciidoctor
+ asciidoctor-maven-plugin
+ 1.5.8
+
+
+ generate-docs
+ prepare-package
+
+ process-asciidoc
+
+
+ html
+ book
+
+
+
+
+
+ org.springframework.restdocs
+ spring-restdocs-asciidoctor
+ ${spring-restdocs.version}
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
diff --git a/src/main/java/com/xgh/test/TestApplication.java b/src/main/java/com/xgh/test/TestApplication.java
new file mode 100644
index 0000000..863fb1a
--- /dev/null
+++ b/src/main/java/com/xgh/test/TestApplication.java
@@ -0,0 +1,13 @@
+package com.xgh.test;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class TestApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(TestApplication.class, args);
+ }
+
+}
diff --git a/src/main/java/com/xgh/test/algorithms/array/ArrayList.java b/src/main/java/com/xgh/test/algorithms/array/ArrayList.java
new file mode 100644
index 0000000..e43c2fb
--- /dev/null
+++ b/src/main/java/com/xgh/test/algorithms/array/ArrayList.java
@@ -0,0 +1,65 @@
+package com.xgh.test.algorithms.array;
+
+import java.util.Arrays;
+
+public class ArrayList implements List {
+
+
+ /**
+ * 默认初始化空间
+ */
+ private static final int DEFAULT_CAPACITY = 10;
+
+ /**
+ * 空元素
+ */
+ private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
+
+ /**
+ * 元素数组缓冲区
+ */
+ transient Object[] elementData;
+
+ /**
+ * 元素个数
+ */
+ private int size;
+
+
+ @Override
+ public boolean add(E e) {
+ //确保内部容量
+ int minCapacity = size + 1;
+ if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
+ minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
+ }
+ //判断扩容
+ if (minCapacity - elementData.length > 0) {
+ int oldCapacity = elementData.length;
+ int newCapacity = oldCapacity + (oldCapacity >> 1);
+ if(newCapacity -minCapacity < 0){
+ newCapacity = minCapacity;
+ }
+ elementData = Arrays.copyOf(elementData,newCapacity);
+ }
+ //添加原始
+ elementData[size++] = e;
+ return true;
+ }
+
+ @Override
+ public E remove(int index) {
+ E oldElement =(E) elementData[index];
+ int numMoved = size - index - 1;
+ if(numMoved > 0){
+ System.arraycopy(elementData,index+1,elementData,index,numMoved);
+ }
+ elementData[--size] = null;
+ return oldElement;
+ }
+
+ @Override
+ public E get(int index) {
+ return (E) elementData[index];
+ }
+}
diff --git a/src/main/java/com/xgh/test/algorithms/array/List.java b/src/main/java/com/xgh/test/algorithms/array/List.java
new file mode 100644
index 0000000..98d4573
--- /dev/null
+++ b/src/main/java/com/xgh/test/algorithms/array/List.java
@@ -0,0 +1,10 @@
+package com.xgh.test.algorithms.array;
+
+public interface List{
+
+ boolean add(E e);
+
+ E remove(int index);
+
+ E get(int index);
+}
diff --git a/src/main/java/com/xgh/test/algorithms/link/LinkedList.java b/src/main/java/com/xgh/test/algorithms/link/LinkedList.java
new file mode 100644
index 0000000..50b7e0f
--- /dev/null
+++ b/src/main/java/com/xgh/test/algorithms/link/LinkedList.java
@@ -0,0 +1,164 @@
+package com.xgh.test.algorithms.link;
+
+
+public class LinkedList implements List {
+
+ transient int size = 0;
+
+ transient Node first;
+
+ transient Node last;
+
+ public LinkedList() {
+
+
+ }
+
+ void linkFirst(E e) {
+ final Node f = first;
+ final Node newNode = new Node<>(null, e, f);
+ first = newNode;
+ if (f == null) {
+ last = newNode;
+ } else {
+ f.prev = newNode;
+ }
+ size++;
+ }
+
+ void linkLast(E e) {
+ final Node l = last;
+ final Node newNode = new Node<>(l, e, null);
+ last = newNode;
+ if (l == null) {
+ first = newNode;
+ } else {
+ l.next = newNode;
+ }
+ size++;
+ }
+
+ @Override
+ public boolean add(E e) {
+ linkLast(e);
+ return true;
+ }
+
+ @Override
+ public boolean addFirst(E e) {
+ linkFirst(e);
+ return true;
+ }
+
+ @Override
+ public boolean addLast(E e) {
+ linkLast(e);
+ return true;
+ }
+
+ @Override
+ public boolean remove(Object o) {
+ if (o == null) {
+ for (Node x = first; x != null; x = x.next) {
+ if (x.item == null) {
+ unLink(x);
+ return true;
+ }
+ }
+ } else {
+ for (Node x = first; x != null; x = x.next) {
+ if (o.equals(x.item)) {
+ unLink(x);
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public E get(int index) {
+ return node(index).item;
+ }
+
+
+ @Override
+ public void printLinkList() {
+ if (this.size == 0) {
+ System.out.println("链表为空");
+ } else {
+ Node temp = first;
+ System.out.print("目前的列表,头节点:" + first.item + " 尾节点:" + last.item + " 整体:");
+ while (temp != null) {
+ System.out.print(temp.item + ",");
+ temp = temp.next;
+ }
+ System.out.println();
+ }
+ }
+
+ E unLink(Node x) {
+ final E element = x.item;
+ final Node prev = x.prev;
+ final Node next = x.next;
+ if (prev == null) {
+ first = next;
+ } else {
+ prev.next = next;
+ x.prev = null;
+ }
+ if (next == null) {
+ last = prev;
+ } else {
+ next.prev = prev;
+ x.next = null;
+ }
+ x.item = null;
+ size--;
+ return element;
+ }
+
+ private Node node(int index) {
+ Node x;
+ if (index < (size >> 1)) {
+ x = first;
+ for (int i = 0; i < index; i++) {
+ x = x.next;
+ }
+ } else {
+ x = last;
+ for (int i = size - 1; i > index; i--) {
+ x = x.prev;
+ }
+ }
+ return x;
+ }
+
+ private static class Node {
+ E item;
+ Node next;
+ Node prev;
+
+ public Node(Node prev, E item, Node next) {
+ this.item = item;
+ this.next = next;
+ this.prev = prev;
+ }
+ }
+
+ public static void main(String[] args) {
+ List list = new LinkedList<>();
+ // 添加元素
+ list.add("a");
+ list.addFirst("b");
+ list.addLast("c");
+ // 打印列表
+ list.printLinkList();
+ // 头插元素
+ list.addFirst("d");
+ // 删除元素
+ list.remove("b");
+ // 打印列表
+ list.printLinkList();
+ }
+}
diff --git a/src/main/java/com/xgh/test/algorithms/link/List.java b/src/main/java/com/xgh/test/algorithms/link/List.java
new file mode 100644
index 0000000..2670fca
--- /dev/null
+++ b/src/main/java/com/xgh/test/algorithms/link/List.java
@@ -0,0 +1,18 @@
+package com.xgh.test.algorithms.link;
+
+public interface List {
+
+ boolean add(E e);
+
+ boolean addFirst(E e);
+
+ boolean addLast(E e);
+
+ boolean remove(Object o);
+
+ E get(int index);
+
+ void printLinkList();
+
+
+}
diff --git a/src/main/java/com/xgh/test/design/IdGenerator.java b/src/main/java/com/xgh/test/design/IdGenerator.java
new file mode 100644
index 0000000..10ca5c5
--- /dev/null
+++ b/src/main/java/com/xgh/test/design/IdGenerator.java
@@ -0,0 +1,54 @@
+package com.xgh.test.design;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+/**
+ * com.xgh.test.design.IdGenerator
+ *
+ * @author xgh
+ * @description
+ * @date 2021年07月08日
+ */
+
+public class IdGenerator {
+ private static final Logger logger = LoggerFactory.getLogger(IdGenerator.class);
+
+ public static String generate() {
+ String id = "";
+ try {
+ String hostName = InetAddress.getLocalHost().getHostName();
+ String[] tokens = hostName.split("\\.");
+ if (tokens.length > 0) {
+ hostName = tokens[tokens.length - 1];
+ }
+ char[] randomChars = new char[8];
+ int count = 0;
+ SecureRandom random = new SecureRandom();
+ while (count < 8) {
+ int randomAscii = random.nextInt(122);
+ if (randomAscii >= 48 && randomAscii <= 57) {
+ randomChars[count] = (char)('0' + (randomAscii - 48));
+ count++;
+ } else if (randomAscii >= 65 && randomAscii <= 90) {
+ randomChars[count] = (char)('A' + (randomAscii - 65));
+ count++;
+ } else if (randomAscii >= 97 && randomAscii <= 122) {
+ randomChars[count] = (char)('a' + (randomAscii - 97));
+ count++;
+ }
+ }
+ id = String.format("%s-%d-%s", hostName,
+ System.currentTimeMillis(), new String(randomChars));
+ } catch (UnknownHostException e) {
+ logger.warn("Failed to get the host name.", e);
+ }
+
+ return id;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/xgh/test/design/STATUS.java b/src/main/java/com/xgh/test/design/STATUS.java
new file mode 100644
index 0000000..001a54d
--- /dev/null
+++ b/src/main/java/com/xgh/test/design/STATUS.java
@@ -0,0 +1,20 @@
+package com.xgh.test.design;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+
+
+@AllArgsConstructor
+public enum STATUS {
+ //排序字段
+ TO_BE_EXECUTD(0," order_time desc"),
+ EXECUTED(1," write_off_time DESC"),
+ EXPIRED(2," update_time DESC"),
+ FAILED(2," update_time DESC"),
+
+ ;
+ private int value;
+ private String name;
+
+
+}
diff --git a/src/main/java/com/xgh/test/design/Transaction.java b/src/main/java/com/xgh/test/design/Transaction.java
new file mode 100644
index 0000000..e32aaee
--- /dev/null
+++ b/src/main/java/com/xgh/test/design/Transaction.java
@@ -0,0 +1,118 @@
+package com.xgh.test.design;
+
+
+import lombok.Data;
+import org.springframework.util.IdGenerator;
+
+import javax.transaction.InvalidTransactionException;
+
+/**
+ * com.xgh.test.design.Transaction
+ *
+ * @author xgh
+ * @description
+ * @date 2021年07月07日
+ */
+@Data
+public class Transaction {
+
+ private String id;
+ private Long buyerId;
+ private Long sellerId;
+
+ private Long productId;
+
+ private Long orderId;
+
+ private Long createTimestamp;
+
+ private Double amount=0.0;
+
+ private STATUS status;
+
+ private String walletTransactionId;
+
+ private WalletRpcService walletRpcService;
+
+ public void setWalletRpcService(WalletRpcService walletRpcService) {
+ this.walletRpcService = walletRpcService;
+ }
+
+ public Transaction(String preAssignedId, Long buyerId, Long sellerId, Long productId, Long orderId) {
+ if (preAssignedId != null && !preAssignedId.isEmpty()) {
+ this.id = preAssignedId;
+ } else {
+ this.id = "12313131";
+ }
+ if (!this.id.startsWith("t_")) {
+ this.id = "t_" + preAssignedId;
+ }
+
+ this.buyerId = buyerId;
+ this.sellerId = sellerId;
+ this.productId = productId;
+ this.orderId = orderId;
+ this.status = STATUS.TO_BE_EXECUTD;
+ this.createTimestamp = System.currentTimeMillis();
+ }
+
+ public boolean execute() throws InvalidTransactionException {
+ if (buyerId == null || (sellerId == null || amount < 0.0)) {
+ throw new InvalidTransactionException("111");
+ }
+ if (status == STATUS.EXECUTED) {
+ return true;
+ }
+ boolean isLocked = false;
+ try {
+ isLocked = getLock(id);
+ if (!isLocked) {
+ return false;
+ }
+ if (status == STATUS.EXECUTED) {
+ return true;
+ }
+ long executionInvokedTimestamp = System.currentTimeMillis();
+ if (executionInvokedTimestamp - createTimestamp > 14) {
+ this.status = STATUS.EXPIRED;
+ return false;
+ }
+ String walletTransactionId = walletRpcService.moveMoney(id, buyerId, sellerId, amount);
+ if (walletTransactionId != null) {
+ this.status = STATUS.EXECUTED;
+ this.walletTransactionId = walletTransactionId;
+ return true;
+ } else {
+ this.status = STATUS.FAILED;
+ return false;
+ }
+
+ } finally {
+ if (isLocked) {
+ unlock(id);
+ }
+ }
+ }
+
+ private void unlock(String id) {
+
+
+ }
+
+ private boolean getLock(String id) {
+ return true;
+ }
+
+ public static void main(String[] args) {
+ Integer[] a = new Integer[]{1,2,3,4,5};
+
+ a[2] = 5;
+ for (Integer integer : a) {
+ System.out.println(integer);
+ }
+
+
+ }
+
+
+}
diff --git a/src/main/java/com/xgh/test/design/WalletRpcService.java b/src/main/java/com/xgh/test/design/WalletRpcService.java
new file mode 100644
index 0000000..ec77e7c
--- /dev/null
+++ b/src/main/java/com/xgh/test/design/WalletRpcService.java
@@ -0,0 +1,13 @@
+package com.xgh.test.design;
+
+/**
+ * com.xgh.test.design.WalletRpcService
+ *
+ * @author xgh
+ * @description
+ * @date 2021年07月08日
+ */
+public interface WalletRpcService {
+
+ String moveMoney(String id, Long fromUserId, Long toUserId, Double amount);
+}
diff --git a/src/main/java/com/xgh/test/design/WalletRpcServiceOne.java b/src/main/java/com/xgh/test/design/WalletRpcServiceOne.java
new file mode 100644
index 0000000..cc9cd23
--- /dev/null
+++ b/src/main/java/com/xgh/test/design/WalletRpcServiceOne.java
@@ -0,0 +1,15 @@
+package com.xgh.test.design;
+
+/**
+ * com.xgh.test.design.WalletRpcServiceOne
+ *
+ * @author xgh
+ * @description
+ * @date 2021年07月08日
+ */
+public class WalletRpcServiceOne implements WalletRpcService{
+ @Override
+ public String moveMoney(String id, Long fromUserId, Long toUserId, Double amount) {
+ return "213112abc";
+ }
+}
diff --git a/src/main/java/com/xgh/test/design/WalletRpcServiceTwo.java b/src/main/java/com/xgh/test/design/WalletRpcServiceTwo.java
new file mode 100644
index 0000000..1fd9995
--- /dev/null
+++ b/src/main/java/com/xgh/test/design/WalletRpcServiceTwo.java
@@ -0,0 +1,15 @@
+package com.xgh.test.design;
+
+/**
+ * com.xgh.test.design.WalletRpcServiceTwo
+ *
+ * @author xgh
+ * @description
+ * @date 2021年07月08日
+ */
+public class WalletRpcServiceTwo implements WalletRpcService{
+ @Override
+ public String moveMoney(String id, Long fromUserId, Long toUserId, Double amount) {
+ return null;
+ }
+}
diff --git a/src/main/java/com/xgh/test/design/id/IdGenerator.java b/src/main/java/com/xgh/test/design/id/IdGenerator.java
new file mode 100644
index 0000000..0497c88
--- /dev/null
+++ b/src/main/java/com/xgh/test/design/id/IdGenerator.java
@@ -0,0 +1,12 @@
+package com.xgh.test.design.id;
+
+/**
+ * com.xgh.test.design.id.IdGenerator
+ *
+ * @author xgh
+ * @description
+ * @date 2021年07月08日
+ */
+public interface IdGenerator {
+ String generate();
+}
diff --git a/src/main/java/com/xgh/test/design/id/LogTraceIdGenerator.java b/src/main/java/com/xgh/test/design/id/LogTraceIdGenerator.java
new file mode 100644
index 0000000..5e78113
--- /dev/null
+++ b/src/main/java/com/xgh/test/design/id/LogTraceIdGenerator.java
@@ -0,0 +1,11 @@
+package com.xgh.test.design.id;
+
+/**
+ * com.xgh.test.design.id.LogTraceIdGenerator
+ *
+ * @author xgh
+ * @description
+ * @date 2021年07月08日
+ */
+public interface LogTraceIdGenerator extends IdGenerator{
+}
diff --git a/src/main/java/com/xgh/test/design/id/RandomIdGenerator.java b/src/main/java/com/xgh/test/design/id/RandomIdGenerator.java
new file mode 100644
index 0000000..feb9b82
--- /dev/null
+++ b/src/main/java/com/xgh/test/design/id/RandomIdGenerator.java
@@ -0,0 +1,65 @@
+package com.xgh.test.design.id;
+
+import org.assertj.core.util.VisibleForTesting;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.security.SecureRandom;
+import java.util.Random;
+
+/**
+ * com.xgh.test.design.id.RandomIdGenerator
+ *
+ * @author xgh
+ * @description
+ * @date 2021年07月08日
+ */
+
+public class RandomIdGenerator implements LogTraceIdGenerator {
+ private static final Logger logger = LoggerFactory.getLogger(RandomIdGenerator.class);
+
+ @Override
+ public String generate() {
+ String substrOfHostName = getLastfieldOfHostName();
+ long currentTimeMillis = System.currentTimeMillis();
+ String randomString = generateRandomAlphameric(8);
+ return String.format("%s-%d-%s", substrOfHostName, currentTimeMillis, randomString);
+ }
+
+ private String getLastfieldOfHostName() {
+ String substrOfHostName = null;
+ try {
+ String hostName = InetAddress.getLocalHost().getHostName();
+ substrOfHostName = getLastSubstrSplittedByDot(hostName);
+ } catch (UnknownHostException e) {
+ logger.warn("Failed to get the host name.", e);
+ }
+ return substrOfHostName;
+ }
+
+ @VisibleForTesting
+ protected String getLastSubstrSplittedByDot(String hostName) {
+ String[] tokens = hostName.split("\\.");
+ return tokens[tokens.length - 1];
+ }
+
+ private String generateRandomAlphameric(int length) {
+ char[] randomChars = new char[length];
+ int count = 0;
+ SecureRandom random = new SecureRandom();
+ while (count < length) {
+ int maxAscii = 'z';
+ int randomAscii = random.nextInt(maxAscii);
+ boolean isDigit = randomAscii >= '0' && randomAscii <= '9';
+ boolean isUppercase = randomAscii >= 'A' && randomAscii <= 'Z';
+ boolean isLowercase = randomAscii >= 'a' && randomAscii <= 'z';
+ if (isDigit || isUppercase || isLowercase) {
+ randomChars[count] = (char) (randomAscii);
+ ++count;
+ }
+ }
+ return new String(randomChars);
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/com/xgh/test/design/id/RandomIdGeneratorTest.java b/src/main/java/com/xgh/test/design/id/RandomIdGeneratorTest.java
new file mode 100644
index 0000000..af72ac7
--- /dev/null
+++ b/src/main/java/com/xgh/test/design/id/RandomIdGeneratorTest.java
@@ -0,0 +1,57 @@
+package com.xgh.test.design.id;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * com.xgh.test.design.id.RandomIdGeneratorTest
+ *
+ * @author xgh
+ * @description
+ * @date 2021年07月08日
+ */
+public class RandomIdGeneratorTest {
+ @Test
+ public void testGetLastSubstrSplittedByDot() {
+ RandomIdGenerator idGenerator = new RandomIdGenerator();
+ String actualSubstr = idGenerator.getLastSubstrSplittedByDot("field1.field2.field3");
+ Assert.assertEquals("field3", actualSubstr);
+ actualSubstr = idGenerator.getLastSubstrSplittedByDot("field1");
+ Assert.assertEquals("field1", actualSubstr);
+ actualSubstr = idGenerator.getLastSubstrSplittedByDot("field1#field2$field3");
+ Assert.assertEquals("field1#field2#field3", actualSubstr);
+ }
+
+ // 此单元测试会失败,因为我们在代码中没有处理hostName为null或空字符串的情况
+ // 这部分优化留在第36、37节课中讲解
+ @Test
+ public void testGetLastSubstrSplittedByDot_nullOrEmpty() {
+ RandomIdGenerator idGenerator = new RandomIdGenerator();
+ String actualSubstr = idGenerator.getLastSubstrSplittedByDot(null);
+ Assert.assertNull(actualSubstr);
+ actualSubstr = idGenerator.getLastSubstrSplittedByDot("");
+ Assert.assertEquals("", actualSubstr);
+ }
+
+ @Test
+ public void testGenerateRandomAlphameric() {
+ RandomIdGenerator idGenerator = new RandomIdGenerator();
+ //String actualRandomString = idGenerator.generateRandomAlphameric(6);
+ // Assert.assertNotNull(actualRandomString);
+ // Assert.assertEquals(6, actualRandomString.length());
+ /* for (char c : actualRandomString.toCharArray()) {
+ Assert.assertTrue(('0' < c && c < '9') || ('a' < c && c < 'z') || ('A' < c && c < 'Z'));
+ }*/
+ }
+
+ // 此单元测试会失败,因为我们在代码中没有处理length<=0的情况
+// 这部分优化留在第36、37节课中讲解
+ @Test
+ public void testGenerateRandomAlphameric_lengthEqualsOrLessThanZero() {
+ RandomIdGenerator idGenerator = new RandomIdGenerator();
+ // String actualRandomString = idGenerator.generateRandomAlphameric(0);
+ // Assert.assertEquals("", actualRandomString);
+ // actualRandomString = idGenerator.generateRandomAlphameric(-1);
+ // Assert.assertNull(actualRandomString);
+ }
+}
diff --git a/src/main/java/com/xgh/test/redis/lock/RedisWithReentrantLock.java b/src/main/java/com/xgh/test/redis/lock/RedisWithReentrantLock.java
new file mode 100644
index 0000000..765a50e
--- /dev/null
+++ b/src/main/java/com/xgh/test/redis/lock/RedisWithReentrantLock.java
@@ -0,0 +1,83 @@
+package com.xgh.test.redis.lock;
+
+import redis.clients.jedis.*;
+
+import java.util.HashMap;
+import java.util.Map;
+
+public class RedisWithReentrantLock {
+
+ private ThreadLocal