commit 7c8ab12d61140ee7fbbe032ddcac9a560e121e50 Author: yichen Date: Thu Aug 18 11:15:00 2022 +0800 将客户端与服务端置于同一仓库 diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6b8ba9e --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.idea +*/target \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..1a8af2f --- /dev/null +++ b/README.md @@ -0,0 +1,279 @@ +# README + +## 项目功能 + +​ 为达到`Service` 从公网访问客户端所在内网中数据源的效果,通过运行在客户机上的代理程序代理`Service`的所有`JDBC`请求,并将查询结果返回给`Service`。实现目标,`Service`除更改使用的`JDBC`驱动外,对代理存在无感知,支持主流的包含`JDBC`支持的数据库。 + + +## 项目依赖 + +`Netty-socketio`与`Socket.io-client-Java`的对应关系是: + +| [`netty-socketio`](https://github.com/mrniko/netty-socketio) | [`Java client`](https://github.com/socketio/socket.io-client-java) | +| ------------------------------------------------------------ | ------------------------------------------------------------ | +| 1.7.19 | 1.0.x | +| 暂无 | [Document](https://socketio.github.io/socket.io-client-java/installation.html) | + +以下用`Service`指代`Socket`连接中的`socket`服务器,它也是需求查询用户内网数据源的公网服务器。 + +用`Agent`指代`Socket`连接中的客户端,也是运行在用户`PC`上承担远程调用`JDBC`方法的代理服务。 + +具体结构见下文项目结构图。 + +## QUICKSTART + +1. 分别下载`Agent`和`Serviec` + +2. 修改数据库配置和对应的SQL语句 +3. 先运行`Service`中的`Test的`主函数 + +4. 运行`Agent`中的`Test`的主函数 + +即可在`Service`上观察到查询结果 + +``` +目前只测试了mysql 数据库,但内置支持 mysql、 postgresql、 oracle、 sqlserver、 db2, 在 Agent 上注册驱动即可使用 +``` + +## 实现方案 + +1. `Service` 启动`socket`服务与 `Agent`建立连接后,可以开始使用代理进行查询。 + +2. `Service`端通过自实现的`JDBC`驱动,进行`JDBC`操作。驱动中使用基于`CGlib`的动态代理,对`Service`端的所有`JDBC`相关驱动类进行增强,所有方法信息会被序列化传递到`Agent`执行,并有选择地将结果回送到`Service` + +## 结构与流程 +project structure + +如上图,对于`Service` 端来讲,`Agent`对其的代理是无感知的。在`Service`来看,只是调用了一个自定义的`JDBC`驱动进行查询。 + +这得益于驱动内部方法地重写,自定义地实现类在`Agent`和`Service`中有相同的名字,但内部实现却不相同,这使得整个RPC的流程十分灵活。 + +## 动态代理 + +动态代理是该项目中的核心,如在 `Driver`类的 `connect`方法中:返回的`Connection`就被替换为了动态代理增强过的`MyConnection`,实现对`Service`中调用的`JDBC`方法的完全代理。代理类会依靠`info`从缓存中找到命名空间(本项目中以`/dataSoure Name`来区别命名空间)对应的`socket`,将方法调用信息以`RPCReqquest`的方式序列化后发送出去。 + +```java + // In Service Source Code + @Override + public Connection connect(String url, Properties info) throws SQLException { + String agentID = info.getProperty("agentID"); + String dbName = info.getProperty("agentDBName"); + if(dbName == null){ + dbName = url.split(":")[1]; + info.setProperty("agentDBName", dbName); + } + MyConnection myConn = (MyConnection) ProxyFactory.getProxy(MyConnection.class, info); + myConn.setInfo(info); + return myConn; + } +``` + +RPC实体类包含如下信息: + +```java +@Data +@Accessors(chain = true) +public class RpcRequest { + // Marks whether the method delivered need loopback data + private boolean reply; + // Marks whether the method will create an instance requeired to be cached. + private boolean binding; + private String ID; + private String IDtoInvoke; + private Class ServiceClass; + private String MethodName; + private Object[] args; + private Class[] argTypes; +} +``` + +在`Agent`收到`Request`的时候,会按照报文要求对方法进行调用,某些创建的实例会被缓存,以便之后调用。在本项目中,这些实例的类是: + +``` +Drive( MyDriver ), Connection( MyConnection ), Statement( MyStatement ), PreparedStatement( MyPreparedStatement ), ResultSet( MyResult ) +``` + +```java +public Object invokeAsRequest(RpcRequest rpcRequest, BeanCache beanCache) { +... + // The ID of the rpcRequest could be save as the ID of an instance + // Because one instance can only been create just once for an unique rpcRequest + String IDtoCache = rpcRequest.getID(); + String IDtoInvoke = rpcRequest.getIDtoInvoke(); +... +``` + +## RPC调用 + +在一次RPC调用流程中,`FutureTask` 异步获取返回结果,以“生产者-消费者”模型实现一次调用的同步管理。 + +`ClientWrapper` 持有着各个命名空间上的`socket`。在这些`socket`上的通信,每次调用,会在`wrapper`中注册一个工具类:`LockAndCondition`,发出消息后,等待`socket`上出现对应的响应报文唤醒`FutureTask` 线程。通过锁机制,保证逻辑的正确性。 + +```java +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ClientWrapper { + private SocketIOClient client; + private static Map lockMap = new ConcurrentHashMap<>(); + + + public SocketIOClient getClient(){ + if(client == null) throw new RuntimeException("no such client"); + return client; + } + + public LockAndCondition getLockAndCondition(String messageID){ + LockAndCondition lac = lockMap.get(messageID); + if(lac == null){ + ReentrantLock lock = new ReentrantLock(); + Condition condition = lock.newCondition(); + lac = new LockAndCondition(lock, condition); + lockMap.put(messageID, lac); + } + return lac; + } + + public void removeLockAndCondition(String messageID){ + lockMap.remove(messageID); + } +} +``` + + + +```java +@Data +@NoArgsConstructor +@AllArgsConstructor +public class LockAndCondition{ + private ReentrantLock lock; + private Condition condition; + private Object result; + private String BindingID; + + LockAndCondition(ReentrantLock lock, Condition condition){ + this.lock = lock; + this.condition = condition; + } +} +``` + + + +```java + FutureTask futureTask = new FutureTask( + new Callable() { + @Override + public Object call() throws Exception { + Object res = null; + ClientWrapper wrapper = ClientCache.getClientWrapper(agentID, dbName); + LockAndCondition lac = wrapper.getLockAndCondition(rpcRequest.getID()); + ReentrantLock lock = lac.getLock(); + Condition condition = lac.getCondition(); + try{ + byte[] bytes = ServerStater.serializer.serialize(rpcRequest); + lock.lock(); + client.sendEvent("RPCRequest", bytes); + condition.await(); + // get res from RPC response data + res = lac.getResult(); + }catch (Exception e){ + e.printStackTrace(); + }finally { + lock.unlock(); + } + return res; + } + } + ); + ServerStater.threadPool.submit(futureTask); + Object res = futureTask.get(); +``` + +`socket`收到响应时解锁对应的线程。 + +```java + // rpcResponse + nameSpace.addEventListener("RPCResponse", byte[].class, ((client, data, ackRequest) -> { + RpcResponse rpcResponse = serializer.deserialize(data, RpcResponse.class); + logger.debug("RPCResponse: " + (rpcResponse.getStatus() ? "success" : "fail")); + + String agentID = Commons.getAgentID(client); + String dbName = Commons.getDBName(client); + ClientWrapper wrapper = ClientCache.getClientWrapper(agentID, dbName); + LockAndCondition lac = wrapper.getLockAndCondition(rpcResponse.getID()); + ReentrantLock lock = lac.getLock(); + Condition condition = lac.getCondition(); + // When a response is received, it notifies that the futuretask thread blocking on the lockandcondition + // If the response contains data, take it out. + try { + lock.lock(); + Object resultData = rpcResponse.getResult(); + if(!rpcResponse.getStatus()){ + logger.error(resultData); + resultData = null; + } + if(resultData != null) lac.setResult(resultData); + condition.signal(); + }catch (Exception e){ + e.printStackTrace(); + }finally { + lock.unlock(); + } + wrapper.removeLockAndCondition(rpcResponse.getID()); + logger.debug("received response message, signaled condition"); + })); +``` + +`Service`是使用`netty`实现的高效同步非阻塞`IO`,上文的同步机制可以很大程度上利用`socket`的并发效果。 + +## 绑定实例 + +确定`Agent`上缓存实例与`Service`端实例的一一对应关系是很必要,不然程序在反射调用方法时会产生问题。 + +例如,对于`createStatement()`方法必须由上一步生成的`Connection`类进行调用。为了达到这一点,这些`Service`端实例必须和`Agent`端具有相同的ID。 + +考虑到在进行`RPC`调用回调的时候,利用时间和随机数生成了一个唯一`ID`。 + +```java + public static String getID(){ + return getTimeInMillis() + getRandom(); + } + + public static String getTimeInMillis() { + long timeInMillis = Calendar.getInstance().getTimeInMillis(); + return timeInMillis+""; + } + + public static String getRandom() { + Random random = new Random(); + int nextInt = random.nextInt(9000000); + nextInt=nextInt+1000000; + String str=nextInt+""; + return str; + } +``` + +而`Agent`端的缓存实例是由某次调用产生的,所以只需将该次调用的`RPC`报文`ID`标记在实例上,并在收到`RPC`响应时为需要绑定的类型打上同样的标记即可。这样`Agent`方面,由于存储的实例都有了唯一的`ID`作为键,大大简化了缓存系统的复杂性。 + +标记实现: + +```java +@Override +public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { ... + + Object returnObj = methodProxy.invokeSuper(o, objects); + + // If the return instance is corresponding with another instance in agent, set the binding ID. + if (InterceptorUtils.isInBindList(returnObj)){ + InterceptorUtils.setInvokeHelper(returnObj, "setID", rpcRequest.getID()); + } +``` + +## 项目参考 + +[nuzzle: A Simple RPC Project](https://github.com/sakiila/nuzzle) + +[CSV JDBC Driver](https://github.com/peterborkuti/csv-jdbc-driver) + diff --git a/agent/pom.xml b/agent/pom.xml new file mode 100644 index 0000000..e68db91 --- /dev/null +++ b/agent/pom.xml @@ -0,0 +1,88 @@ + + + + intranet + org.example + 1.0-SNAPSHOT + + 4.0.0 + + agent + + + 8 + 8 + + + + + io.socket + socket.io-client + 1.0.2 + + + + org.projectlombok + lombok + 1.18.22 + + + + com.esotericsoftware + kryo + 5.3.0 + + + + org.postgresql + postgresql + 42.4.0 + + + + + org.apache.logging.log4j + log4j-api + 2.17.2 + + + + org.apache.logging.log4j + log4j-core + 2.17.2 + + + + mysql + mysql-connector-java + 8.0.29 + + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.17.2 + test + + + + + org.hsqldb + hsqldb + debug + 2.5.2 + test + + + + + org.junit.jupiter + junit-jupiter-engine + 5.4.0 + test + + + + \ No newline at end of file diff --git a/agent/src/main/java/com/fanruan/AgentStarter.java b/agent/src/main/java/com/fanruan/AgentStarter.java new file mode 100644 index 0000000..5e73494 --- /dev/null +++ b/agent/src/main/java/com/fanruan/AgentStarter.java @@ -0,0 +1,135 @@ +package com.fanruan; + +import com.fanruan.handler.MyDispatcherImpl; +import com.fanruan.pojo.message.RpcRequest; +import com.fanruan.serializer.KryoSerializer; +import com.fanruan.serializer.Serializer; +import io.socket.client.IO; +import io.socket.client.Socket; +import io.socket.engineio.client.transports.WebSocket; +import okhttp3.Dispatcher; +import okhttp3.OkHttpClient; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URI; +import java.util.Properties; +import java.util.concurrent.TimeUnit; + + +/** + * @author YiChen Dai + */ +public class AgentStarter { + + protected static final Logger logger = LogManager.getLogger(); + + public final static Serializer SERIALIZER = new KryoSerializer(); + + public static MyDispatcherImpl myDispatcherImpl; + + public static String AgentID; + + public AgentStarter(String[] DBs) { + myDispatcherImpl = new MyDispatcherImpl(); + try { + createSocket(DBs); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private void createSocket(String[] DBs) throws IOException { + logger.debug("加载配置"); + IO.Options options = new IO.Options(); + try{ + InputStream in = this.getClass().getResourceAsStream("/socket.properties"); + Properties props = new Properties(); + + InputStreamReader inputStreamReader = new InputStreamReader(in, "UTF-8"); + props.load(inputStreamReader); + + options.transports = new String[]{WebSocket.NAME}; + options.reconnectionAttempts = Integer.parseInt(props.getProperty("reconnectionAttempts")); + options.query = "agentID=" + props.getProperty("agentID"); + AgentID = props.getProperty("agentID"); + options.reconnectionDelay = Integer.parseInt(props.getProperty("reconnectionDelay")); + options.timeout = Integer.parseInt(props.getProperty("timeout")); + String uri = props.getProperty("uri"); + in.close(); + + // config the max number of socket + int MAX_CLIENTS = 10; + Dispatcher dispatcher = new Dispatcher(); + dispatcher.setMaxRequests(MAX_CLIENTS * 2); + dispatcher.setMaxRequestsPerHost(MAX_CLIENTS * 2); + + OkHttpClient okHttpClient = new OkHttpClient.Builder() + .dispatcher(dispatcher) + // important for HTTP long-polling + .readTimeout(1, TimeUnit.MINUTES) + .build(); + + options.callFactory = okHttpClient; + options.webSocketFactory = okHttpClient; + + Socket defaultSocket = IO.socket(URI.create(uri), options); + MyDispatcherImpl.CACHE.registerSocket("/", defaultSocket); + configDefaultSocket(defaultSocket); + + for(String dbName : DBs){ + Socket socket = IO.socket(URI.create(uri + "/" + dbName), options); + MyDispatcherImpl.CACHE.registerSocket(dbName, socket); + configSocket(socket, dbName); + } + + }catch (Exception e){ + e.printStackTrace(); + } + } + + private void configDefaultSocket(Socket socket) throws IOException { + socket.on(Socket.EVENT_CONNECT, objects -> { + logger.info("default-socket connected!"); + }); + + socket.on(Socket.EVENT_CONNECT_ERROR, objects -> { + logger.info("default-socket error: " + objects[0].toString()); + }); + + socket.on(Socket.EVENT_DISCONNECT, objects -> { + for(Object obj : objects){ + logger.info("default-socket closed: " + obj.toString()); + } + }); + } + + private void configSocket(Socket socket, String dbName) throws IOException { + socket.on(Socket.EVENT_CONNECT, objects -> { + logger.info(dbName + "-socket connected!"); + }); + + socket.on(Socket.EVENT_DISCONNECT, objects -> { + for(Object obj : objects){ + logger.info(dbName + "-socket closed: " + obj.toString()); + } + }); + + socket.on(Socket.EVENT_CONNECT_ERROR, objects -> { + logger.info(dbName + "-socket error: " + objects[0].toString()); + }); + + socket.on("RPCRequest", objects -> { + RpcRequest rpcRequest = SERIALIZER.deserialize((byte[]) objects[0], RpcRequest.class); + logger.debug(dbName + "-RPCRequest: " + rpcRequest.toString()); + try { + myDispatcherImpl.doDispatch(rpcRequest, dbName); + } catch (Exception e) { + e.printStackTrace(); + } + }); + } +} diff --git a/agent/src/main/java/com/fanruan/cache/BeanCache.java b/agent/src/main/java/com/fanruan/cache/BeanCache.java new file mode 100644 index 0000000..de0d338 --- /dev/null +++ b/agent/src/main/java/com/fanruan/cache/BeanCache.java @@ -0,0 +1,34 @@ +package com.fanruan.cache; + +/** + * Define the operation of the Map where cached instances like Driver, Connection + * @author Yichen Dai + * @date 2022/8/16 16:41 + */ +public interface BeanCache { + + /** + * Get cached instances like Driver, Connection + * @param ID The unique num of a cache instance, It comes from the RPC request ID, + * which asked to create the instance. + * @param clazz The class of the cached instance. + * @return cached instance of the given type. + */ + T getCachedInstances(String ID, Class clazz); + + /** + * Remove a cached instance of given ID. + * @param ID The unique num of a cache instance, It comes from the RPC request ID, + * which asked to create the instance. + */ + void removeInstances(String ID); + + /** + * Save the given object + * @param ID The unique num of a cache instance, It comes from the RPC request ID, + * which asked to create the instance. + * @param o Instance to be cached + */ + void cacheInstance(String ID, Object o); + +} diff --git a/agent/src/main/java/com/fanruan/cache/BeanCacheImpl.java b/agent/src/main/java/com/fanruan/cache/BeanCacheImpl.java new file mode 100644 index 0000000..0926afb --- /dev/null +++ b/agent/src/main/java/com/fanruan/cache/BeanCacheImpl.java @@ -0,0 +1,41 @@ +package com.fanruan.cache; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author Yichen Dai + */ +public class BeanCacheImpl implements BeanCache{ + + public String dbName; + + /** + * cache those instances asked to be established by RPC request + */ + final private static Map CACHE = new ConcurrentHashMap<>(); + + public BeanCacheImpl(String dbName){ + this.dbName = dbName; + } + + @Override + public T getCachedInstances(String ID, Class clazz){ + try { + return clazz.cast(CACHE.get(ID)); + }catch (Exception e){ + e.printStackTrace(); + } + return null; + } + + @Override + public void removeInstances(String ID){ + CACHE.remove(ID); + } + + @Override + public void cacheInstance(String ID, Object o){ + CACHE.put(ID, o); + } +} diff --git a/agent/src/main/java/com/fanruan/cache/Cache.java b/agent/src/main/java/com/fanruan/cache/Cache.java new file mode 100644 index 0000000..7c03272 --- /dev/null +++ b/agent/src/main/java/com/fanruan/cache/Cache.java @@ -0,0 +1,54 @@ +package com.fanruan.cache; + +import io.socket.client.Socket; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * Global cache which has the same life cycle with Dispatcher + * @author Yichen Dai + * @date 2022/8/16 16:13 + */ +public interface Cache { + /** + * stored socket instance by dataBase name + */ + Map SOCKET_MAP = new ConcurrentHashMap<>(); + + + /** + * Bunch of Map where cached instances like Driver, Connection + */ + Map BEAN_CACHE = new ConcurrentHashMap<>(); + + /** + * register the socket of specific nameSpace, + * the nameSpace is named as "/" + DB name. + * @param dbName the key of cache entry + * @param socket the value of cache entry + */ + void registerSocket(String dbName, Socket socket); + + /** + * get socket by DB name + * @param dbName the key of cache entry + * @return socket of the nameSpace + */ + Socket getSocket(String dbName); + + /** + * register the socket of specific nameSpace/db + * @param dbName the key of cache + * @param beanCache Map where cached instances like Driver, Connection + */ + void registerBeanCache(String dbName, BeanCacheImpl beanCache); + + /** + * get the beanCache of specific nameSpace/db + * @param dbName the key of cache + * @return beanCache: Map where cached instances like Driver, Connection + */ + BeanCacheImpl getBeanCache(String dbName); + +} diff --git a/agent/src/main/java/com/fanruan/cache/CacheImpl.java b/agent/src/main/java/com/fanruan/cache/CacheImpl.java new file mode 100644 index 0000000..817b79f --- /dev/null +++ b/agent/src/main/java/com/fanruan/cache/CacheImpl.java @@ -0,0 +1,39 @@ +package com.fanruan.cache; + +import io.socket.client.Socket; + +/** + * @author Yichen Dai + * @date 2022/8/16 16:13 + */ +public class CacheImpl implements Cache{ + + @Override + public void registerSocket(String dbName, Socket socket){ + SOCKET_MAP.put(dbName, socket); + } + + @Override + public Socket getSocket(String dbName){ + Socket socket = SOCKET_MAP.get(dbName); + if (socket == null){ + throw new RuntimeException("no such DataBase Name"); + } + return socket; + } + + @Override + public void registerBeanCache(String dbName, BeanCacheImpl beanCache) { + BEAN_CACHE.put(dbName, beanCache); + } + + @Override + public BeanCacheImpl getBeanCache(String dbName) { + BeanCacheImpl beanCache = BEAN_CACHE.get(dbName); + if(beanCache == null){ + beanCache = new BeanCacheImpl(dbName); + registerBeanCache(dbName, beanCache); + } + return beanCache; + } +} diff --git a/agent/src/main/java/com/fanruan/handler/Dispatcher.java b/agent/src/main/java/com/fanruan/handler/Dispatcher.java new file mode 100644 index 0000000..0f659e5 --- /dev/null +++ b/agent/src/main/java/com/fanruan/handler/Dispatcher.java @@ -0,0 +1,34 @@ +package com.fanruan.handler; + +import com.fanruan.cache.BeanCacheImpl; +import com.fanruan.cache.CacheImpl; +import com.fanruan.pojo.message.RpcRequest; + +/** + * Dispatch and process received requests + * @author Yichen Dai + * @date 2022/8/16 14:56 + */ +public interface Dispatcher { + CacheImpl CACHE = new CacheImpl(); + + ResponseEmitterImpl RESPONSE_EMITTER_IMPL = new ResponseEmitterImpl(); + /** + * + * @param rpcRequest + * @param dbName + */ + void doDispatch(RpcRequest rpcRequest, String dbName); + + /** + * + * @param rpcRequest + * @param beanCache + * @return + * @throws Throwable + */ + Object invokeAsRequest(RpcRequest rpcRequest, BeanCacheImpl beanCache) throws Throwable; + + + +} diff --git a/agent/src/main/java/com/fanruan/handler/DispatcherHelper.java b/agent/src/main/java/com/fanruan/handler/DispatcherHelper.java new file mode 100644 index 0000000..3668874 --- /dev/null +++ b/agent/src/main/java/com/fanruan/handler/DispatcherHelper.java @@ -0,0 +1,60 @@ +package com.fanruan.handler; + + +import java.util.HashMap; +import java.util.Map; + +/** + * Some utils for MyDispatcher + * @author Yichen Dai + */ +public class DispatcherHelper { + + private static final Map> WRAPPER_CLASS_MAP = new HashMap>(){ + { + put("Integer", Integer.TYPE); + put("Short", Short.TYPE); + put("Long", Long.TYPE); + put("Double", Double.TYPE); + put("Float", Float.TYPE); + put("Byte", Byte.TYPE); + put("Character", Character.TYPE); + put("Boolean", Boolean.TYPE); + } + }; + + public final static String[] CACHE_LIST = new String[]{ + "com.fanruan.jdbc.driver.MyDriver", + "com.fanruan.jdbc.connection.MyConnection", + "com.fanruan.jdbc.statement.MyStatement", + "com.fanruan.jdbc.statement.MyPreparedStatement", + "com.fanruan.jdbc.resultset.MyResultSet", + + }; + + public static boolean isInCacheList(String className){ + for(String s : CACHE_LIST){ + if(s.equals(className)){ + return true; + } + } + return false; + } + + public static boolean isWraps(Class clz){ + return WRAPPER_CLASS_MAP.containsKey(getClassName(clz.getName())); + } + + public static Class castToPrimitiveClass(Class clz){ + return WRAPPER_CLASS_MAP.get(getClassName(clz.getName())); + } + + public static String getClassName(String fullyQualifiedClassName){ + String[] arr = fullyQualifiedClassName.split("\\."); + int n = arr.length; + if(n == 0) { + throw new RuntimeException("the class name invoked is wrong"); + } + return arr[n-1]; + } +} diff --git a/agent/src/main/java/com/fanruan/handler/MyDispatcherImpl.java b/agent/src/main/java/com/fanruan/handler/MyDispatcherImpl.java new file mode 100644 index 0000000..d822ca3 --- /dev/null +++ b/agent/src/main/java/com/fanruan/handler/MyDispatcherImpl.java @@ -0,0 +1,109 @@ +package com.fanruan.handler; + + +import com.fanruan.cache.BeanCacheImpl; +import com.fanruan.pojo.message.RpcRequest; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.lang.reflect.Method; + + +/** + * @author Yichen Dai + */ +public class MyDispatcherImpl implements Dispatcher{ + protected static final Logger logger = LogManager.getLogger(); + + public final static String CLOSE_NAME = "close"; + + public MyDispatcherImpl(){} + + @Override + public void doDispatch(RpcRequest rpcRequest, String dbName) { + logger.debug("do dispatcher"); + BeanCacheImpl beanCache = CACHE.getBeanCache(dbName); + + Object res = null; + try { + res = invokeAsRequest(rpcRequest, beanCache); + }catch (Exception e){ + RESPONSE_EMITTER_IMPL.sendError(CACHE.getSocket(dbName), rpcRequest, e); + } + + if(rpcRequest.isReply()){ + RESPONSE_EMITTER_IMPL.replyWithData(CACHE.getSocket(dbName), rpcRequest, res); + }else { + RESPONSE_EMITTER_IMPL.sendOk(CACHE.getSocket(dbName), rpcRequest); + } + } + + @Override + public Object invokeAsRequest(RpcRequest rpcRequest, BeanCacheImpl beanCache) throws Exception{ + Class clazz = rpcRequest.getServiceClass(); + String methodName = rpcRequest.getMethodName(); + Object[] args = rpcRequest.getArgs(); + Class[] argTypes = rpcRequest.getArgTypes(); + Object calledClassInstance = null; + // The ID of the rpcRequest could be save as the ID of an instance + // Because one instance can only been create just once for an unique rpcRequest + String IDToCache = rpcRequest.getID(); + String IDToInvoke = rpcRequest.getIDToInvoke(); + + String fullName = clazz.getName(); + String className = DispatcherHelper.getClassName(fullName); + + // If BeanCache contains instance, get it; if not, create it. + if(IDToInvoke == null){ + try{ + // create + calledClassInstance = Class.forName(fullName).newInstance(); + }catch (Exception e) { + e.printStackTrace(); + } + beanCache.cacheInstance(IDToCache, calledClassInstance); + }else{ + calledClassInstance = beanCache.getCachedInstances(IDToInvoke, clazz); + } + + Method method; + + try { + // The primitive variable's type will be automatically packaged when passed as a class object, + // And an error will be reported when the method with the primitive variable as the parameter is called + method = clazz.getDeclaredMethod(methodName, argTypes); + }catch (Exception e){ + for(int i=0; i clz = argTypes[i]; + if(DispatcherHelper.isWraps(clz)){ + argTypes[i] = DispatcherHelper.castToPrimitiveClass(clz); + } + } + method = clazz.getDeclaredMethod(methodName, argTypes); + + + } + + + Object res = method.invoke(calledClassInstance, args); + + if(CLOSE_NAME.equals(methodName)){ + beanCache.removeInstances(IDToInvoke); + } + + // Cached some instances need to be invoke later. + // Some method return null, so determine the value of `res` before referencing it. + if(res != null){ + String resClassName = res.getClass().getName(); + if(DispatcherHelper.isInCacheList(resClassName)) { + beanCache.cacheInstance(rpcRequest.getID(), res); + } + logger.info("invoke" + className + "-" + methodName + " and return a instance of" + res.getClass().getName()); + }else{ + logger.info("invoke" + className + "-" + methodName + " and no return value"); + } + return res; + } + + +} diff --git a/agent/src/main/java/com/fanruan/handler/ResponseEmitter.java b/agent/src/main/java/com/fanruan/handler/ResponseEmitter.java new file mode 100644 index 0000000..45007b5 --- /dev/null +++ b/agent/src/main/java/com/fanruan/handler/ResponseEmitter.java @@ -0,0 +1,34 @@ +package com.fanruan.handler; + +import com.fanruan.pojo.message.RpcRequest; +import io.socket.client.Socket; + +/** + * @author Yichen Dai + * @date 2022/8/16 16:57 + */ +public interface ResponseEmitter { + + /** + * Send success response for the request which not require reply. + * @param socket socket to send event + * @param rpcRequest corresponding request + */ + void sendOk(Socket socket, RpcRequest rpcRequest); + + /** + * Send failure response when error occur while handle request. + * @param socket + * @param rpcRequest + * @param e Exception happened while handle request. + */ + void sendError(Socket socket, RpcRequest rpcRequest, Exception e); + + /** + * Send success response with data asked by request. + * @param socket + * @param rpcRequest + * @param res data required. + */ + void replyWithData(Socket socket, RpcRequest rpcRequest, Object res); +} diff --git a/agent/src/main/java/com/fanruan/handler/ResponseEmitterImpl.java b/agent/src/main/java/com/fanruan/handler/ResponseEmitterImpl.java new file mode 100644 index 0000000..6db0580 --- /dev/null +++ b/agent/src/main/java/com/fanruan/handler/ResponseEmitterImpl.java @@ -0,0 +1,48 @@ +package com.fanruan.handler; + +import com.fanruan.AgentStarter; +import com.fanruan.pojo.message.RpcRequest; +import com.fanruan.pojo.message.RpcResponse; +import io.socket.client.Socket; + +/** + * Utils for dispatcher to send response; + * @author Yichen Dai + */ +public class ResponseEmitterImpl implements ResponseEmitter{ + + @Override + public void sendOk(Socket socket, RpcRequest rpcRequest){ + RpcResponse rpcResponse = new RpcResponse(); + rpcResponse.setResult(null) + .setID(rpcRequest.getID()) + .setBinding(rpcRequest.isBinding()) + .setStatus(true); + byte[] bytes = AgentStarter.SERIALIZER.serialize(rpcResponse); + socket.emit("RPCResponse", bytes); + } + + @Override + public void sendError(Socket socket, RpcRequest rpcRequest, Exception e){ + RpcResponse rpcResponse = new RpcResponse(); + rpcResponse.setResult("Some errors happened when AgentID: " + AgentStarter.AgentID + " " + + rpcRequest.getMethodName() + " is being invoked!" + "\n" + + "Error Message: " + e.getMessage() + + " and check your code") + .setID(rpcRequest.getID()) + .setBinding(rpcRequest.isBinding()) + .setStatus(false); + byte[] bytes = AgentStarter.SERIALIZER.serialize(rpcResponse); + socket.emit("RPCResponse", bytes); + } + + @Override + public void replyWithData(Socket socket, RpcRequest rpcRequest, Object res){ + RpcResponse rpcResponse = new RpcResponse(); + rpcResponse.setID(rpcRequest.getID()) + .setStatus(true) + .setResult(res); + byte[] bytes = AgentStarter.SERIALIZER.serialize(rpcResponse); + socket.emit("RPCResponse", bytes); + } +} diff --git a/agent/src/main/java/com/fanruan/jdbc/connection/MyConnection.java b/agent/src/main/java/com/fanruan/jdbc/connection/MyConnection.java new file mode 100644 index 0000000..eba9f53 --- /dev/null +++ b/agent/src/main/java/com/fanruan/jdbc/connection/MyConnection.java @@ -0,0 +1,292 @@ +package com.fanruan.jdbc.connection; + +import com.fanruan.jdbc.statement.MyPreparedStatement; +import com.fanruan.jdbc.statement.MyStatement; + +import java.sql.*; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +/** + * @author Yichen Dai + */ +public class MyConnection implements Connection { + final private Connection conn; + + public MyConnection(Connection conn){ + this.conn = conn; + } + + @Override + public Statement createStatement() throws SQLException { + Statement st = this.conn.createStatement(); + return new MyStatement(st); + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + PreparedStatement pst = this.conn.prepareStatement(sql); + return new MyPreparedStatement(pst); + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + return conn.prepareCall(sql); + } + + @Override + public String nativeSQL(String sql) throws SQLException { + return conn.nativeSQL(sql); + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + conn.setAutoCommit(autoCommit); + } + + @Override + public boolean getAutoCommit() throws SQLException { + return conn.getAutoCommit(); + } + + @Override + public void commit() throws SQLException { + conn.commit(); + } + + @Override + public void rollback() throws SQLException { + conn.rollback(); + } + + @Override + public void close() throws SQLException { + conn.close(); + } + + @Override + public boolean isClosed() throws SQLException { + return conn.isClosed(); + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return conn.getMetaData(); + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + conn.setReadOnly(readOnly); + } + + @Override + public boolean isReadOnly() throws SQLException { + return conn.isReadOnly(); + } + + @Override + public void setCatalog(String catalog) throws SQLException { + conn.setCatalog(catalog); + } + + @Override + public String getCatalog() throws SQLException { + return conn.getCatalog(); + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + conn.setTransactionIsolation(level); + } + + @Override + public int getTransactionIsolation() throws SQLException { + return conn.getTransactionIsolation(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return conn.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + conn.clearWarnings(); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return conn.createStatement(resultSetType, resultSetConcurrency); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return conn.prepareStatement(sql, resultSetType, resultSetConcurrency); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return conn.prepareCall(sql, resultSetType, resultSetConcurrency); + } + + @Override + public Map> getTypeMap() throws SQLException { + return conn.getTypeMap(); + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + conn.setTypeMap(map); + } + + @Override + public void setHoldability(int holdability) throws SQLException { + conn.setHoldability(holdability); + } + + @Override + public int getHoldability() throws SQLException { + return conn.getHoldability(); + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return conn.setSavepoint(); + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + return conn.setSavepoint(name); + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + conn.rollback(savepoint); + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + conn.releaseSavepoint(savepoint); + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return conn.createStatement(resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return conn.prepareStatement(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return conn.prepareCall(sql, resultSetType, resultSetConcurrency, resultSetHoldability); + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + return conn.prepareStatement(sql, autoGeneratedKeys); + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + return conn.prepareStatement(sql, columnIndexes); + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + return conn.prepareStatement(sql, columnNames); + } + + @Override + public Clob createClob() throws SQLException { + return conn.createClob(); + } + + @Override + public Blob createBlob() throws SQLException { + return conn.createBlob(); + } + + @Override + public NClob createNClob() throws SQLException { + return conn.createNClob(); + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return conn.createSQLXML(); + } + + @Override + public boolean isValid(int timeout) throws SQLException { + return conn.isValid(timeout); + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + conn.setClientInfo(name, value); + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + conn.setClientInfo(properties); + } + + @Override + public String getClientInfo(String name) throws SQLException { + return conn.getClientInfo(name); + } + + @Override + public Properties getClientInfo() throws SQLException { + return conn.getClientInfo(); + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return conn.createArrayOf(typeName, elements); + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return conn.createStruct(typeName, attributes); + } + + @Override + public void setSchema(String schema) throws SQLException { + conn.setSchema(schema); + } + + @Override + public String getSchema() throws SQLException { + return conn.getSchema(); + } + + @Override + public void abort(Executor executor) throws SQLException { + conn.abort(executor); + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + conn.setNetworkTimeout(executor, milliseconds); + } + + @Override + public int getNetworkTimeout() throws SQLException { + return conn.getNetworkTimeout(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return conn.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return conn.isWrapperFor(iface); + } +} diff --git a/agent/src/main/java/com/fanruan/jdbc/driver/MyDriver.java b/agent/src/main/java/com/fanruan/jdbc/driver/MyDriver.java new file mode 100644 index 0000000..61c262e --- /dev/null +++ b/agent/src/main/java/com/fanruan/jdbc/driver/MyDriver.java @@ -0,0 +1,74 @@ +package com.fanruan.jdbc.driver; + +import com.fanruan.jdbc.connection.MyConnection; + +import java.sql.*; +import java.util.Enumeration; +import java.util.Properties; +import java.util.logging.Logger; + +/** + * @author Yichen Dai + */ +public class MyDriver implements Driver { + + + static public final int DRIVER_VERSION_MAJOR = 1; + static public final int DRIVER_VERSION_MINOR = 1; + + //依靠静态函数块注册驱动 + static{ + try { + DriverManager.registerDriver(new MyDriver()); + } catch (Exception e) { + throw new RuntimeException("Can't register driver"); + } + } + + @Override + public Connection connect(String url, Properties info) throws SQLException { + return new MyConnection(DriverManager.getConnection(url, info)); + } + + @Override + public boolean acceptsURL(String url) throws SQLException { + Enumeration registeredDrivers = DriverManager.getDrivers(); + while (registeredDrivers.hasMoreElements()) { + Driver driver = registeredDrivers.nextElement(); + if(driver instanceof MyDriver){ + continue; + } + if(driver.acceptsURL(url)){ + return true; + } + } + return false; + } + + @Override + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info){ + return new DriverPropertyInfo[0]; + } + + @Override + public int getMajorVersion() { + return DRIVER_VERSION_MAJOR; + } + + @Override + public int getMinorVersion() { + return DRIVER_VERSION_MINOR; + } + + @Override + public boolean jdbcCompliant() { + return false; + } + + @Override + public Logger getParentLogger(){ + return null; + } +} + + diff --git a/agent/src/main/java/com/fanruan/jdbc/resultset/MyResultSet.java b/agent/src/main/java/com/fanruan/jdbc/resultset/MyResultSet.java new file mode 100644 index 0000000..1432acf --- /dev/null +++ b/agent/src/main/java/com/fanruan/jdbc/resultset/MyResultSet.java @@ -0,0 +1,973 @@ +package com.fanruan.jdbc.resultset; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.Calendar; +import java.util.Map; + +public class MyResultSet implements ResultSet { + + final private ResultSet resultSet; + + public MyResultSet(ResultSet resultSet){ + this.resultSet = resultSet; + } + + @Override + public boolean next() throws SQLException { + return resultSet.next(); + } + + @Override + public void close() throws SQLException { + resultSet.close(); + } + + @Override + public boolean wasNull() throws SQLException { + return resultSet.wasNull(); + } + + @Override + public String getString(int columnIndex) throws SQLException { + return resultSet.getString(columnIndex); + } + + @Override + public boolean getBoolean(int columnIndex) throws SQLException { + return resultSet.getBoolean(columnIndex); + } + + @Override + public byte getByte(int columnIndex) throws SQLException { + return resultSet.getByte(columnIndex); + } + + @Override + public short getShort(int columnIndex) throws SQLException { + return resultSet.getShort(columnIndex); + } + + @Override + public int getInt(int columnIndex) throws SQLException { + return resultSet.getInt(columnIndex); + } + + @Override + public long getLong(int columnIndex) throws SQLException { + return resultSet.getLong(columnIndex); + } + + @Override + public float getFloat(int columnIndex) throws SQLException { + return resultSet.getFloat(columnIndex); + } + + @Override + public double getDouble(int columnIndex) throws SQLException { + return resultSet.getDouble(columnIndex); + } + + @Override + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { + return resultSet.getBigDecimal(columnIndex, scale); + } + + @Override + public byte[] getBytes(int columnIndex) throws SQLException { + return resultSet.getBytes(columnIndex); + } + + @Override + public Date getDate(int columnIndex) throws SQLException { + return resultSet.getDate(columnIndex); + } + + @Override + public Time getTime(int columnIndex) throws SQLException { + return resultSet.getTime(columnIndex); + } + + @Override + public Timestamp getTimestamp(int columnIndex) throws SQLException { + return resultSet.getTimestamp(columnIndex); + } + + @Override + public InputStream getAsciiStream(int columnIndex) throws SQLException { + return resultSet.getAsciiStream(columnIndex); + } + + @Override + public InputStream getUnicodeStream(int columnIndex) throws SQLException { + return resultSet.getUnicodeStream(columnIndex); + } + + @Override + public InputStream getBinaryStream(int columnIndex) throws SQLException { + return resultSet.getBinaryStream(columnIndex); + } + + @Override + public String getString(String columnLabel) throws SQLException { + return resultSet.getString(columnLabel); + } + + @Override + public boolean getBoolean(String columnLabel) throws SQLException { + return resultSet.getBoolean(columnLabel); + } + + @Override + public byte getByte(String columnLabel) throws SQLException { + return resultSet.getByte(columnLabel); + } + + @Override + public short getShort(String columnLabel) throws SQLException { + return resultSet.getShort(columnLabel); + } + + @Override + public int getInt(String columnLabel) throws SQLException { + return resultSet.getInt(columnLabel); + } + + @Override + public long getLong(String columnLabel) throws SQLException { + return resultSet.getLong(columnLabel); + } + + @Override + public float getFloat(String columnLabel) throws SQLException { + return resultSet.getFloat(columnLabel); + } + + @Override + public double getDouble(String columnLabel) throws SQLException { + return resultSet.getDouble(columnLabel); + } + + @Override + public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { + return resultSet.getBigDecimal(columnLabel, scale); + } + + @Override + public byte[] getBytes(String columnLabel) throws SQLException { + return resultSet.getBytes(columnLabel); + } + + @Override + public Date getDate(String columnLabel) throws SQLException { + return resultSet.getDate(columnLabel); + } + + @Override + public Time getTime(String columnLabel) throws SQLException { + return resultSet.getTime(columnLabel); + } + + @Override + public Timestamp getTimestamp(String columnLabel) throws SQLException { + return resultSet.getTimestamp(columnLabel); + } + + @Override + public InputStream getAsciiStream(String columnLabel) throws SQLException { + return resultSet.getAsciiStream(columnLabel); + } + + @Override + public InputStream getUnicodeStream(String columnLabel) throws SQLException { + return resultSet.getUnicodeStream(columnLabel); + } + + @Override + public InputStream getBinaryStream(String columnLabel) throws SQLException { + return resultSet.getBinaryStream(columnLabel); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return resultSet.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + resultSet.clearWarnings(); + } + + @Override + public String getCursorName() throws SQLException { + return resultSet.getCursorName(); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + return resultSet.getMetaData(); + } + + @Override + public Object getObject(int columnIndex) throws SQLException { + return resultSet.getObject(columnIndex); + } + + @Override + public Object getObject(String columnLabel) throws SQLException { + return resultSet.getObject(columnLabel); + } + + @Override + public int findColumn(String columnLabel) throws SQLException { + return resultSet.findColumn(columnLabel); + } + + @Override + public Reader getCharacterStream(int columnIndex) throws SQLException { + return resultSet.getCharacterStream(columnIndex); + } + + @Override + public Reader getCharacterStream(String columnLabel) throws SQLException { + return resultSet.getCharacterStream(columnLabel); + } + + @Override + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + return resultSet.getBigDecimal(columnIndex); + } + + @Override + public BigDecimal getBigDecimal(String columnLabel) throws SQLException { + return resultSet.getBigDecimal(columnLabel); + } + + @Override + public boolean isBeforeFirst() throws SQLException { + return resultSet.isBeforeFirst(); + } + + @Override + public boolean isAfterLast() throws SQLException { + return resultSet.isAfterLast(); + } + + @Override + public boolean isFirst() throws SQLException { + return resultSet.isFirst(); + } + + @Override + public boolean isLast() throws SQLException { + return resultSet.isLast(); + } + + @Override + public void beforeFirst() throws SQLException { + resultSet.beforeFirst(); + } + + @Override + public void afterLast() throws SQLException { + resultSet.afterLast(); + } + + @Override + public boolean first() throws SQLException { + return resultSet.first(); + } + + @Override + public boolean last() throws SQLException { + return resultSet.last(); + } + + @Override + public int getRow() throws SQLException { + return resultSet.getRow(); + } + + @Override + public boolean absolute(int row) throws SQLException { + return resultSet.absolute(row); + } + + @Override + public boolean relative(int rows) throws SQLException { + return resultSet.relative(rows); + } + + @Override + public boolean previous() throws SQLException { + return resultSet.previous(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + resultSet.setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + return resultSet.getFetchDirection(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + resultSet.setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + return resultSet.getFetchSize(); + } + + @Override + public int getType() throws SQLException { + return resultSet.getType(); + } + + @Override + public int getConcurrency() throws SQLException { + return resultSet.getConcurrency(); + } + + @Override + public boolean rowUpdated() throws SQLException { + return resultSet.rowUpdated(); + } + + @Override + public boolean rowInserted() throws SQLException { + return resultSet.rowInserted(); + } + + @Override + public boolean rowDeleted() throws SQLException { + return resultSet.rowDeleted(); + } + + @Override + public void updateNull(int columnIndex) throws SQLException { + resultSet.updateNull(columnIndex); + } + + @Override + public void updateBoolean(int columnIndex, boolean x) throws SQLException { + resultSet.updateBoolean(columnIndex, x); + } + + @Override + public void updateByte(int columnIndex, byte x) throws SQLException { + resultSet.updateByte(columnIndex, x); + } + + @Override + public void updateShort(int columnIndex, short x) throws SQLException { + resultSet.updateShort(columnIndex, x); + } + + @Override + public void updateInt(int columnIndex, int x) throws SQLException { + resultSet.updateInt(columnIndex, x); + } + + @Override + public void updateLong(int columnIndex, long x) throws SQLException { + resultSet.updateLong(columnIndex, x); + } + + @Override + public void updateFloat(int columnIndex, float x) throws SQLException { + resultSet.updateFloat(columnIndex, x); + } + + @Override + public void updateDouble(int columnIndex, double x) throws SQLException { + resultSet.updateDouble(columnIndex, x); + } + + @Override + public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + resultSet.updateBigDecimal(columnIndex, x); + } + + @Override + public void updateString(int columnIndex, String x) throws SQLException { + resultSet.updateString(columnIndex, x); + } + + @Override + public void updateBytes(int columnIndex, byte[] x) throws SQLException { + resultSet.updateBytes(columnIndex, x); + } + + @Override + public void updateDate(int columnIndex, Date x) throws SQLException { + resultSet.updateDate(columnIndex, x); + } + + @Override + public void updateTime(int columnIndex, Time x) throws SQLException { + resultSet.updateTime(columnIndex, x); + } + + @Override + public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { + resultSet.updateTimestamp(columnIndex, x); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { + resultSet.updateAsciiStream(columnIndex, x, length); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { + resultSet.updateBinaryStream(columnIndex, x, length); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { + resultSet.updateCharacterStream(columnIndex, x, length); + } + + @Override + public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { + resultSet.updateObject(columnIndex, x, scaleOrLength); + } + + @Override + public void updateObject(int columnIndex, Object x) throws SQLException { + resultSet.updateObject(columnIndex, x); + } + + @Override + public void updateNull(String columnLabel) throws SQLException { + resultSet.updateNull(columnLabel); + } + + @Override + public void updateBoolean(String columnLabel, boolean x) throws SQLException { + resultSet.updateBoolean(columnLabel, x); + } + + @Override + public void updateByte(String columnLabel, byte x) throws SQLException { + resultSet.updateByte(columnLabel, x); + } + + @Override + public void updateShort(String columnLabel, short x) throws SQLException { + resultSet.updateShort(columnLabel, x); + } + + @Override + public void updateInt(String columnLabel, int x) throws SQLException { + resultSet.updateInt(columnLabel, x); + } + + @Override + public void updateLong(String columnLabel, long x) throws SQLException { + resultSet.updateLong(columnLabel, x); + } + + @Override + public void updateFloat(String columnLabel, float x) throws SQLException { + resultSet.updateFloat(columnLabel, x); + } + + @Override + public void updateDouble(String columnLabel, double x) throws SQLException { + resultSet.updateDouble(columnLabel, x); + } + + @Override + public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { + resultSet.updateBigDecimal(columnLabel, x); + } + + @Override + public void updateString(String columnLabel, String x) throws SQLException { + resultSet.updateString(columnLabel, x); + } + + @Override + public void updateBytes(String columnLabel, byte[] x) throws SQLException { + resultSet.updateBytes(columnLabel, x); + } + + @Override + public void updateDate(String columnLabel, Date x) throws SQLException { + resultSet.updateDate(columnLabel, x); + } + + @Override + public void updateTime(String columnLabel, Time x) throws SQLException { + resultSet.updateTime(columnLabel, x); + } + + @Override + public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { + resultSet.updateTimestamp(columnLabel, x); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { + resultSet.updateAsciiStream(columnLabel, x, length); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { + resultSet.updateBinaryStream(columnLabel, x, length); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { + resultSet.updateCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException { + resultSet.updateObject(columnLabel, x, scaleOrLength); + } + + @Override + public void updateObject(String columnLabel, Object x) throws SQLException { + resultSet.updateObject(columnLabel, x); + } + + @Override + public void insertRow() throws SQLException { + resultSet.insertRow(); + } + + @Override + public void updateRow() throws SQLException { + resultSet.updateRow(); + } + + @Override + public void deleteRow() throws SQLException { + resultSet.deleteRow(); + } + + @Override + public void refreshRow() throws SQLException { + resultSet.refreshRow(); + } + + @Override + public void cancelRowUpdates() throws SQLException { + resultSet.cancelRowUpdates(); + } + + @Override + public void moveToInsertRow() throws SQLException { + resultSet.moveToInsertRow(); + } + + @Override + public void moveToCurrentRow() throws SQLException { + resultSet.moveToCurrentRow(); + } + + @Override + public Statement getStatement() throws SQLException { + return resultSet.getStatement(); + } + + @Override + public Object getObject(int columnIndex, Map> map) throws SQLException { + return resultSet.getObject(columnIndex, map); + } + + @Override + public Ref getRef(int columnIndex) throws SQLException { + return resultSet.getRef(columnIndex); + } + + @Override + public Blob getBlob(int columnIndex) throws SQLException { + return resultSet.getBlob(columnIndex); + } + + @Override + public Clob getClob(int columnIndex) throws SQLException { + return resultSet.getClob(columnIndex); + } + + @Override + public Array getArray(int columnIndex) throws SQLException { + return resultSet.getArray(columnIndex); + } + + @Override + public Object getObject(String columnLabel, Map> map) throws SQLException { + return resultSet.getObject(columnLabel, map); + } + + @Override + public Ref getRef(String columnLabel) throws SQLException { + return resultSet.getRef(columnLabel); + } + + @Override + public Blob getBlob(String columnLabel) throws SQLException { + return resultSet.getBlob(columnLabel); + } + + @Override + public Clob getClob(String columnLabel) throws SQLException { + return resultSet.getClob(columnLabel); + } + + @Override + public Array getArray(String columnLabel) throws SQLException { + return resultSet.getArray(columnLabel); + } + + @Override + public Date getDate(int columnIndex, Calendar cal) throws SQLException { + return resultSet.getDate(columnIndex, cal); + } + + @Override + public Date getDate(String columnLabel, Calendar cal) throws SQLException { + return resultSet.getDate(columnLabel, cal); + } + + @Override + public Time getTime(int columnIndex, Calendar cal) throws SQLException { + return resultSet.getTime(columnIndex, cal); + } + + @Override + public Time getTime(String columnLabel, Calendar cal) throws SQLException { + return resultSet.getTime(columnLabel, cal); + } + + @Override + public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { + return resultSet.getTimestamp(columnIndex, cal); + } + + @Override + public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { + return resultSet.getTimestamp(columnLabel, cal); + } + + @Override + public URL getURL(int columnIndex) throws SQLException { + return resultSet.getURL(columnIndex); + } + + @Override + public URL getURL(String columnLabel) throws SQLException { + return resultSet.getURL(columnLabel); + } + + @Override + public void updateRef(int columnIndex, Ref x) throws SQLException { + resultSet.updateRef(columnIndex, x); + } + + @Override + public void updateRef(String columnLabel, Ref x) throws SQLException { + resultSet.updateRef(columnLabel, x); + } + + @Override + public void updateBlob(int columnIndex, Blob x) throws SQLException { + resultSet.updateBlob(columnIndex, x); + } + + @Override + public void updateBlob(String columnLabel, Blob x) throws SQLException { + resultSet.updateBlob(columnLabel, x); + } + + @Override + public void updateClob(int columnIndex, Clob x) throws SQLException { + resultSet.updateClob(columnIndex, x); + } + + @Override + public void updateClob(String columnLabel, Clob x) throws SQLException { + resultSet.updateClob(columnLabel, x); + } + + @Override + public void updateArray(int columnIndex, Array x) throws SQLException { + resultSet.updateArray(columnIndex, x); + } + + @Override + public void updateArray(String columnLabel, Array x) throws SQLException { + resultSet.updateArray(columnLabel, x); + } + + @Override + public RowId getRowId(int columnIndex) throws SQLException { + return resultSet.getRowId(columnIndex); + } + + @Override + public RowId getRowId(String columnLabel) throws SQLException { + return resultSet.getRowId(columnLabel); + } + + @Override + public void updateRowId(int columnIndex, RowId x) throws SQLException { + resultSet.updateRowId(columnIndex, x); + } + + @Override + public void updateRowId(String columnLabel, RowId x) throws SQLException { + resultSet.updateRowId(columnLabel, x); + } + + @Override + public int getHoldability() throws SQLException { + return resultSet.getHoldability(); + } + + @Override + public boolean isClosed() throws SQLException { + return resultSet.isClosed(); + } + + @Override + public void updateNString(int columnIndex, String nString) throws SQLException { + resultSet.updateNString(columnIndex, nString); + } + + @Override + public void updateNString(String columnLabel, String nString) throws SQLException { + resultSet.updateNString(columnLabel, nString); + } + + @Override + public void updateNClob(int columnIndex, NClob nClob) throws SQLException { + resultSet.updateNClob(columnIndex, nClob); + } + + @Override + public void updateNClob(String columnLabel, NClob nClob) throws SQLException { + resultSet.updateNClob(columnLabel, nClob); + } + + @Override + public NClob getNClob(int columnIndex) throws SQLException { + return resultSet.getNClob(columnIndex); + } + + @Override + public NClob getNClob(String columnLabel) throws SQLException { + return resultSet.getNClob(columnLabel); + } + + @Override + public SQLXML getSQLXML(int columnIndex) throws SQLException { + return resultSet.getSQLXML(columnIndex); + } + + @Override + public SQLXML getSQLXML(String columnLabel) throws SQLException { + return resultSet.getSQLXML(columnLabel); + } + + @Override + public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { + resultSet.updateSQLXML(columnIndex, xmlObject); + } + + @Override + public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { + resultSet.updateSQLXML(columnLabel, xmlObject); + } + + @Override + public String getNString(int columnIndex) throws SQLException { + return resultSet.getNString(columnIndex); + } + + @Override + public String getNString(String columnLabel) throws SQLException { + return resultSet.getNString(columnLabel); + } + + @Override + public Reader getNCharacterStream(int columnIndex) throws SQLException { + return resultSet.getNCharacterStream(columnIndex); + } + + @Override + public Reader getNCharacterStream(String columnLabel) throws SQLException { + return resultSet.getNCharacterStream(columnLabel); + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + resultSet.updateNCharacterStream(columnIndex, x, length); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + resultSet.updateNCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { + resultSet.updateAsciiStream(columnIndex, x, length); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { + resultSet.updateBinaryStream(columnIndex, x, length); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + resultSet.updateCharacterStream(columnIndex, x, length); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { + resultSet.updateAsciiStream(columnLabel, x, length); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { + resultSet.updateBinaryStream(columnLabel, x, length); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + resultSet.updateCharacterStream(columnLabel, reader, length); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { + resultSet.updateBlob(columnIndex, inputStream, length); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { + resultSet.updateBlob(columnLabel, inputStream, length); + } + + @Override + public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { + resultSet.updateClob(columnIndex, reader, length); + } + + @Override + public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { + resultSet.updateClob(columnLabel, reader, length); + } + + @Override + public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { + resultSet.updateNClob(columnIndex, reader, length); + } + + @Override + public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { + resultSet.updateNClob(columnLabel, reader, length); + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { + resultSet.updateNCharacterStream(columnIndex, x); + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { + resultSet.updateNCharacterStream(columnLabel, reader); + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { + resultSet.updateAsciiStream(columnIndex, x); + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { + resultSet.updateBinaryStream(columnIndex, x); + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { + resultSet.updateCharacterStream(columnIndex, x); + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { + resultSet.updateAsciiStream(columnLabel, x); + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { + resultSet.updateBinaryStream(columnLabel, x); + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { + resultSet.updateCharacterStream(columnLabel, reader); + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { + resultSet.updateBlob(columnIndex, inputStream); + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { + resultSet.updateBlob(columnLabel, inputStream); + } + + @Override + public void updateClob(int columnIndex, Reader reader) throws SQLException { + resultSet.updateClob(columnIndex, reader); + } + + @Override + public void updateClob(String columnLabel, Reader reader) throws SQLException { + resultSet.updateClob(columnLabel, reader); + } + + @Override + public void updateNClob(int columnIndex, Reader reader) throws SQLException { + resultSet.updateNClob(columnIndex, reader); + } + + @Override + public void updateNClob(String columnLabel, Reader reader) throws SQLException { + resultSet.updateNClob(columnLabel, reader); + } + + @Override + public T getObject(int columnIndex, Class type) throws SQLException { + return resultSet.getObject(columnIndex, type); + } + + @Override + public T getObject(String columnLabel, Class type) throws SQLException { + return resultSet.getObject(columnLabel, type); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return resultSet.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return resultSet.isWrapperFor(iface); + } +} diff --git a/agent/src/main/java/com/fanruan/jdbc/statement/MyPreparedStatement.java b/agent/src/main/java/com/fanruan/jdbc/statement/MyPreparedStatement.java new file mode 100644 index 0000000..a96e520 --- /dev/null +++ b/agent/src/main/java/com/fanruan/jdbc/statement/MyPreparedStatement.java @@ -0,0 +1,517 @@ +package com.fanruan.jdbc.statement; + + +import com.fanruan.jdbc.resultset.MyResultSet; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.Calendar; + +/** + * @author Yichen Dai + */ +public class MyPreparedStatement implements PreparedStatement { + + final PreparedStatement pst; + + public MyPreparedStatement(PreparedStatement pst) { + this.pst = pst; + } + + @Override + public ResultSet executeQuery() throws SQLException { + return new MyResultSet(pst.executeQuery()); + } + + @Override + public int executeUpdate() throws SQLException { + return pst.executeUpdate(); + } + + @Override + public void setNull(int parameterIndex, int sqlType) throws SQLException { + pst.setNull(parameterIndex, sqlType); + } + + @Override + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + pst.setBoolean(parameterIndex, x); + } + + @Override + public void setByte(int parameterIndex, byte x) throws SQLException { + pst.setByte(parameterIndex, x); + } + + @Override + public void setShort(int parameterIndex, short x) throws SQLException { + pst.setShort(parameterIndex, x); + } + + @Override + public void setInt(int parameterIndex, int x) throws SQLException { + pst.setInt(parameterIndex, x); + } + + @Override + public void setLong(int parameterIndex, long x) throws SQLException { + pst.setLong(parameterIndex, x); + } + + @Override + public void setFloat(int parameterIndex, float x) throws SQLException { + pst.setFloat(parameterIndex, x); + } + + @Override + public void setDouble(int parameterIndex, double x) throws SQLException { + pst.setDouble(parameterIndex, x); + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + pst.setBigDecimal(parameterIndex, x); + } + + @Override + public void setString(int parameterIndex, String x) throws SQLException { + pst.setString(parameterIndex, x); + } + + @Override + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + pst.setBytes(parameterIndex, x); + } + + @Override + public void setDate(int parameterIndex, Date x) throws SQLException { + pst.setDate(parameterIndex, x); + } + + @Override + public void setTime(int parameterIndex, Time x) throws SQLException { + pst.setTime(parameterIndex, x); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + pst.setTimestamp(parameterIndex, x); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + pst.setAsciiStream(parameterIndex, x, length); + } + + @Override + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + pst.setBinaryStream(parameterIndex, x, length); + } + + @Override + public void clearParameters() throws SQLException { + pst.clearParameters(); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + pst.setObject(parameterIndex, x, targetSqlType); + } + + @Override + public void setObject(int parameterIndex, Object x) throws SQLException { + pst.setObject(parameterIndex, x); + } + + @Override + public boolean execute() throws SQLException { + return pst.execute(); + } + + @Override + public void addBatch() throws SQLException { + pst.addBatch(); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + pst.setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setRef(int parameterIndex, Ref x) throws SQLException { + pst.setRef(parameterIndex, x); + } + + @Override + public void setBlob(int parameterIndex, Blob x) throws SQLException { + pst.setBlob(parameterIndex, x); + } + + @Override + public void setClob(int parameterIndex, Clob x) throws SQLException { + pst.setClob(parameterIndex, x); + } + + @Override + public void setArray(int parameterIndex, Array x) throws SQLException { + pst.setArray(parameterIndex, x); + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + return pst.getMetaData(); + } + + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + pst.setDate(parameterIndex, x, cal); + } + + @Override + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + pst.setTime(parameterIndex, x, cal); + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + pst.setTimestamp(parameterIndex, x, cal); + } + + @Override + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + pst.setNull(parameterIndex, sqlType, typeName); + } + + @Override + public void setURL(int parameterIndex, URL x) throws SQLException { + pst.setURL(parameterIndex, x); + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + return pst.getParameterMetaData(); + } + + @Override + public void setRowId(int parameterIndex, RowId x) throws SQLException { + pst.setRowId(parameterIndex, x); + } + + @Override + public void setNString(int parameterIndex, String value) throws SQLException { + pst.setNString(parameterIndex, value); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + pst.setNCharacterStream(parameterIndex, value, length); + } + + @Override + public void setNClob(int parameterIndex, NClob value) throws SQLException { + pst.setNClob(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + pst.setClob(parameterIndex, reader, length); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + pst.setBlob(parameterIndex, inputStream, length); + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + pst.setNClob(parameterIndex, reader, length); + } + + @Override + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + pst.setSQLXML(parameterIndex, xmlObject); + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + pst.setObject(parameterIndex, x, targetSqlType, scaleOrLength); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + pst.setAsciiStream(parameterIndex, x, length); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + pst.setBinaryStream(parameterIndex, x, length); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + pst.setCharacterStream(parameterIndex, reader, length); + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + pst.setAsciiStream(parameterIndex, x); + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + pst.setBinaryStream(parameterIndex, x); + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + pst.setCharacterStream(parameterIndex, reader); + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + pst.setNCharacterStream(parameterIndex, value); + } + + @Override + public void setClob(int parameterIndex, Reader reader) throws SQLException { + pst.setClob(parameterIndex, reader); + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + pst.setBlob(parameterIndex, inputStream); + } + + @Override + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + pst.setNClob(parameterIndex, reader); + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + return pst.executeQuery(sql); + } + + @Override + public int executeUpdate(String sql) throws SQLException { + return pst.executeUpdate(sql); + } + + @Override + public void close() throws SQLException { + pst.close(); + } + + @Override + public int getMaxFieldSize() throws SQLException { + return pst.getMaxFieldSize(); + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + pst.setMaxFieldSize(max); + } + + @Override + public int getMaxRows() throws SQLException { + return pst.getMaxRows(); + } + + @Override + public void setMaxRows(int max) throws SQLException { + pst.setMaxRows(max); + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + pst.setEscapeProcessing(enable); + } + + @Override + public int getQueryTimeout() throws SQLException { + return pst.getQueryTimeout(); + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + pst.setQueryTimeout(seconds); + } + + @Override + public void cancel() throws SQLException { + pst.cancel(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return pst.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + pst.clearWarnings(); + } + + @Override + public void setCursorName(String name) throws SQLException { + pst.setCursorName(name); + } + + @Override + public boolean execute(String sql) throws SQLException { + return pst.execute(sql); + } + + @Override + public ResultSet getResultSet() throws SQLException { + return pst.getResultSet(); + } + + @Override + public int getUpdateCount() throws SQLException { + return pst.getUpdateCount(); + } + + @Override + public boolean getMoreResults() throws SQLException { + return pst.getMoreResults(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + pst.setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + return pst.getFetchDirection(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + pst.setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + return pst.getFetchSize(); + } + + @Override + public int getResultSetConcurrency() throws SQLException { + return pst.getResultSetConcurrency(); + } + + @Override + public int getResultSetType() throws SQLException { + return pst.getResultSetType(); + } + + @Override + public void addBatch(String sql) throws SQLException { + pst.addBatch(sql); + } + + @Override + public void clearBatch() throws SQLException { + pst.clearBatch(); + } + + @Override + public int[] executeBatch() throws SQLException { + return pst.executeBatch(); + } + + @Override + public Connection getConnection() throws SQLException { + return pst.getConnection(); + } + + @Override + public boolean getMoreResults(int current) throws SQLException { + return pst.getMoreResults(current); + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + return pst.getGeneratedKeys(); + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return pst.executeUpdate(sql, autoGeneratedKeys); + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + return pst.executeUpdate(sql, columnIndexes); + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + return pst.executeUpdate(sql, columnNames); + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + return pst.execute(sql, autoGeneratedKeys); + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + return pst.execute(sql, columnIndexes); + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + return pst.execute(sql, columnNames); + } + + @Override + public int getResultSetHoldability() throws SQLException { + return pst.getResultSetHoldability(); + } + + @Override + public boolean isClosed() throws SQLException { + return pst.isClosed(); + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + pst.setPoolable(poolable); + } + + @Override + public boolean isPoolable() throws SQLException { + return pst.isPoolable(); + } + + @Override + public void closeOnCompletion() throws SQLException { + pst.closeOnCompletion(); + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + return pst.isCloseOnCompletion(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return pst.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return pst.isWrapperFor(iface); + } +} diff --git a/agent/src/main/java/com/fanruan/jdbc/statement/MyStatement.java b/agent/src/main/java/com/fanruan/jdbc/statement/MyStatement.java new file mode 100644 index 0000000..709428f --- /dev/null +++ b/agent/src/main/java/com/fanruan/jdbc/statement/MyStatement.java @@ -0,0 +1,237 @@ +package com.fanruan.jdbc.statement; + +import com.fanruan.jdbc.resultset.MyResultSet; + +import java.sql.*; + +public class MyStatement implements Statement { + + final private Statement st; + + + public MyStatement(Statement statement) { + this.st = statement; + } + + + //使用与 Service 同名的类保证数据库对应的 JDBC + @Override + public ResultSet executeQuery(String sql) throws SQLException { + return new MyResultSet(st.executeQuery(sql)); + } + + @Override + public int executeUpdate(String sql) throws SQLException { + return st.executeUpdate(sql); + } + + @Override + public void close() throws SQLException { + st.close(); + } + + @Override + public int getMaxFieldSize() throws SQLException { + return st.getMaxFieldSize(); + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + st.setMaxFieldSize(max); + } + + @Override + public int getMaxRows() throws SQLException { + return st.getMaxRows(); + } + + @Override + public void setMaxRows(int max) throws SQLException { + st.setMaxRows(max); + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + st.setEscapeProcessing(enable); + } + + @Override + public int getQueryTimeout() throws SQLException { + return st.getQueryTimeout(); + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + st.setQueryTimeout(seconds); + } + + @Override + public void cancel() throws SQLException { + st.cancel(); + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return st.getWarnings(); + } + + @Override + public void clearWarnings() throws SQLException { + st.clearWarnings(); + } + + @Override + public void setCursorName(String name) throws SQLException { + st.setCursorName(name); + } + + @Override + public boolean execute(String sql) throws SQLException { + return st.execute(sql); + } + + @Override + public ResultSet getResultSet() throws SQLException { + return st.getResultSet(); + } + + @Override + public int getUpdateCount() throws SQLException { + return st.getUpdateCount(); + } + + @Override + public boolean getMoreResults() throws SQLException { + return st.getMoreResults(); + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + st.setFetchDirection(direction); + } + + @Override + public int getFetchDirection() throws SQLException { + return st.getFetchDirection(); + } + + @Override + public void setFetchSize(int rows) throws SQLException { + st.setFetchSize(rows); + } + + @Override + public int getFetchSize() throws SQLException { + return st.getFetchSize(); + } + + @Override + public int getResultSetConcurrency() throws SQLException { + return st.getResultSetConcurrency(); + } + + @Override + public int getResultSetType() throws SQLException { + return st.getResultSetType(); + } + + @Override + public void addBatch(String sql) throws SQLException { + st.addBatch(sql); + } + + @Override + public void clearBatch() throws SQLException { + st.clearBatch(); + } + + @Override + public int[] executeBatch() throws SQLException { + return st.executeBatch(); + } + + @Override + public Connection getConnection() throws SQLException { + return st.getConnection(); + } + + @Override + public boolean getMoreResults(int current) throws SQLException { + return st.getMoreResults(current); + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + return st.getGeneratedKeys(); + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return st.executeUpdate(sql, autoGeneratedKeys); + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + return st.executeUpdate(sql, columnIndexes); + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + return st.executeUpdate(sql, columnNames); + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + return st.execute(sql, autoGeneratedKeys); + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + return st.execute(sql, columnIndexes); + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + return st.execute(sql, columnNames); + } + + @Override + public int getResultSetHoldability() throws SQLException { + return st.getResultSetHoldability(); + } + + @Override + public boolean isClosed() throws SQLException { + return st.isClosed(); + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + st.setPoolable(poolable); + } + + @Override + public boolean isPoolable() throws SQLException { + return st.isPoolable(); + } + + @Override + public void closeOnCompletion() throws SQLException { + st.closeOnCompletion(); + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + return st.isCloseOnCompletion(); + } + + @Override + public T unwrap(Class iface) throws SQLException { + return st.unwrap(iface); + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return st.isWrapperFor(iface); + } +} diff --git a/agent/src/main/java/com/fanruan/pojo/message/RpcRequest.java b/agent/src/main/java/com/fanruan/pojo/message/RpcRequest.java new file mode 100644 index 0000000..afa4879 --- /dev/null +++ b/agent/src/main/java/com/fanruan/pojo/message/RpcRequest.java @@ -0,0 +1,20 @@ +package com.fanruan.pojo.message; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * @author Yichen Dai + */ +@Data +@Accessors(chain = true) +public class RpcRequest { + private boolean reply; + private String ID; + private boolean binding; + private String IDToInvoke; + private Class serviceClass; + private String methodName; + private Object[] args; + private Class[] argTypes; +} diff --git a/agent/src/main/java/com/fanruan/pojo/message/RpcResponse.java b/agent/src/main/java/com/fanruan/pojo/message/RpcResponse.java new file mode 100644 index 0000000..29b9583 --- /dev/null +++ b/agent/src/main/java/com/fanruan/pojo/message/RpcResponse.java @@ -0,0 +1,19 @@ +package com.fanruan.pojo.message; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * @author Yichen Dai + */ +@Data +@Accessors(chain = true) +public class RpcResponse { + private String ID; + + private Object result; + + private boolean binding; + + private Boolean status; +} \ No newline at end of file diff --git a/agent/src/main/java/com/fanruan/serializer/KryoSerializer.java b/agent/src/main/java/com/fanruan/serializer/KryoSerializer.java new file mode 100644 index 0000000..fe16a1b --- /dev/null +++ b/agent/src/main/java/com/fanruan/serializer/KryoSerializer.java @@ -0,0 +1,54 @@ +package com.fanruan.serializer; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.fanruan.pojo.message.RpcRequest; +import com.fanruan.pojo.message.RpcResponse; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * @author Yichen Dai + */ +public class KryoSerializer implements Serializer { + + private static final ThreadLocal kryoThreadLocal = ThreadLocal.withInitial(() -> { + final Kryo kryo = new Kryo(); + kryo.register(RpcRequest.class); + kryo.register(RpcResponse.class); + kryo.setReferences(true); + kryo.setRegistrationRequired(false); + return kryo; + }); + + @Override + public byte[] serialize(Object object) { + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Output output = new Output(byteArrayOutputStream)) { + final Kryo kryo = kryoThreadLocal.get(); + kryo.writeObject(output, object); + kryoThreadLocal.remove(); + return output.toBytes(); + } catch (IOException e) { + e.printStackTrace(); + } + return new byte[0]; + } + + @Override + public T deserialize(byte[] bytes, Class clazz) { + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + Input input = new Input(byteArrayInputStream)) { + final Kryo kryo = kryoThreadLocal.get(); + final Object o = kryo.readObject(input, clazz); + kryoThreadLocal.remove(); + return clazz.cast(o); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/agent/src/main/java/com/fanruan/serializer/Serializer.java b/agent/src/main/java/com/fanruan/serializer/Serializer.java new file mode 100644 index 0000000..572e017 --- /dev/null +++ b/agent/src/main/java/com/fanruan/serializer/Serializer.java @@ -0,0 +1,22 @@ +package com.fanruan.serializer; + +/** + * @author Yichen Dai + */ +public interface Serializer { + + /** + * Use to serialize a object to a byte array + * @param object to be serialized + * @return byte[] serialized data with the format of byte array + */ + byte[] serialize(Object object); + + /** + * Use to deserialize a byte array to a class with designate class + * @param bytes Serialized data with the format of byte array + * @param clazz The class of the object to be deserialized + * @return object as designate class + */ + T deserialize(byte[] bytes, Class clazz); +} diff --git a/agent/src/main/java/com/fanruan/utils/DBProperties.java b/agent/src/main/java/com/fanruan/utils/DBProperties.java new file mode 100644 index 0000000..e85399b --- /dev/null +++ b/agent/src/main/java/com/fanruan/utils/DBProperties.java @@ -0,0 +1,20 @@ +package com.fanruan.utils; + +/** + * @author Yichen Dai + */ +public class DBProperties { + public static final String MYSQL = "mysql"; + public static final String POSTGRESQL = "postgresql"; + public static final String ORACLE = "oracle"; + public static final String SQLSERVER = "sqlserver"; + public static final String DB2 = "db2"; + public static final String HSQL = "hsql"; + + public static final String MYSQL_DRIVER_NAME = "com.mysql.cj.jdbc.Driver"; + public static final String POSTGRESQL_DRIVER_NAME = "org.postgresql.Driver"; + public static final String ORACLE_DRIVER_NAME = "oracle.jdbc.driver.OracleDriver"; + public static final String SQLSERVER_DRIVER_NAME = "com.microsoft.jdbc.sqlserver.SQLServerDriver"; + public static final String DB2_DRIVER_NAME = "com.ibm.db2.jdbc.app.DB2Driver"; + public static final String HSQL_DRIVER_NAME = "org.hsqldb.jdbcDriver"; +} diff --git a/agent/src/main/resources/log4j2.properties b/agent/src/main/resources/log4j2.properties new file mode 100644 index 0000000..a46ced9 --- /dev/null +++ b/agent/src/main/resources/log4j2.properties @@ -0,0 +1,10 @@ +# Console appender configuration +appender.console.type = Console +appender.console.name = STDOUT +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n + + +rootLogger.level = debug +rootLogger.appenderRefs = stdout +rootLogger.appenderRef.stdout.ref = STDOUT \ No newline at end of file diff --git a/agent/src/main/resources/socket.properties b/agent/src/main/resources/socket.properties new file mode 100644 index 0000000..79337ee --- /dev/null +++ b/agent/src/main/resources/socket.properties @@ -0,0 +1,14 @@ +# 服务器地址 +uri = http://127.0.0.1:10246 + +# 代理编号 +agentID = 1001 + +# 重连尝试次数 +reconnectionAttempts = 1 + +# 重连间隔事件 +reconnectionDelay = 1000 + +# 连接超时时间 +timeout = 500 \ No newline at end of file diff --git a/agent/src/test/java/HSQLTest.java b/agent/src/test/java/HSQLTest.java new file mode 100644 index 0000000..9ee4c64 --- /dev/null +++ b/agent/src/test/java/HSQLTest.java @@ -0,0 +1,100 @@ +import com.fanruan.utils.DBProperties; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.sql.*; + +/** + * @author Yichen Dai + * @date 2022/8/17 11:25 + */ +public class HSQLTest { + + @BeforeEach + void startHSQL(){ + try { + Class.forName(DBProperties.HSQL_DRIVER_NAME); + Thread.sleep(1000); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Test + void getConnection() throws SQLException { + Connection conn = null; + try { + conn = DriverManager.getConnection("jdbc:hsqldb:mem:test", "sa", ""); + Assertions.assertNotNull(conn, "can't get connection"); + } catch (SQLException e) { + e.printStackTrace(); + }finally { + conn.close(); + } + } + + /** + * test to create statement instance and prepareStatement, create table, select form table, delete from table + */ + @Test + void testCURD(){ + Connection conn = null; + Statement st = null; + PreparedStatement pst = null; + ResultSet rs = null; + try { + conn = DriverManager.getConnection("jdbc:hsqldb:mem:test", "sa", ""); + st = conn.createStatement(); + st.executeUpdate("DROP TABLE student IF EXISTS;"); + + st.executeUpdate("CREATE TABLE student (" + + "student_id INTEGER GENERATED BY DEFAULT AS IDENTITY " + + "(START WITH 1, INCREMENT BY 1) NOT NULL," + + "student_name VARCHAR(100) NOT NULL," + + "student_address VARCHAR(100) NOT NULL," + + "PRIMARY KEY (student_id)" + + ");"); + + st.executeUpdate("INSERT INTO student VALUES" + + "(1, '张三', '上海')," + + "(2, '李四', '北京')," + + "(3, '王五', '成都');"); + + + + + pst = conn.prepareStatement("delete from student where student_id = ?"); + + pst.setInt(1, 1); + + pst.executeUpdate(); + + rs = st.executeQuery("select * from student"); + + String[] nameStrings = new String[]{"张三", "李四", "王五"}; + String[] addressStrings = new String[]{"上海", "北京", "成都"}; + + int num = 2; + while(rs.next()) { + Assertions.assertEquals(rs.getInt("student_id"), num); + Assertions.assertEquals(rs.getString("student_name"), nameStrings[num-1]); + Assertions.assertEquals(rs.getString("student_address"), addressStrings[num-1]); + num++; + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try{ + rs.close(); + st.close(); + pst.close(); + conn.close(); + }catch (Exception e){ + e.printStackTrace(); + } + } + } + + +} diff --git a/agent/src/test/java/Test.java b/agent/src/test/java/Test.java new file mode 100644 index 0000000..9c91c2c --- /dev/null +++ b/agent/src/test/java/Test.java @@ -0,0 +1,37 @@ +import com.fanruan.AgentStarter; +import com.fanruan.utils.DBProperties; +import io.socket.client.Socket; + +import java.io.IOException; + + + +public class Test { + + public static void main(String[] args){ + try{ + testStart(); + }catch(Exception e){ + e.printStackTrace(); + } + + } + + static void testStart() throws IOException, ClassNotFoundException { + Class.forName(DBProperties.HSQL_DRIVER_NAME); + String[] DBs = new String[]{ + DBProperties.HSQL, + }; + + new AgentStarter(DBs); + + Socket mainSocket = AgentStarter.myDispatcherImpl.CACHE.getSocket("/"); + mainSocket.connect(); + + Socket socket = AgentStarter.myDispatcherImpl.CACHE.getSocket(DBProperties.HSQL); + socket.connect(); + + + } + +} diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..bd1a8f2 --- /dev/null +++ b/pom.xml @@ -0,0 +1,21 @@ + + + 4.0.0 + + org.example + intranet + 1.0-SNAPSHOT + + agent + service + + pom + + + 8 + 8 + + + \ No newline at end of file diff --git a/service/pom.xml b/service/pom.xml new file mode 100644 index 0000000..91a4083 --- /dev/null +++ b/service/pom.xml @@ -0,0 +1,82 @@ + + + + intranet + org.example + 1.0-SNAPSHOT + + 4.0.0 + + service + + + 8 + 8 + + + + + com.corundumstudio.socketio + netty-socketio + 1.7.19 + + + + org.apache.logging.log4j + log4j-api + 2.17.2 + + + + org.apache.logging.log4j + log4j-core + 2.17.2 + + + + + org.apache.logging.log4j + log4j-slf4j-impl + 2.17.2 + test + + + + com.google.guava + guava + 31.1-jre + + + + + cglib + cglib + 3.3.0 + + + + + com.esotericsoftware + kryo + 5.3.0 + + + + org.projectlombok + lombok + 1.18.22 + + + + + org.junit.jupiter + junit-jupiter-engine + 5.4.0 + test + + + + + \ No newline at end of file diff --git a/service/src/main/java/com/fanruan/ServerStater.java b/service/src/main/java/com/fanruan/ServerStater.java new file mode 100644 index 0000000..b051f8e --- /dev/null +++ b/service/src/main/java/com/fanruan/ServerStater.java @@ -0,0 +1,193 @@ +package com.fanruan; + +import com.corundumstudio.socketio.SocketConfig; +import com.corundumstudio.socketio.SocketIONamespace; +import com.corundumstudio.socketio.SocketIOServer; +import com.corundumstudio.socketio.Transport; +import com.fanruan.cache.ClientCache; +import com.fanruan.cache.ClientWrapper; +import com.fanruan.cache.LockAndCondition; +import com.fanruan.pojo.message.RpcResponse; +import com.fanruan.serializer.KryoSerializer; +import com.fanruan.serializer.Serializer; +import com.fanruan.utils.Commons; +import com.google.common.util.concurrent.ThreadFactoryBuilder; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Properties; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author Yichen Dai + */ +public class ServerStater{ + protected static final Logger logger = LogManager.getLogger(); + + public static final Serializer SERIALIZER = new KryoSerializer(); + + public static ExecutorService threadPool; + + public static SocketIOServer server; + + public static int workCount; + + + + public ServerStater(String[] dbs){ + try{ + loadConfig(); + for(String dbName : dbs){ + SocketIONamespace namespace = server.addNamespace("/" + dbName); + addEvent(namespace); + } + server.start(); + }catch (Exception e){ + e.printStackTrace(); + } + + } + + private void addEvent(SocketIONamespace nameSpace){ + logger.debug("配置事件监听"); + nameSpace.addConnectListener(client -> { + logger.info(nameSpace.getName() + "- socket connected!"); + String agentID = Commons.getAgentID(client); + + if(agentID == null){ + // 如果连接信息错误,发送异常信息,关闭socket + logger.info("连接信息错误:agentID, 连接关闭"); + client.disconnect(); + } + + String dbName = Commons.getDBName(client); + // 缓存连接 + ClientCache.saveClient(agentID, dbName, client); + + }); + + // rpcResponse + nameSpace.addEventListener("RPCResponse", byte[].class, ((client, data, ackRequest) -> { + RpcResponse rpcResponse = SERIALIZER.deserialize(data, RpcResponse.class); + logger.debug("RPCResponse: " + (rpcResponse.getStatus() ? "success" : "fail")); + + String agentID = Commons.getAgentID(client); + String dbName = Commons.getDBName(client); + ClientWrapper wrapper = ClientCache.getClientWrapper(agentID, dbName); + LockAndCondition lac = wrapper.getLockAndCondition(rpcResponse.getID()); + + ReentrantLock lock = lac.getLock(); + Condition condition = lac.getCondition(); + // When a response is received, it notifies that the FutureTask thread blocking on the LockAndCondition + // If the response contains data, take it out. + lock.lock(); + try { + Object resultData = rpcResponse.getResult(); + if(!rpcResponse.getStatus()){ + logger.error(resultData); + resultData = null; + } + if(resultData != null) { + lac.setResult(resultData); + } + condition.signal(); + }catch (Exception e){ + e.printStackTrace(); + }finally { + lock.unlock(); + } + wrapper.removeLockAndCondition(rpcResponse.getID()); + logger.debug("received response message, signaled condition"); + })); + + // 处理错误事件 + nameSpace.addEventListener("ErrorMessage", String.class, + ((client, data, ackRequest) -> logger.info("Error: " + data) + )); + } + + private void loadConfig() throws IOException { + logger.debug("加载配置"); + SocketConfig socketConfig = new SocketConfig(); + + com.corundumstudio.socketio.Configuration config = + new com.corundumstudio.socketio.Configuration(); + + InputStream in = this.getClass().getResourceAsStream("/socketIO.properties"); + Properties props = new Properties(); + InputStreamReader inputStreamReader = new InputStreamReader(in, StandardCharsets.UTF_8); + props.load(inputStreamReader); + + int bossCount = Integer.parseInt(props.getProperty("bossCount")); + String host = props.getProperty("host"); + int port = Integer.parseInt(props.getProperty("port")); + workCount = Integer.parseInt(props.getProperty("workCount")); + boolean allowCustomRequests = Boolean.parseBoolean(props.getProperty("allowCustomRequests")); + int upgradeTimeout = Integer.parseInt(props.getProperty("upgradeTimeout")); + int pingTimeout = Integer.parseInt(props.getProperty("pingTimeout")); + int pingInterval = Integer.parseInt(props.getProperty("pingInterval")); + + config.setSocketConfig(socketConfig); + config.setHostname(host); + config.setPort(port); + config.setBossThreads(bossCount); + config.setWorkerThreads(workCount); + config.setAllowCustomRequests(allowCustomRequests); + config.setUpgradeTimeout(upgradeTimeout); + config.setPingTimeout(pingTimeout); + config.setPingInterval(pingInterval); + config.setTransports(Transport.WEBSOCKET); + in.close(); + + threadPool = new ThreadPoolExecutor( + 0, workCount, + 60L, TimeUnit.SECONDS, + new LinkedBlockingQueue<>(), + new ThreadFactoryBuilder().setNameFormat("Thread-for-FutureTask-%d").build() + ); + server = new SocketIOServer(config); + + server.addConnectListener(client -> { + String agentID = Commons.getAgentID(client); + String dbName = Commons.getDBName(client); + if(agentID == null){ + // 如果连接信息错误,发送异常信息,关闭socket + logger.info("连接信息错误:agentID, 连接关闭"); + client.disconnect(); + } + // 缓存连接 + ClientCache.saveClient(agentID, dbName, client); + }); + + // 添加客户端连接监听器 + server.addDisconnectListener(client -> { + String agentID = Commons.getAgentID(client); + String dbName = Commons.getDBName(client); + + if(agentID == null){ + // 如果连接信息错误,发送异常信息,关闭socket + logger.info("agentID: 连接关闭"); + client.disconnect(); + } + + // 缓存连接 + ClientCache.deleteClient(agentID, dbName); + logger.info("agentID: " + agentID + "连接关闭"); + logger.info("agentID: " + agentID + "连接已删除"); + }); + + server.addEventListener("message", String.class, + ((client, data, ackRequest) -> logger.info("message: " + data) + )); + } +} + diff --git a/service/src/main/java/com/fanruan/cache/ClientCache.java b/service/src/main/java/com/fanruan/cache/ClientCache.java new file mode 100644 index 0000000..65da620 --- /dev/null +++ b/service/src/main/java/com/fanruan/cache/ClientCache.java @@ -0,0 +1,69 @@ +package com.fanruan.cache; + +import com.corundumstudio.socketio.SocketIOClient; +import com.fanruan.ServerStater; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + + +/** + * @author Yichen Dai + */ +public class ClientCache { + + private static final Map> AGENT_MAP = new ConcurrentHashMap<>(); + + /** + * 缓存代理通道 + * @param agentID 代理名称 + * @param client 代理通道 + */ + public static void saveClient(String agentID, String dbName, SocketIOClient client){ + Map nameSpaceMap = AGENT_MAP.get(agentID); + if(nameSpaceMap == null){ + nameSpaceMap = new ConcurrentHashMap<>(ServerStater.workCount * 10); + AGENT_MAP.put(agentID, nameSpaceMap); + } + ClientWrapper wrapper = nameSpaceMap.get(dbName); + if(wrapper == null){ + wrapper = new ClientWrapper(); + nameSpaceMap.put(dbName, wrapper); + } + wrapper.setClient(client); + } + + public static SocketIOClient getClient(String agentID, String dbName){ + Map map = AGENT_MAP.get(agentID); + if(map == null) { + return null; + } + ClientWrapper wrapper = map.get(dbName); + if(wrapper == null) { + return null; + } + return wrapper.getClient(); + } + + public static void deleteClient(String agentID, String dbName){ + Map map = AGENT_MAP.get(agentID); + if(map == null) { + return; + } + map.remove(dbName); + + } + + + public static ClientWrapper getClientWrapper(String agentID, String dbName){ + Map map = AGENT_MAP.get(agentID); + if(map == null) { + throw new RuntimeException("wrong agent ID"); + } + ClientWrapper wrapper = map.get(dbName); + if(wrapper == null) { + throw new RuntimeException("wrong dbName"); + } + return wrapper; + } +} \ No newline at end of file diff --git a/service/src/main/java/com/fanruan/cache/ClientWrapper.java b/service/src/main/java/com/fanruan/cache/ClientWrapper.java new file mode 100644 index 0000000..4e11599 --- /dev/null +++ b/service/src/main/java/com/fanruan/cache/ClientWrapper.java @@ -0,0 +1,45 @@ +package com.fanruan.cache; + +import com.corundumstudio.socketio.SocketIOClient; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author Yichen Dai + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ClientWrapper { + private SocketIOClient client; + private static Map lockMap = new ConcurrentHashMap<>(); + + + public SocketIOClient getClient(){ + if(client == null) { + throw new RuntimeException("no such client"); + } + return client; + } + + public LockAndCondition getLockAndCondition(String messageID){ + LockAndCondition lac = lockMap.get(messageID); + if(lac == null){ + ReentrantLock lock = new ReentrantLock(); + Condition condition = lock.newCondition(); + lac = new LockAndCondition(lock, condition); + lockMap.put(messageID, lac); + } + return lac; + } + + public void removeLockAndCondition(String messageID){ + lockMap.remove(messageID); + } +} diff --git a/service/src/main/java/com/fanruan/cache/LockAndCondition.java b/service/src/main/java/com/fanruan/cache/LockAndCondition.java new file mode 100644 index 0000000..0973c67 --- /dev/null +++ b/service/src/main/java/com/fanruan/cache/LockAndCondition.java @@ -0,0 +1,26 @@ +package com.fanruan.cache; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author Yichen Dai + */ +@Data +@NoArgsConstructor +@AllArgsConstructor +public class LockAndCondition{ + private ReentrantLock lock; + private Condition condition; + private Object result; + private String BindingID; + + LockAndCondition(ReentrantLock lock, Condition condition){ + this.lock = lock; + this.condition = condition; + } +} diff --git a/service/src/main/java/com/fanruan/jdbc/MyDataBaseMetaData.java b/service/src/main/java/com/fanruan/jdbc/MyDataBaseMetaData.java new file mode 100644 index 0000000..169945e --- /dev/null +++ b/service/src/main/java/com/fanruan/jdbc/MyDataBaseMetaData.java @@ -0,0 +1,891 @@ +package com.fanruan.jdbc; + + +import com.fanruan.jdbc.driver.MyDriver; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.RowIdLifetime; +import java.sql.SQLException; + +public class MyDataBaseMetaData implements java.sql.DatabaseMetaData{ + @Override + public boolean allProceduresAreCallable() throws SQLException { + return false; + } + + @Override + public boolean allTablesAreSelectable() throws SQLException { + return false; + } + + @Override + public String getURL() throws SQLException { + return null; + } + + @Override + public String getUserName() throws SQLException { + return null; + } + + @Override + public boolean isReadOnly() throws SQLException { + return false; + } + + @Override + public boolean nullsAreSortedHigh() throws SQLException { + return false; + } + + @Override + public boolean nullsAreSortedLow() throws SQLException { + return false; + } + + @Override + public boolean nullsAreSortedAtStart() throws SQLException { + return false; + } + + @Override + public boolean nullsAreSortedAtEnd() throws SQLException { + return false; + } + + @Override + public String getDatabaseProductName() throws SQLException { + return "agent server 1.0.0"; + } + + @Override + public String getDatabaseProductVersion() throws SQLException { + return "1.0.0"; + } + + @Override + public String getDriverName() throws SQLException { + return "myDriver for Agent"; + } + + @Override + public String getDriverVersion() throws SQLException { + return "1.0.0"; + } + + @Override + public int getDriverMajorVersion() { + return MyDriver.DRIVER_VERSION_MAJOR; + } + + @Override + public int getDriverMinorVersion() { + return MyDriver.DRIVER_VERSION_MINOR; + } + + @Override + public boolean usesLocalFiles() throws SQLException { + return false; + } + + @Override + public boolean usesLocalFilePerTable() throws SQLException { + return false; + } + + @Override + public boolean supportsMixedCaseIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesUpperCaseIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesLowerCaseIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesMixedCaseIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean supportsMixedCaseQuotedIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesUpperCaseQuotedIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesLowerCaseQuotedIdentifiers() throws SQLException { + return false; + } + + @Override + public boolean storesMixedCaseQuotedIdentifiers() throws SQLException { + return false; + } + + @Override + public String getIdentifierQuoteString() throws SQLException { + return null; + } + + @Override + public String getSQLKeywords() throws SQLException { + return null; + } + + @Override + public String getNumericFunctions() throws SQLException { + return null; + } + + @Override + public String getStringFunctions() throws SQLException { + return null; + } + + @Override + public String getSystemFunctions() throws SQLException { + return null; + } + + @Override + public String getTimeDateFunctions() throws SQLException { + return null; + } + + @Override + public String getSearchStringEscape() throws SQLException { + return null; + } + + @Override + public String getExtraNameCharacters() throws SQLException { + return null; + } + + @Override + public boolean supportsAlterTableWithAddColumn() throws SQLException { + return false; + } + + @Override + public boolean supportsAlterTableWithDropColumn() throws SQLException { + return false; + } + + @Override + public boolean supportsColumnAliasing() throws SQLException { + return false; + } + + @Override + public boolean nullPlusNonNullIsNull() throws SQLException { + return false; + } + + @Override + public boolean supportsConvert() throws SQLException { + return false; + } + + @Override + public boolean supportsConvert(int fromType, int toType) throws SQLException { + return false; + } + + @Override + public boolean supportsTableCorrelationNames() throws SQLException { + return false; + } + + @Override + public boolean supportsDifferentTableCorrelationNames() throws SQLException { + return false; + } + + @Override + public boolean supportsExpressionsInOrderBy() throws SQLException { + return false; + } + + @Override + public boolean supportsOrderByUnrelated() throws SQLException { + return false; + } + + @Override + public boolean supportsGroupBy() throws SQLException { + return false; + } + + @Override + public boolean supportsGroupByUnrelated() throws SQLException { + return false; + } + + @Override + public boolean supportsGroupByBeyondSelect() throws SQLException { + return false; + } + + @Override + public boolean supportsLikeEscapeClause() throws SQLException { + return false; + } + + @Override + public boolean supportsMultipleResultSets() throws SQLException { + return false; + } + + @Override + public boolean supportsMultipleTransactions() throws SQLException { + return false; + } + + @Override + public boolean supportsNonNullableColumns() throws SQLException { + return false; + } + + @Override + public boolean supportsMinimumSQLGrammar() throws SQLException { + return false; + } + + @Override + public boolean supportsCoreSQLGrammar() throws SQLException { + return false; + } + + @Override + public boolean supportsExtendedSQLGrammar() throws SQLException { + return false; + } + + @Override + public boolean supportsANSI92EntryLevelSQL() throws SQLException { + return false; + } + + @Override + public boolean supportsANSI92IntermediateSQL() throws SQLException { + return false; + } + + @Override + public boolean supportsANSI92FullSQL() throws SQLException { + return false; + } + + @Override + public boolean supportsIntegrityEnhancementFacility() throws SQLException { + return false; + } + + @Override + public boolean supportsOuterJoins() throws SQLException { + return false; + } + + @Override + public boolean supportsFullOuterJoins() throws SQLException { + return false; + } + + @Override + public boolean supportsLimitedOuterJoins() throws SQLException { + return false; + } + + @Override + public String getSchemaTerm() throws SQLException { + return null; + } + + @Override + public String getProcedureTerm() throws SQLException { + return null; + } + + @Override + public String getCatalogTerm() throws SQLException { + return null; + } + + @Override + public boolean isCatalogAtStart() throws SQLException { + return false; + } + + @Override + public String getCatalogSeparator() throws SQLException { + return null; + } + + @Override + public boolean supportsSchemasInDataManipulation() throws SQLException { + return false; + } + + @Override + public boolean supportsSchemasInProcedureCalls() throws SQLException { + return false; + } + + @Override + public boolean supportsSchemasInTableDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsSchemasInIndexDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsSchemasInPrivilegeDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsCatalogsInDataManipulation() throws SQLException { + return false; + } + + @Override + public boolean supportsCatalogsInProcedureCalls() throws SQLException { + return false; + } + + @Override + public boolean supportsCatalogsInTableDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsCatalogsInIndexDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsCatalogsInPrivilegeDefinitions() throws SQLException { + return false; + } + + @Override + public boolean supportsPositionedDelete() throws SQLException { + return false; + } + + @Override + public boolean supportsPositionedUpdate() throws SQLException { + return false; + } + + @Override + public boolean supportsSelectForUpdate() throws SQLException { + return false; + } + + @Override + public boolean supportsStoredProcedures() throws SQLException { + return false; + } + + @Override + public boolean supportsSubqueriesInComparisons() throws SQLException { + return false; + } + + @Override + public boolean supportsSubqueriesInExists() throws SQLException { + return false; + } + + @Override + public boolean supportsSubqueriesInIns() throws SQLException { + return false; + } + + @Override + public boolean supportsSubqueriesInQuantifieds() throws SQLException { + return false; + } + + @Override + public boolean supportsCorrelatedSubqueries() throws SQLException { + return false; + } + + @Override + public boolean supportsUnion() throws SQLException { + return false; + } + + @Override + public boolean supportsUnionAll() throws SQLException { + return false; + } + + @Override + public boolean supportsOpenCursorsAcrossCommit() throws SQLException { + return false; + } + + @Override + public boolean supportsOpenCursorsAcrossRollback() throws SQLException { + return false; + } + + @Override + public boolean supportsOpenStatementsAcrossCommit() throws SQLException { + return false; + } + + @Override + public boolean supportsOpenStatementsAcrossRollback() throws SQLException { + return false; + } + + @Override + public int getMaxBinaryLiteralLength() throws SQLException { + return 0; + } + + @Override + public int getMaxCharLiteralLength() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnsInGroupBy() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnsInIndex() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnsInOrderBy() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnsInSelect() throws SQLException { + return 0; + } + + @Override + public int getMaxColumnsInTable() throws SQLException { + return 0; + } + + @Override + public int getMaxConnections() throws SQLException { + return 0; + } + + @Override + public int getMaxCursorNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxIndexLength() throws SQLException { + return 0; + } + + @Override + public int getMaxSchemaNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxProcedureNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxCatalogNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxRowSize() throws SQLException { + return 0; + } + + @Override + public boolean doesMaxRowSizeIncludeBlobs() throws SQLException { + return false; + } + + @Override + public int getMaxStatementLength() throws SQLException { + return 0; + } + + @Override + public int getMaxStatements() throws SQLException { + return 0; + } + + @Override + public int getMaxTableNameLength() throws SQLException { + return 0; + } + + @Override + public int getMaxTablesInSelect() throws SQLException { + return 0; + } + + @Override + public int getMaxUserNameLength() throws SQLException { + return 0; + } + + @Override + public int getDefaultTransactionIsolation() throws SQLException { + return 0; + } + + @Override + public boolean supportsTransactions() throws SQLException { + return false; + } + + @Override + public boolean supportsTransactionIsolationLevel(int level) throws SQLException { + return false; + } + + @Override + public boolean supportsDataDefinitionAndDataManipulationTransactions() throws SQLException { + return false; + } + + @Override + public boolean supportsDataManipulationTransactionsOnly() throws SQLException { + return false; + } + + @Override + public boolean dataDefinitionCausesTransactionCommit() throws SQLException { + return false; + } + + @Override + public boolean dataDefinitionIgnoredInTransactions() throws SQLException { + return false; + } + + @Override + public ResultSet getProcedures(String catalog, String schemaPattern, String procedureNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getProcedureColumns(String catalog, String schemaPattern, String procedureNamePattern, String columnNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getTables(String catalog, String schemaPattern, String tableNamePattern, String[] types) throws SQLException { + return null; + } + + @Override + public ResultSet getSchemas() throws SQLException { + return null; + } + + @Override + public ResultSet getCatalogs() throws SQLException { + return null; + } + + @Override + public ResultSet getTableTypes() throws SQLException { + return null; + } + + @Override + public ResultSet getColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getColumnPrivileges(String catalog, String schema, String table, String columnNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getTablePrivileges(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getBestRowIdentifier(String catalog, String schema, String table, int scope, boolean nullable) throws SQLException { + return null; + } + + @Override + public ResultSet getVersionColumns(String catalog, String schema, String table) throws SQLException { + return null; + } + + @Override + public ResultSet getPrimaryKeys(String catalog, String schema, String table) throws SQLException { + return null; + } + + @Override + public ResultSet getImportedKeys(String catalog, String schema, String table) throws SQLException { + return null; + } + + @Override + public ResultSet getExportedKeys(String catalog, String schema, String table) throws SQLException { + return null; + } + + @Override + public ResultSet getCrossReference(String parentCatalog, String parentSchema, String parentTable, String foreignCatalog, String foreignSchema, String foreignTable) throws SQLException { + return null; + } + + @Override + public ResultSet getTypeInfo() throws SQLException { + return null; + } + + @Override + public ResultSet getIndexInfo(String catalog, String schema, String table, boolean unique, boolean approximate) throws SQLException { + return null; + } + + @Override + public boolean supportsResultSetType(int type) throws SQLException { + return false; + } + + @Override + public boolean supportsResultSetConcurrency(int type, int concurrency) throws SQLException { + return false; + } + + @Override + public boolean ownUpdatesAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean ownDeletesAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean ownInsertsAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean othersUpdatesAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean othersDeletesAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean othersInsertsAreVisible(int type) throws SQLException { + return false; + } + + @Override + public boolean updatesAreDetected(int type) throws SQLException { + return false; + } + + @Override + public boolean deletesAreDetected(int type) throws SQLException { + return false; + } + + @Override + public boolean insertsAreDetected(int type) throws SQLException { + return false; + } + + @Override + public boolean supportsBatchUpdates() throws SQLException { + return false; + } + + @Override + public ResultSet getUDTs(String catalog, String schemaPattern, String typeNamePattern, int[] types) throws SQLException { + return null; + } + + @Override + public Connection getConnection() throws SQLException { + return null; + } + + @Override + public boolean supportsSavepoints() throws SQLException { + return false; + } + + @Override + public boolean supportsNamedParameters() throws SQLException { + return false; + } + + @Override + public boolean supportsMultipleOpenResults() throws SQLException { + return false; + } + + @Override + public boolean supportsGetGeneratedKeys() throws SQLException { + return false; + } + + @Override + public ResultSet getSuperTypes(String catalog, String schemaPattern, String typeNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getSuperTables(String catalog, String schemaPattern, String tableNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getAttributes(String catalog, String schemaPattern, String typeNamePattern, String attributeNamePattern) throws SQLException { + return null; + } + + @Override + public boolean supportsResultSetHoldability(int holdability) throws SQLException { + return false; + } + + @Override + public int getResultSetHoldability() throws SQLException { + return 0; + } + + @Override + public int getDatabaseMajorVersion() throws SQLException { + return 0; + } + + @Override + public int getDatabaseMinorVersion() throws SQLException { + return 0; + } + + @Override + public int getJDBCMajorVersion() throws SQLException { + return 0; + } + + @Override + public int getJDBCMinorVersion() throws SQLException { + return 0; + } + + @Override + public int getSQLStateType() throws SQLException { + return 0; + } + + @Override + public boolean locatorsUpdateCopy() throws SQLException { + return false; + } + + @Override + public boolean supportsStatementPooling() throws SQLException { + return false; + } + + @Override + public RowIdLifetime getRowIdLifetime() throws SQLException { + return null; + } + + @Override + public ResultSet getSchemas(String catalog, String schemaPattern) throws SQLException { + return null; + } + + @Override + public boolean supportsStoredFunctionsUsingCallSyntax() throws SQLException { + return false; + } + + @Override + public boolean autoCommitFailureClosesAllResultSets() throws SQLException { + return false; + } + + @Override + public ResultSet getClientInfoProperties() throws SQLException { + return null; + } + + @Override + public ResultSet getFunctions(String catalog, String schemaPattern, String functionNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getFunctionColumns(String catalog, String schemaPattern, String functionNamePattern, String columnNamePattern) throws SQLException { + return null; + } + + @Override + public ResultSet getPseudoColumns(String catalog, String schemaPattern, String tableNamePattern, String columnNamePattern) throws SQLException { + return null; + } + + @Override + public boolean generatedKeyAlwaysReturned() throws SQLException { + return false; + } + + @Override + public T unwrap(Class iface) throws SQLException { + return null; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return false; + } +} diff --git a/service/src/main/java/com/fanruan/jdbc/connection/MyConnection.java b/service/src/main/java/com/fanruan/jdbc/connection/MyConnection.java new file mode 100644 index 0000000..b467558 --- /dev/null +++ b/service/src/main/java/com/fanruan/jdbc/connection/MyConnection.java @@ -0,0 +1,318 @@ +package com.fanruan.jdbc.connection; + + +import com.corundumstudio.socketio.SocketIOClient; +import com.fanruan.cache.ClientCache; +import com.fanruan.jdbc.MyDataBaseMetaData; +import com.fanruan.jdbc.statement.MyPreparedStatement; +import com.fanruan.jdbc.statement.MyStatement; +import com.fanruan.proxy.ProxyFactory; + +import java.sql.*; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.Executor; + +/** + * @author Yichen Dai + */ +public class MyConnection implements Connection { + private String ID; + + private Properties info; + private boolean autoCommit = true; + SocketIOClient client; + + public MyConnection(){ + } + + public void setID(String ID){ + this.ID = ID; + } + + public String getID(){ + return this.ID; + } + + public void setInfo(Properties info){ + this.info = info; + client = ClientCache.getClient(info.getProperty("agentID"), info.getProperty("agentDBName")); + } + + + @Override + public Statement createStatement(){ + MyStatement st = (MyStatement) ProxyFactory.getProxy(MyStatement.class, info); + st.setInfo(info); + return st; + } + + @Override + public PreparedStatement prepareStatement(String sql) throws SQLException { + MyPreparedStatement pst = (MyPreparedStatement) ProxyFactory.getProxy(MyPreparedStatement.class, info); + // 将需要准备的sql 加入 properties 中, 将用以标识生成的 ResultSet + info.setProperty("PreparedSQL", sql); + pst.setInfo(info); + return pst; + } + + @Override + public CallableStatement prepareCall(String sql) throws SQLException { + return null; + } + + @Override + public String nativeSQL(String sql) throws SQLException { + return sql; + } + + @Override + public void setAutoCommit(boolean autoCommit) throws SQLException { + this.autoCommit = autoCommit; + } + + @Override + public boolean getAutoCommit() throws SQLException { + return autoCommit; + } + + @Override + public void commit() throws SQLException { + + } + + @Override + public void rollback() throws SQLException { + + } + + @Override + public void close() throws SQLException { + client.disconnect(); + } + + @Override + public boolean isClosed() throws SQLException { + return client.isChannelOpen(); + } + + @Override + public DatabaseMetaData getMetaData() throws SQLException { + return new MyDataBaseMetaData(); + } + + @Override + public void setReadOnly(boolean readOnly) throws SQLException { + + } + + @Override + public boolean isReadOnly() throws SQLException { + return false; + } + + @Override + public void setCatalog(String catalog) throws SQLException { + + } + + @Override + public String getCatalog() throws SQLException { + return null; + } + + @Override + public void setTransactionIsolation(int level) throws SQLException { + + } + + @Override + public int getTransactionIsolation() throws SQLException { + return 0; + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return null; + } + + @Override + public void clearWarnings() throws SQLException { + + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return null; + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency) throws SQLException { + return null; + } + + @Override + public Map> getTypeMap() throws SQLException { + return null; + } + + @Override + public void setTypeMap(Map> map) throws SQLException { + + } + + @Override + public void setHoldability(int holdability) throws SQLException { + + } + + @Override + public int getHoldability() throws SQLException { + return 0; + } + + @Override + public Savepoint setSavepoint() throws SQLException { + return null; + } + + @Override + public Savepoint setSavepoint(String name) throws SQLException { + return null; + } + + @Override + public void rollback(Savepoint savepoint) throws SQLException { + + } + + @Override + public void releaseSavepoint(Savepoint savepoint) throws SQLException { + + } + + @Override + public Statement createStatement(int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return null; + } + + @Override + public CallableStatement prepareCall(String sql, int resultSetType, int resultSetConcurrency, int resultSetHoldability) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int autoGeneratedKeys) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, int[] columnIndexes) throws SQLException { + return null; + } + + @Override + public PreparedStatement prepareStatement(String sql, String[] columnNames) throws SQLException { + return null; + } + + @Override + public Clob createClob() throws SQLException { + return null; + } + + @Override + public Blob createBlob() throws SQLException { + return null; + } + + @Override + public NClob createNClob() throws SQLException { + return null; + } + + @Override + public SQLXML createSQLXML() throws SQLException { + return null; + } + + @Override + public boolean isValid(int timeout) throws SQLException { + return false; + } + + @Override + public void setClientInfo(String name, String value) throws SQLClientInfoException { + + } + + @Override + public void setClientInfo(Properties properties) throws SQLClientInfoException { + + } + + @Override + public String getClientInfo(String name) throws SQLException { + return null; + } + + @Override + public Properties getClientInfo() throws SQLException { + return null; + } + + @Override + public Array createArrayOf(String typeName, Object[] elements) throws SQLException { + return null; + } + + @Override + public Struct createStruct(String typeName, Object[] attributes) throws SQLException { + return null; + } + + @Override + public void setSchema(String schema) throws SQLException { + + } + + @Override + public String getSchema() throws SQLException { + return null; + } + + @Override + public void abort(Executor executor) throws SQLException { + + } + + @Override + public void setNetworkTimeout(Executor executor, int milliseconds) throws SQLException { + + } + + @Override + public int getNetworkTimeout() throws SQLException { + return 0; + } + + @Override + public T unwrap(Class iface) throws SQLException { + return null; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return false; + } +} diff --git a/service/src/main/java/com/fanruan/jdbc/driver/MyDriver.java b/service/src/main/java/com/fanruan/jdbc/driver/MyDriver.java new file mode 100644 index 0000000..af4d9c3 --- /dev/null +++ b/service/src/main/java/com/fanruan/jdbc/driver/MyDriver.java @@ -0,0 +1,84 @@ +package com.fanruan.jdbc.driver; + +import com.fanruan.jdbc.connection.MyConnection; +import com.fanruan.proxy.ProxyFactory; + +import java.sql.*; +import java.util.Properties; +import java.util.logging.Logger; + + +/** + * @author Yichen Dai + */ +public class MyDriver implements Driver { + + static public final int DRIVER_VERSION_MAJOR = 1; + static public final int DRIVER_VERSION_MINOR = 1; + private String ID; + + //依靠静态函数块注册驱动 + static{ + try { + DriverManager.registerDriver((MyDriver) ProxyFactory.getProxy(MyDriver.class, null)); + } catch (Exception e) { + throw new RuntimeException("Can't register driver"); + } + } + + + /** + * These corresponding code is to make the format correct, because the getID() + * will be called, even if the filed is never not null. + * @return ID + */ + public String getID(){ + return this.ID; + } + + public void setID(String ID){ + this.ID = ID; + } + + @Override + public Connection connect(String url, Properties info){ + String dbName = info.getProperty("agentDBName"); + if(dbName == null){ + dbName = url.split(":")[1]; + info.setProperty("agentDBName", dbName); + } + MyConnection myConn = (MyConnection) ProxyFactory.getProxy(MyConnection.class, info); + myConn.setInfo(info); + return myConn; + } + + @Override + public boolean acceptsURL(String url){ + return true; + } + + @Override + public DriverPropertyInfo[] getPropertyInfo(String url, Properties info){ + return new DriverPropertyInfo[0]; + } + + @Override + public int getMajorVersion() { + return DRIVER_VERSION_MAJOR; + } + + @Override + public int getMinorVersion() { + return DRIVER_VERSION_MINOR; + } + + @Override + public boolean jdbcCompliant() { + return false; + } + + @Override + public Logger getParentLogger() throws SQLFeatureNotSupportedException { + return null; + } +} diff --git a/service/src/main/java/com/fanruan/jdbc/resultset/MyResultSet.java b/service/src/main/java/com/fanruan/jdbc/resultset/MyResultSet.java new file mode 100644 index 0000000..50efb3c --- /dev/null +++ b/service/src/main/java/com/fanruan/jdbc/resultset/MyResultSet.java @@ -0,0 +1,990 @@ +package com.fanruan.jdbc.resultset; + + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.Calendar; +import java.util.Map; + + +/** + * @author Yichen Dai + */ +public class MyResultSet implements ResultSet { + + private String sql; + private String ID; + + public String getID(){ + return this.ID; + } + + public void setID(String ID){ + this.ID = ID; + } + + public void setSql(String sql){ + this.sql = sql; + } + + public String getSql(){ + return this.sql; + } + + @Override + public boolean next() throws SQLException { + return false; + } + + @Override + public void close() throws SQLException { + } + + @Override + public boolean wasNull() throws SQLException { + return false; + } + + @Override + public String getString(int columnIndex) throws SQLException { + return null; + } + + @Override + public boolean getBoolean(int columnIndex) throws SQLException { + return false; + } + + @Override + public byte getByte(int columnIndex) throws SQLException { + return 0; + } + + @Override + public short getShort(int columnIndex) throws SQLException { + return 0; + } + + @Override + public int getInt(int columnIndex) throws SQLException { + return 0; + } + + @Override + public long getLong(int columnIndex) throws SQLException { + return 0; + } + + @Override + public float getFloat(int columnIndex) throws SQLException { + return 0; + } + + @Override + public double getDouble(int columnIndex) throws SQLException { + return 0; + } + + @Override + public BigDecimal getBigDecimal(int columnIndex, int scale) throws SQLException { + return null; + } + + @Override + public byte[] getBytes(int columnIndex) throws SQLException { + return new byte[0]; + } + + @Override + public Date getDate(int columnIndex) throws SQLException { + return null; + } + + @Override + public Time getTime(int columnIndex) throws SQLException { + return null; + } + + @Override + public Timestamp getTimestamp(int columnIndex) throws SQLException { + return null; + } + + @Override + public InputStream getAsciiStream(int columnIndex) throws SQLException { + return null; + } + + @Override + public InputStream getUnicodeStream(int columnIndex) throws SQLException { + return null; + } + + @Override + public InputStream getBinaryStream(int columnIndex) throws SQLException { + return null; + } + + @Override + public String getString(String columnLabel) throws SQLException { + return null; + } + + @Override + public boolean getBoolean(String columnLabel) throws SQLException { + return false; + } + + @Override + public byte getByte(String columnLabel) throws SQLException { + return 0; + } + + @Override + public short getShort(String columnLabel) throws SQLException { + return 0; + } + + @Override + public int getInt(String columnLabel) throws SQLException { + return 0; + } + + @Override + public long getLong(String columnLabel) throws SQLException { + return 0; + } + + @Override + public float getFloat(String columnLabel) throws SQLException { + return 0; + } + + @Override + public double getDouble(String columnLabel) throws SQLException { + return 0; + } + + @Override + public BigDecimal getBigDecimal(String columnLabel, int scale) throws SQLException { + return null; + } + + @Override + public byte[] getBytes(String columnLabel) throws SQLException { + return new byte[0]; + } + + @Override + public Date getDate(String columnLabel) throws SQLException { + return null; + } + + @Override + public Time getTime(String columnLabel) throws SQLException { + return null; + } + + @Override + public Timestamp getTimestamp(String columnLabel) throws SQLException { + return null; + } + + @Override + public InputStream getAsciiStream(String columnLabel) throws SQLException { + return null; + } + + @Override + public InputStream getUnicodeStream(String columnLabel) throws SQLException { + return null; + } + + @Override + public InputStream getBinaryStream(String columnLabel) throws SQLException { + return null; + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return null; + } + + @Override + public void clearWarnings() throws SQLException { + + } + + @Override + public String getCursorName() throws SQLException { + return null; + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + return null; + } + + @Override + public Object getObject(int columnIndex) throws SQLException { + return null; + } + + @Override + public Object getObject(String columnLabel) throws SQLException { + return null; + } + + @Override + public int findColumn(String columnLabel) throws SQLException { + return 0; + } + + @Override + public Reader getCharacterStream(int columnIndex) throws SQLException { + return null; + } + + @Override + public Reader getCharacterStream(String columnLabel) throws SQLException { + return null; + } + + @Override + public BigDecimal getBigDecimal(int columnIndex) throws SQLException { + return null; + } + + @Override + public BigDecimal getBigDecimal(String columnLabel) throws SQLException { + return null; + } + + @Override + public boolean isBeforeFirst() throws SQLException { + return false; + } + + @Override + public boolean isAfterLast() throws SQLException { + return false; + } + + @Override + public boolean isFirst() throws SQLException { + return false; + } + + @Override + public boolean isLast() throws SQLException { + return false; + } + + @Override + public void beforeFirst() throws SQLException { + + } + + @Override + public void afterLast() throws SQLException { + + } + + @Override + public boolean first() throws SQLException { + return false; + } + + @Override + public boolean last() throws SQLException { + return false; + } + + @Override + public int getRow() throws SQLException { + return 0; + } + + @Override + public boolean absolute(int row) throws SQLException { + return false; + } + + @Override + public boolean relative(int rows) throws SQLException { + return false; + } + + @Override + public boolean previous() throws SQLException { + return false; + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + + } + + @Override + public int getFetchDirection() throws SQLException { + return 0; + } + + @Override + public void setFetchSize(int rows) throws SQLException { + + } + + @Override + public int getFetchSize() throws SQLException { + return 0; + } + + @Override + public int getType() throws SQLException { + return 0; + } + + @Override + public int getConcurrency() throws SQLException { + return 0; + } + + @Override + public boolean rowUpdated() throws SQLException { + return false; + } + + @Override + public boolean rowInserted() throws SQLException { + return false; + } + + @Override + public boolean rowDeleted() throws SQLException { + return false; + } + + @Override + public void updateNull(int columnIndex) throws SQLException { + + } + + @Override + public void updateBoolean(int columnIndex, boolean x) throws SQLException { + + } + + @Override + public void updateByte(int columnIndex, byte x) throws SQLException { + + } + + @Override + public void updateShort(int columnIndex, short x) throws SQLException { + + } + + @Override + public void updateInt(int columnIndex, int x) throws SQLException { + + } + + @Override + public void updateLong(int columnIndex, long x) throws SQLException { + + } + + @Override + public void updateFloat(int columnIndex, float x) throws SQLException { + + } + + @Override + public void updateDouble(int columnIndex, double x) throws SQLException { + + } + + @Override + public void updateBigDecimal(int columnIndex, BigDecimal x) throws SQLException { + + } + + @Override + public void updateString(int columnIndex, String x) throws SQLException { + + } + + @Override + public void updateBytes(int columnIndex, byte[] x) throws SQLException { + + } + + @Override + public void updateDate(int columnIndex, Date x) throws SQLException { + + } + + @Override + public void updateTime(int columnIndex, Time x) throws SQLException { + + } + + @Override + public void updateTimestamp(int columnIndex, Timestamp x) throws SQLException { + + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, int length) throws SQLException { + + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, int length) throws SQLException { + + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, int length) throws SQLException { + + } + + @Override + public void updateObject(int columnIndex, Object x, int scaleOrLength) throws SQLException { + + } + + @Override + public void updateObject(int columnIndex, Object x) throws SQLException { + + } + + @Override + public void updateNull(String columnLabel) throws SQLException { + + } + + @Override + public void updateBoolean(String columnLabel, boolean x) throws SQLException { + + } + + @Override + public void updateByte(String columnLabel, byte x) throws SQLException { + + } + + @Override + public void updateShort(String columnLabel, short x) throws SQLException { + + } + + @Override + public void updateInt(String columnLabel, int x) throws SQLException { + + } + + @Override + public void updateLong(String columnLabel, long x) throws SQLException { + + } + + @Override + public void updateFloat(String columnLabel, float x) throws SQLException { + + } + + @Override + public void updateDouble(String columnLabel, double x) throws SQLException { + + } + + @Override + public void updateBigDecimal(String columnLabel, BigDecimal x) throws SQLException { + + } + + @Override + public void updateString(String columnLabel, String x) throws SQLException { + + } + + @Override + public void updateBytes(String columnLabel, byte[] x) throws SQLException { + + } + + @Override + public void updateDate(String columnLabel, Date x) throws SQLException { + + } + + @Override + public void updateTime(String columnLabel, Time x) throws SQLException { + + } + + @Override + public void updateTimestamp(String columnLabel, Timestamp x) throws SQLException { + + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, int length) throws SQLException { + + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, int length) throws SQLException { + + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, int length) throws SQLException { + + } + + @Override + public void updateObject(String columnLabel, Object x, int scaleOrLength) throws SQLException { + + } + + @Override + public void updateObject(String columnLabel, Object x) throws SQLException { + + } + + @Override + public void insertRow() throws SQLException { + + } + + @Override + public void updateRow() throws SQLException { + + } + + @Override + public void deleteRow() throws SQLException { + + } + + @Override + public void refreshRow() throws SQLException { + + } + + @Override + public void cancelRowUpdates() throws SQLException { + + } + + @Override + public void moveToInsertRow() throws SQLException { + + } + + @Override + public void moveToCurrentRow() throws SQLException { + + } + + @Override + public Statement getStatement() throws SQLException { + return null; + } + + @Override + public Object getObject(int columnIndex, Map> map) throws SQLException { + return null; + } + + @Override + public Ref getRef(int columnIndex) throws SQLException { + return null; + } + + @Override + public Blob getBlob(int columnIndex) throws SQLException { + return null; + } + + @Override + public Clob getClob(int columnIndex) throws SQLException { + return null; + } + + @Override + public Array getArray(int columnIndex) throws SQLException { + return null; + } + + @Override + public Object getObject(String columnLabel, Map> map) throws SQLException { + return null; + } + + @Override + public Ref getRef(String columnLabel) throws SQLException { + return null; + } + + @Override + public Blob getBlob(String columnLabel) throws SQLException { + return null; + } + + @Override + public Clob getClob(String columnLabel) throws SQLException { + return null; + } + + @Override + public Array getArray(String columnLabel) throws SQLException { + return null; + } + + @Override + public Date getDate(int columnIndex, Calendar cal) throws SQLException { + return null; + } + + @Override + public Date getDate(String columnLabel, Calendar cal) throws SQLException { + return null; + } + + @Override + public Time getTime(int columnIndex, Calendar cal) throws SQLException { + return null; + } + + @Override + public Time getTime(String columnLabel, Calendar cal) throws SQLException { + return null; + } + + @Override + public Timestamp getTimestamp(int columnIndex, Calendar cal) throws SQLException { + return null; + } + + @Override + public Timestamp getTimestamp(String columnLabel, Calendar cal) throws SQLException { + return null; + } + + @Override + public URL getURL(int columnIndex) throws SQLException { + return null; + } + + @Override + public URL getURL(String columnLabel) throws SQLException { + return null; + } + + @Override + public void updateRef(int columnIndex, Ref x) throws SQLException { + + } + + @Override + public void updateRef(String columnLabel, Ref x) throws SQLException { + + } + + @Override + public void updateBlob(int columnIndex, Blob x) throws SQLException { + + } + + @Override + public void updateBlob(String columnLabel, Blob x) throws SQLException { + + } + + @Override + public void updateClob(int columnIndex, Clob x) throws SQLException { + + } + + @Override + public void updateClob(String columnLabel, Clob x) throws SQLException { + + } + + @Override + public void updateArray(int columnIndex, Array x) throws SQLException { + + } + + @Override + public void updateArray(String columnLabel, Array x) throws SQLException { + + } + + @Override + public RowId getRowId(int columnIndex) throws SQLException { + return null; + } + + @Override + public RowId getRowId(String columnLabel) throws SQLException { + return null; + } + + @Override + public void updateRowId(int columnIndex, RowId x) throws SQLException { + + } + + @Override + public void updateRowId(String columnLabel, RowId x) throws SQLException { + + } + + @Override + public int getHoldability() throws SQLException { + return 0; + } + + @Override + public boolean isClosed() throws SQLException { + return false; + } + + @Override + public void updateNString(int columnIndex, String nString) throws SQLException { + + } + + @Override + public void updateNString(String columnLabel, String nString) throws SQLException { + + } + + @Override + public void updateNClob(int columnIndex, NClob nClob) throws SQLException { + + } + + @Override + public void updateNClob(String columnLabel, NClob nClob) throws SQLException { + + } + + @Override + public NClob getNClob(int columnIndex) throws SQLException { + return null; + } + + @Override + public NClob getNClob(String columnLabel) throws SQLException { + return null; + } + + @Override + public SQLXML getSQLXML(int columnIndex) throws SQLException { + return null; + } + + @Override + public SQLXML getSQLXML(String columnLabel) throws SQLException { + return null; + } + + @Override + public void updateSQLXML(int columnIndex, SQLXML xmlObject) throws SQLException { + + } + + @Override + public void updateSQLXML(String columnLabel, SQLXML xmlObject) throws SQLException { + + } + + @Override + public String getNString(int columnIndex) throws SQLException { + return null; + } + + @Override + public String getNString(String columnLabel) throws SQLException { + return null; + } + + @Override + public Reader getNCharacterStream(int columnIndex) throws SQLException { + return null; + } + + @Override + public Reader getNCharacterStream(String columnLabel) throws SQLException { + return null; + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x, long length) throws SQLException { + + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x, long length) throws SQLException { + + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x, long length) throws SQLException { + + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x, long length) throws SQLException { + + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x, long length) throws SQLException { + + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader, long length) throws SQLException { + + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream, long length) throws SQLException { + + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream, long length) throws SQLException { + + } + + @Override + public void updateClob(int columnIndex, Reader reader, long length) throws SQLException { + + } + + @Override + public void updateClob(String columnLabel, Reader reader, long length) throws SQLException { + + } + + @Override + public void updateNClob(int columnIndex, Reader reader, long length) throws SQLException { + + } + + @Override + public void updateNClob(String columnLabel, Reader reader, long length) throws SQLException { + + } + + @Override + public void updateNCharacterStream(int columnIndex, Reader x) throws SQLException { + + } + + @Override + public void updateNCharacterStream(String columnLabel, Reader reader) throws SQLException { + + } + + @Override + public void updateAsciiStream(int columnIndex, InputStream x) throws SQLException { + + } + + @Override + public void updateBinaryStream(int columnIndex, InputStream x) throws SQLException { + + } + + @Override + public void updateCharacterStream(int columnIndex, Reader x) throws SQLException { + + } + + @Override + public void updateAsciiStream(String columnLabel, InputStream x) throws SQLException { + + } + + @Override + public void updateBinaryStream(String columnLabel, InputStream x) throws SQLException { + + } + + @Override + public void updateCharacterStream(String columnLabel, Reader reader) throws SQLException { + + } + + @Override + public void updateBlob(int columnIndex, InputStream inputStream) throws SQLException { + + } + + @Override + public void updateBlob(String columnLabel, InputStream inputStream) throws SQLException { + + } + + @Override + public void updateClob(int columnIndex, Reader reader) throws SQLException { + + } + + @Override + public void updateClob(String columnLabel, Reader reader) throws SQLException { + + } + + @Override + public void updateNClob(int columnIndex, Reader reader) throws SQLException { + + } + + @Override + public void updateNClob(String columnLabel, Reader reader) throws SQLException { + + } + + @Override + public T getObject(int columnIndex, Class type) throws SQLException { + return null; + } + + @Override + public T getObject(String columnLabel, Class type) throws SQLException { + return null; + } + + @Override + public T unwrap(Class iface) throws SQLException { + return null; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return false; + } +} diff --git a/service/src/main/java/com/fanruan/jdbc/statement/MyPreparedStatement.java b/service/src/main/java/com/fanruan/jdbc/statement/MyPreparedStatement.java new file mode 100644 index 0000000..d732fd1 --- /dev/null +++ b/service/src/main/java/com/fanruan/jdbc/statement/MyPreparedStatement.java @@ -0,0 +1,535 @@ +package com.fanruan.jdbc.statement; + +import com.fanruan.jdbc.resultset.MyResultSet; +import com.fanruan.proxy.ProxyFactory; + +import java.io.InputStream; +import java.io.Reader; +import java.math.BigDecimal; +import java.net.URL; +import java.sql.*; +import java.util.Calendar; +import java.util.Properties; + +/** + * @author Yichen Dai + */ +public class MyPreparedStatement implements PreparedStatement { + + private Properties info; + + private String ID; + + public MyPreparedStatement() {} + + public String getID(){ + return this.ID; + } + + public void setID(String ID){ + this.ID = ID; + } + + public void setInfo(Properties info) {this.info = info;} + + + @Override + public ResultSet executeQuery() throws SQLException { + if(isClosed()) { + throw new SQLException("This Statement is closed."); + } + MyResultSet rs = (MyResultSet) ProxyFactory.getProxy(MyResultSet.class, info); + rs.setSql(info.getProperty("PreparedSQL")); + return rs; + } + + @Override + public int executeUpdate() throws SQLException { + return 0; + } + + @Override + public void setNull(int parameterIndex, int sqlType) throws SQLException { + + } + + @Override + public void setBoolean(int parameterIndex, boolean x) throws SQLException { + + } + + @Override + public void setByte(int parameterIndex, byte x) throws SQLException { + + } + + @Override + public void setShort(int parameterIndex, short x) throws SQLException { + + } + + @Override + public void setInt(int parameterIndex, int x) throws SQLException { + + } + + @Override + public void setLong(int parameterIndex, long x) throws SQLException { + + } + + @Override + public void setFloat(int parameterIndex, float x) throws SQLException { + + } + + @Override + public void setDouble(int parameterIndex, double x) throws SQLException { + + } + + @Override + public void setBigDecimal(int parameterIndex, BigDecimal x) throws SQLException { + + } + + @Override + public void setString(int parameterIndex, String x) throws SQLException { + + } + + @Override + public void setBytes(int parameterIndex, byte[] x) throws SQLException { + + } + + @Override + public void setDate(int parameterIndex, Date x) throws SQLException { + + } + + @Override + public void setTime(int parameterIndex, Time x) throws SQLException { + + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x) throws SQLException { + + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, int length) throws SQLException { + + } + + @Override + public void setUnicodeStream(int parameterIndex, InputStream x, int length) throws SQLException { + + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, int length) throws SQLException { + + } + + @Override + public void clearParameters() throws SQLException { + + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType) throws SQLException { + + } + + @Override + public void setObject(int parameterIndex, Object x) throws SQLException { + + } + + @Override + public boolean execute() throws SQLException { + return false; + } + + @Override + public void addBatch() throws SQLException { + + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, int length) throws SQLException { + + } + + @Override + public void setRef(int parameterIndex, Ref x) throws SQLException { + + } + + @Override + public void setBlob(int parameterIndex, Blob x) throws SQLException { + + } + + @Override + public void setClob(int parameterIndex, Clob x) throws SQLException { + + } + + @Override + public void setArray(int parameterIndex, Array x) throws SQLException { + + } + + @Override + public ResultSetMetaData getMetaData() throws SQLException { + return null; + } + + @Override + public void setDate(int parameterIndex, Date x, Calendar cal) throws SQLException { + + } + + @Override + public void setTime(int parameterIndex, Time x, Calendar cal) throws SQLException { + + } + + @Override + public void setTimestamp(int parameterIndex, Timestamp x, Calendar cal) throws SQLException { + + } + + @Override + public void setNull(int parameterIndex, int sqlType, String typeName) throws SQLException { + + } + + @Override + public void setURL(int parameterIndex, URL x) throws SQLException { + + } + + @Override + public ParameterMetaData getParameterMetaData() throws SQLException { + return null; + } + + @Override + public void setRowId(int parameterIndex, RowId x) throws SQLException { + + } + + @Override + public void setNString(int parameterIndex, String value) throws SQLException { + + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value, long length) throws SQLException { + + } + + @Override + public void setNClob(int parameterIndex, NClob value) throws SQLException { + + } + + @Override + public void setClob(int parameterIndex, Reader reader, long length) throws SQLException { + + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream, long length) throws SQLException { + + } + + @Override + public void setNClob(int parameterIndex, Reader reader, long length) throws SQLException { + + } + + @Override + public void setSQLXML(int parameterIndex, SQLXML xmlObject) throws SQLException { + + } + + @Override + public void setObject(int parameterIndex, Object x, int targetSqlType, int scaleOrLength) throws SQLException { + + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x, long length) throws SQLException { + + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x, long length) throws SQLException { + + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader, long length) throws SQLException { + + } + + @Override + public void setAsciiStream(int parameterIndex, InputStream x) throws SQLException { + + } + + @Override + public void setBinaryStream(int parameterIndex, InputStream x) throws SQLException { + + } + + @Override + public void setCharacterStream(int parameterIndex, Reader reader) throws SQLException { + + } + + @Override + public void setNCharacterStream(int parameterIndex, Reader value) throws SQLException { + + } + + @Override + public void setClob(int parameterIndex, Reader reader) throws SQLException { + + } + + @Override + public void setBlob(int parameterIndex, InputStream inputStream) throws SQLException { + + } + + @Override + public void setNClob(int parameterIndex, Reader reader) throws SQLException { + + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + return null; + } + + @Override + public int executeUpdate(String sql) throws SQLException { + return 0; + } + + @Override + public void close() throws SQLException { + + } + + @Override + public int getMaxFieldSize() throws SQLException { + return 0; + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + + } + + @Override + public int getMaxRows() throws SQLException { + return 0; + } + + @Override + public void setMaxRows(int max) throws SQLException { + + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + + } + + @Override + public int getQueryTimeout() throws SQLException { + return 0; + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + + } + + @Override + public void cancel() throws SQLException { + + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return null; + } + + @Override + public void clearWarnings() throws SQLException { + + } + + @Override + public void setCursorName(String name) throws SQLException { + + } + + @Override + public boolean execute(String sql) throws SQLException { + return false; + } + + @Override + public ResultSet getResultSet() throws SQLException { + return null; + } + + @Override + public int getUpdateCount() throws SQLException { + return 0; + } + + @Override + public boolean getMoreResults() throws SQLException { + return false; + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + + } + + @Override + public int getFetchDirection() throws SQLException { + return 0; + } + + @Override + public void setFetchSize(int rows) throws SQLException { + + } + + @Override + public int getFetchSize() throws SQLException { + return 0; + } + + @Override + public int getResultSetConcurrency() throws SQLException { + return 0; + } + + @Override + public int getResultSetType() throws SQLException { + return 0; + } + + @Override + public void addBatch(String sql) throws SQLException { + + } + + @Override + public void clearBatch() throws SQLException { + + } + + @Override + public int[] executeBatch() throws SQLException { + return new int[0]; + } + + @Override + public Connection getConnection() throws SQLException { + return null; + } + + @Override + public boolean getMoreResults(int current) throws SQLException { + return false; + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + return null; + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return 0; + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + return 0; + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + return 0; + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + return false; + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + return false; + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + return false; + } + + @Override + public int getResultSetHoldability() throws SQLException { + return 0; + } + + @Override + public boolean isClosed() throws SQLException { + return false; + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + + } + + @Override + public boolean isPoolable() throws SQLException { + return false; + } + + @Override + public void closeOnCompletion() throws SQLException { + + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + return false; + } + + @Override + public T unwrap(Class iface) throws SQLException { + return null; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return false; + } +} diff --git a/service/src/main/java/com/fanruan/jdbc/statement/MyStatement.java b/service/src/main/java/com/fanruan/jdbc/statement/MyStatement.java new file mode 100644 index 0000000..b9e7495 --- /dev/null +++ b/service/src/main/java/com/fanruan/jdbc/statement/MyStatement.java @@ -0,0 +1,252 @@ +package com.fanruan.jdbc.statement; + +import com.fanruan.jdbc.resultset.MyResultSet; +import com.fanruan.proxy.ProxyFactory; + +import java.sql.*; +import java.util.Properties; + +public class MyStatement implements Statement { + private Properties info; + + private String ID; + + public MyStatement() {} + + public String getID(){ + return this.ID; + } + + public void setID(String ID){ + this.ID = ID; + } + + public void setInfo(Properties info){ + this.info = info; + } + + @Override + public ResultSet executeQuery(String sql) throws SQLException { + if(isClosed()) { + throw new SQLException("This Statement is closed."); + } + MyResultSet rs = (MyResultSet) ProxyFactory.getProxy(MyResultSet.class, info); + rs.setSql(sql); + return rs; + } + + @Override + public int executeUpdate(String sql) throws SQLException { + return 0; + } + + @Override + public void close() throws SQLException { + + } + + @Override + public int getMaxFieldSize() throws SQLException { + return 0; + } + + @Override + public void setMaxFieldSize(int max) throws SQLException { + + } + + @Override + public int getMaxRows() throws SQLException { + return 0; + } + + @Override + public void setMaxRows(int max) throws SQLException { + + } + + @Override + public void setEscapeProcessing(boolean enable) throws SQLException { + + } + + @Override + public int getQueryTimeout() throws SQLException { + return 0; + } + + @Override + public void setQueryTimeout(int seconds) throws SQLException { + + } + + @Override + public void cancel() throws SQLException { + + } + + @Override + public SQLWarning getWarnings() throws SQLException { + return null; + } + + @Override + public void clearWarnings() throws SQLException { + + } + + @Override + public void setCursorName(String name) throws SQLException { + + } + + @Override + public boolean execute(String sql) throws SQLException { + return false; + } + + @Override + public ResultSet getResultSet() throws SQLException { + return null; + } + + @Override + public int getUpdateCount() throws SQLException { + return 0; + } + + @Override + public boolean getMoreResults() throws SQLException { + return false; + } + + @Override + public void setFetchDirection(int direction) throws SQLException { + + } + + @Override + public int getFetchDirection() throws SQLException { + return 0; + } + + @Override + public void setFetchSize(int rows) throws SQLException { + + } + + @Override + public int getFetchSize() throws SQLException { + return 0; + } + + @Override + public int getResultSetConcurrency() throws SQLException { + return 0; + } + + @Override + public int getResultSetType() throws SQLException { + return 0; + } + + @Override + public void addBatch(String sql) throws SQLException { + + } + + @Override + public void clearBatch() throws SQLException { + + } + + @Override + public int[] executeBatch() throws SQLException { + return new int[0]; + } + + @Override + public Connection getConnection() throws SQLException { + return null; + } + + @Override + public boolean getMoreResults(int current) throws SQLException { + return false; + } + + @Override + public ResultSet getGeneratedKeys() throws SQLException { + return null; + } + + @Override + public int executeUpdate(String sql, int autoGeneratedKeys) throws SQLException { + return 0; + } + + @Override + public int executeUpdate(String sql, int[] columnIndexes) throws SQLException { + return 0; + } + + @Override + public int executeUpdate(String sql, String[] columnNames) throws SQLException { + return 0; + } + + @Override + public boolean execute(String sql, int autoGeneratedKeys) throws SQLException { + return false; + } + + @Override + public boolean execute(String sql, int[] columnIndexes) throws SQLException { + return false; + } + + @Override + public boolean execute(String sql, String[] columnNames) throws SQLException { + return false; + } + + @Override + public int getResultSetHoldability() throws SQLException { + return 0; + } + + @Override + public boolean isClosed() throws SQLException { + return false; + } + + @Override + public void setPoolable(boolean poolable) throws SQLException { + + } + + @Override + public boolean isPoolable() throws SQLException { + return false; + } + + @Override + public void closeOnCompletion() throws SQLException { + + } + + @Override + public boolean isCloseOnCompletion() throws SQLException { + return false; + } + + @Override + public T unwrap(Class iface) throws SQLException { + return null; + } + + @Override + public boolean isWrapperFor(Class iface) throws SQLException { + return false; + } +} diff --git a/service/src/main/java/com/fanruan/pojo/message/RpcRequest.java b/service/src/main/java/com/fanruan/pojo/message/RpcRequest.java new file mode 100644 index 0000000..1bfa565 --- /dev/null +++ b/service/src/main/java/com/fanruan/pojo/message/RpcRequest.java @@ -0,0 +1,29 @@ +package com.fanruan.pojo.message; + +import lombok.Data; +import lombok.experimental.Accessors; + +/** + * @author Yichen Dai + */ +@Data +@Accessors(chain = true) +public class RpcRequest { + /** + * Marks whether the method delivered need loopback data + */ + private boolean reply; + + /** + * Marks whether the method will create an instance requeired to be cached. + * In the project, they are Drive( MyDriver ), Connection( MyConnection ), Statement( MyStatement ), + * PreparedStatement( MyPreparedStatement ), ResultSet( MyResult ). + */ + private boolean binding; + private String ID; + private String IDToInvoke; + private Class serviceClass; + private String methodName; + private Object[] args; + private Class[] argTypes; +} diff --git a/service/src/main/java/com/fanruan/pojo/message/RpcResponse.java b/service/src/main/java/com/fanruan/pojo/message/RpcResponse.java new file mode 100644 index 0000000..803ee22 --- /dev/null +++ b/service/src/main/java/com/fanruan/pojo/message/RpcResponse.java @@ -0,0 +1,21 @@ +package com.fanruan.pojo.message; + +import lombok.Data; +import lombok.experimental.Accessors; + + +/** + * @author Yichen Dai + */ +@Data +@Accessors(chain = true) +public class RpcResponse { + + private String ID; + + private Object result; + + private boolean binding; + + private Boolean status; +} \ No newline at end of file diff --git a/service/src/main/java/com/fanruan/proxy/ProxyFactory.java b/service/src/main/java/com/fanruan/proxy/ProxyFactory.java new file mode 100644 index 0000000..a91d729 --- /dev/null +++ b/service/src/main/java/com/fanruan/proxy/ProxyFactory.java @@ -0,0 +1,21 @@ +package com.fanruan.proxy; + + +import com.fanruan.proxy.interceptor.Interceptor; +import net.sf.cglib.proxy.Enhancer; + +import java.util.Properties; + +/** + * @author Yichen Dai + */ +public class ProxyFactory { + + public static Object getProxy(Class clazz, Properties info){ + final Enhancer enhancer = new Enhancer(); + enhancer.setClassLoader(clazz.getClassLoader()); + enhancer.setSuperclass(clazz); + enhancer.setCallback(new Interceptor(clazz, info)); + return enhancer.create(); + } +} diff --git a/service/src/main/java/com/fanruan/proxy/interceptor/Interceptor.java b/service/src/main/java/com/fanruan/proxy/interceptor/Interceptor.java new file mode 100644 index 0000000..90eda3e --- /dev/null +++ b/service/src/main/java/com/fanruan/proxy/interceptor/Interceptor.java @@ -0,0 +1,146 @@ +package com.fanruan.proxy.interceptor; + + +import com.corundumstudio.socketio.SocketIOClient; +import com.fanruan.ServerStater; +import com.fanruan.cache.ClientCache; +import com.fanruan.cache.ClientWrapper; +import com.fanruan.cache.LockAndCondition; +import com.fanruan.pojo.message.RpcRequest; +import com.fanruan.utils.Commons; +import net.sf.cglib.proxy.MethodInterceptor; +import net.sf.cglib.proxy.MethodProxy; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; + +import java.lang.reflect.Method; +import java.util.Properties; +import java.util.concurrent.FutureTask; +import java.util.concurrent.locks.Condition; +import java.util.concurrent.locks.ReentrantLock; + +/** + * @author Yichen Dai + * cglib enhenced method to relize RPC + * For example, when service execute query operation, + * the RPC request will send to notify agent to do the same things, + * like create connection and statement + */ +public class Interceptor implements MethodInterceptor { + protected static final Logger logger = LogManager.getLogger(); + + private Class clazz; + private SocketIOClient client; + private Properties info; + + public Interceptor(Class clazz, Properties info){ + this.clazz = clazz; + this.info = info; + } + + @Override + public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { + if(InterceptorUtils.isInExcludedList(method.getName())){ + return methodProxy.invokeSuper(o, objects); + } + // Parameters injection of class MyDriver's construction method will be delayed util the first "connect" method was intercepted + // Because Driver Instance is registered on the DriverManager in the static code block, + // in which, the parameters used to fetch socket in cache is hard to pass in. + if(info == null){ + info = (Properties) objects[1]; + } + String agentId = info.getProperty("agentID"); + String dbName = info.getProperty("agentDBName"); + if(client == null){ + client = ClientCache.getClient(agentId, dbName); + } + + logger.debug("start invoke " + method.getName()); + + RpcRequest rpcRequest = new RpcRequest(); + rpcRequest.setReply(false) + .setBinding(false) + .setID(Commons.getID()) + .setServiceClass(clazz) + .setMethodName(method.getName()) + .setArgs(objects) + .setArgTypes(getArgTypes(objects)); + + // Set whether the rpcResponses of this rpcRequest need to carry return value + if(o instanceof com.fanruan.jdbc.resultset.MyResultSet){ + boolean flag = InterceptorUtils.isInReplyList(method.getName()); + if(flag) { + rpcRequest.setReply(true); + } + } + + // Some instance need to be bound one-to-one, to make sure the operator happen in service + // will be deliver to this specific corresponding instance. + if(InterceptorUtils.isInBindList(o)){ + rpcRequest.setBinding(true); + } + + // IDtoInvoke is an unique ID to identify an one-to-one binding relation. + // It comes from rpcRequest in which the instance in the agent is created. + String idToInvoke = InterceptorUtils.getInvokeHelper(o, "getID", String.class); + if(idToInvoke != null){ + rpcRequest.setIDToInvoke(idToInvoke); + } + + FutureTask futureTask = new FutureTask<>( + () -> { + Object res = null; + ClientWrapper wrapper = ClientCache.getClientWrapper(agentId, dbName); + LockAndCondition lac = wrapper.getLockAndCondition(rpcRequest.getID()); + ReentrantLock lock = lac.getLock(); + Condition condition = lac.getCondition(); + lock.lock(); + try{ + byte[] bytes = ServerStater.SERIALIZER.serialize(rpcRequest); + client.sendEvent("RPCRequest", bytes); + condition.await(); + // get res from RPC response data + res = lac.getResult(); + }catch (Exception e){ + e.printStackTrace(); + }finally { + lock.unlock(); + } + return res; + } + ); + ServerStater.threadPool.submit(futureTask); + Object res = futureTask.get(); + + // res is not null, it indicates the response carries data. + // if the type of res is primitive type, An error will occur when using cast(), just return them directly. + // And the data carried by response will never be the instance need to be bound. + if(res != null){ + if(InterceptorUtils.isWraps(res)){ + return res; + } + return res; + } + + + Object returnObj = methodProxy.invokeSuper(o, objects); + + // If the return instance is corresponding with another instance in agent, set the binding ID. + if (InterceptorUtils.isInBindList(returnObj)){ + InterceptorUtils.setInvokeHelper(returnObj, "setID", rpcRequest.getID()); + } + + return returnObj; + } + + + + public Class[] getArgTypes(Object[] objects){ + int n = objects.length; + Class[] argTypes = new Class[n]; + for(int i=0; i T getInvokeHelper(Object o, String methodName, Class T){ + try { + Method method = o.getClass().getDeclaredMethod(methodName); + T res = (T) method.invoke(o); + return res; + } catch (Exception e) { + e.printStackTrace(); + } + return null; + } + +} diff --git a/service/src/main/java/com/fanruan/serializer/KryoSerializer.java b/service/src/main/java/com/fanruan/serializer/KryoSerializer.java new file mode 100644 index 0000000..fe16a1b --- /dev/null +++ b/service/src/main/java/com/fanruan/serializer/KryoSerializer.java @@ -0,0 +1,54 @@ +package com.fanruan.serializer; + +import com.esotericsoftware.kryo.Kryo; +import com.esotericsoftware.kryo.io.Input; +import com.esotericsoftware.kryo.io.Output; +import com.fanruan.pojo.message.RpcRequest; +import com.fanruan.pojo.message.RpcResponse; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +/** + * @author Yichen Dai + */ +public class KryoSerializer implements Serializer { + + private static final ThreadLocal kryoThreadLocal = ThreadLocal.withInitial(() -> { + final Kryo kryo = new Kryo(); + kryo.register(RpcRequest.class); + kryo.register(RpcResponse.class); + kryo.setReferences(true); + kryo.setRegistrationRequired(false); + return kryo; + }); + + @Override + public byte[] serialize(Object object) { + try (ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + Output output = new Output(byteArrayOutputStream)) { + final Kryo kryo = kryoThreadLocal.get(); + kryo.writeObject(output, object); + kryoThreadLocal.remove(); + return output.toBytes(); + } catch (IOException e) { + e.printStackTrace(); + } + return new byte[0]; + } + + @Override + public T deserialize(byte[] bytes, Class clazz) { + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes); + Input input = new Input(byteArrayInputStream)) { + final Kryo kryo = kryoThreadLocal.get(); + final Object o = kryo.readObject(input, clazz); + kryoThreadLocal.remove(); + return clazz.cast(o); + } catch (IOException e) { + e.printStackTrace(); + } + return null; + } +} diff --git a/service/src/main/java/com/fanruan/serializer/Serializer.java b/service/src/main/java/com/fanruan/serializer/Serializer.java new file mode 100644 index 0000000..c8ca2fb --- /dev/null +++ b/service/src/main/java/com/fanruan/serializer/Serializer.java @@ -0,0 +1,21 @@ +package com.fanruan.serializer; + +/** + * @author Yichen Dai + */ +public interface Serializer { + /** + * Use to serialize a object to a byte array + * @param object to be serialized + * @return byte[] serialized data with the format of byte array + */ + byte[] serialize(Object object); + + /** + * Use to deserialize a byte array to a class with designate class + * @param bytes Serialized data with the format of byte array + * @param clazz The class of the object to be deserialized + * @return object as designate class + */ + T deserialize(byte[] bytes, Class clazz); +} diff --git a/service/src/main/java/com/fanruan/utils/Commons.java b/service/src/main/java/com/fanruan/utils/Commons.java new file mode 100644 index 0000000..136cb7e --- /dev/null +++ b/service/src/main/java/com/fanruan/utils/Commons.java @@ -0,0 +1,47 @@ +package com.fanruan.utils; + +import com.corundumstudio.socketio.SocketIOClient; + +import java.util.Calendar; +import java.util.Random; + + +/** + * @author Yichen Dai + */ +public class Commons { + + static public String getAgentID(SocketIOClient client){ + return client.getHandshakeData().getSingleUrlParam("agentID"); + } + + static public String getDBName(SocketIOClient client){ + // the format of spaceNamed "/" + "dbName" + // default name space named as "/" + String spaceName = client.getNamespace().getName(); + String dbName; + if(spaceName.length() > 1) { + dbName = spaceName.substring(1); + }else{ + dbName = spaceName; + } + return dbName; + } + + public static String getID(){ + return getTimeInMillis() + getRandom(); + } + + public static String getTimeInMillis() { + long timeInMillis = Calendar.getInstance().getTimeInMillis(); + return timeInMillis+""; + } + + public static String getRandom() { + Random random = new Random(); + int nextInt = random.nextInt(9000000); + nextInt = nextInt + 1000000; + return nextInt+""; + } + +} diff --git a/service/src/main/java/com/fanruan/utils/DBProperties.java b/service/src/main/java/com/fanruan/utils/DBProperties.java new file mode 100644 index 0000000..1ce051e --- /dev/null +++ b/service/src/main/java/com/fanruan/utils/DBProperties.java @@ -0,0 +1,21 @@ +package com.fanruan.utils; + + +/** + * @author Yichen Dai + */ +public class DBProperties { + public static final String MYSQL = "mysql"; + public static final String POSTGRESQL = "postgresql"; + public static final String ORACLE = "oracle"; + public static final String SQLSERVER = "sqlserver"; + public static final String DB2 = "db2"; + public static final String HSQL = "hsql"; + + public static final String MYSQL_DRIVER_NAME = "com.mysql.cj.jdbc.Driver"; + public static final String POSTGRESQL_DRIVER_NAME = "org.postgresql.Driver"; + public static final String ORACLE_DRIVER_NAME = "oracle.jdbc.driver.OracleDriver"; + public static final String SQLSERVER_DRIVER_NAME = "com.microsoft.jdbc.sqlserver.SQLServerDriver"; + public static final String DB2_DRIVER_NAME = "com.ibm.db2.jdbc.app.DB2Driver"; + public static final String HSQL_DRIVER_NAME = "org.hsqldb.jdbcDriver"; +} diff --git a/service/src/main/resources/log4j2.properties b/service/src/main/resources/log4j2.properties new file mode 100644 index 0000000..a1780ce --- /dev/null +++ b/service/src/main/resources/log4j2.properties @@ -0,0 +1,11 @@ + +# Console appender configuration +appender.console.type = Console +appender.console.name = STDOUT +appender.console.layout.type = PatternLayout +appender.console.layout.pattern = [%-5level] %d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %c{1} - %msg%n + + +rootLogger.level = info +rootLogger.appenderRefs = stdout +rootLogger.appenderRef.stdout.ref = STDOUT \ No newline at end of file diff --git a/service/src/main/resources/socketIO.properties b/service/src/main/resources/socketIO.properties new file mode 100644 index 0000000..488ceaa --- /dev/null +++ b/service/src/main/resources/socketIO.properties @@ -0,0 +1,23 @@ +#============================================================================ +# netty socket io setting +#============================================================================ +# host在本地测试可以设置为localhost或者本机IP,在Linux服务器跑可换成服务器IP +#监听的ip +host = 127.0.0.1 +#监听端口 +port = 10246 +# 设置最大每帧处理数据的长度,防止他人利用大数据来攻击服务器 +maxFramePayloadLength = 1048576 +# 设置http交互最大内容长度 +maxHttpContentLength = 1048576 +# socket连接数大小(如只监听一个端口boss线程组为1即可) +bossCount = 1 +workCount = 10 +allowCustomRequests = true +# 协议升级超时时间(毫秒),默认10秒。HTTP握手升级为ws协议超时时间 +upgradeTimeout = 1000000 +# Ping消息超时时间(毫秒),默认60秒,这个时间间隔内没有接收到心跳消息就会发送超时事件 +pingTimeout = 6000000 +# Ping消息间隔(毫秒),默认25秒。客户端向服务器发送一条心跳消息间隔 +pingInterval = 25000 + diff --git a/service/src/test/java/AutoStarter.java b/service/src/test/java/AutoStarter.java new file mode 100644 index 0000000..8108300 --- /dev/null +++ b/service/src/test/java/AutoStarter.java @@ -0,0 +1,22 @@ +import com.fanruan.ServerStater; +import com.fanruan.utils.DBProperties; + +/** + * @author Yichen Dai + * @date 2022/8/18 9:54 + */ +public class AutoStarter { + + static { + String[] DBs = new String[]{ + DBProperties.HSQL, + }; + new ServerStater(DBs); + + try { + Class.forName("com.fanruan.jdbc.driver.MyDriver"); + } catch (ClassNotFoundException e) { + e.printStackTrace(); + } + } +} diff --git a/service/src/test/java/ServiceTest.java b/service/src/test/java/ServiceTest.java new file mode 100644 index 0000000..9e5d424 --- /dev/null +++ b/service/src/test/java/ServiceTest.java @@ -0,0 +1,105 @@ +import com.corundumstudio.socketio.SocketIOClient; +import com.corundumstudio.socketio.SocketIOServer; +import com.fanruan.cache.ClientCache; +import com.fanruan.utils.DBProperties; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.net.Socket; +import java.sql.*; +import java.util.Properties; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.FutureTask; + +/** + * @author Yichen Dai + * @date 2022/8/18 9:49 + */ +public class ServiceTest extends AutoStarter{ + + @BeforeEach + void listen() throws ExecutionException, InterruptedException { + FutureTask ft = new FutureTask<>( + () -> { + while(ClientCache.getClient("1001", DBProperties.HSQL) == null){ + try { + Thread.sleep(100); + }catch (Exception e){ + e.printStackTrace(); + } + } + return ClientCache.getClient("1001", DBProperties.HSQL); + } + ); + ft.run(); + ft.get(); + + + } + + @Test + void testCURD(){ + Properties info = new Properties(); + info.setProperty("user", "sa"); + info.setProperty("password", ""); + info.setProperty("agentID", "1001"); + info.setProperty("agentDBName", DBProperties.HSQL); + + Connection conn = null; + Statement st = null; + PreparedStatement pst = null; + ResultSet rs = null; + try { + conn = DriverManager.getConnection("jdbc:hsqldb:mem:test", info); + st = conn.createStatement(); + st.executeUpdate("DROP TABLE student IF EXISTS;"); + + st.executeUpdate("CREATE TABLE student (" + + "student_id INTEGER GENERATED BY DEFAULT AS IDENTITY " + + "(START WITH 1, INCREMENT BY 1) NOT NULL," + + "student_name VARCHAR(100) NOT NULL," + + "student_address VARCHAR(100) NOT NULL," + + "PRIMARY KEY (student_id)" + + ");"); + + st.executeUpdate("INSERT INTO student VALUES" + + "(1, '张三', '上海')," + + "(2, '李四', '北京')," + + "(3, '王五', '成都');"); + + + + + pst = conn.prepareStatement("delete from student where student_id = ?"); + + pst.setInt(1, 1); + + pst.executeUpdate(); + + rs = st.executeQuery("select * from student"); + + String[] nameStrings = new String[]{"张三", "李四", "王五"}; + String[] addressStrings = new String[]{"上海", "北京", "成都"}; + + int num = 2; + while(rs.next()) { + Assertions.assertEquals(rs.getInt("student_id"), num); + Assertions.assertEquals(rs.getString("student_name"), nameStrings[num-1]); + Assertions.assertEquals(rs.getString("student_address"), addressStrings[num-1]); + num++; + } + } catch (Exception e) { + e.printStackTrace(); + } finally { + try{ + rs.close(); + st.close(); + pst.close(); + conn.close(); + }catch (Exception e){ + e.printStackTrace(); + } + } + } +} diff --git a/service/src/test/java/Test.java b/service/src/test/java/Test.java new file mode 100644 index 0000000..554c218 --- /dev/null +++ b/service/src/test/java/Test.java @@ -0,0 +1,244 @@ +import com.fanruan.ServerStater; +import com.fanruan.cache.ClientCache; +import com.fanruan.utils.DBProperties; + +import java.sql.*; +import java.util.Properties; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + + +public class Test { + public static void main(String[] args) { + + Test test = new Test(); + + String[] DBs = new String[]{ + DBProperties.MYSQL, + DBProperties.POSTGRESQL, +// DBProperties.SQLSERVER, +// DBProperties.DB2, +// DBProperties.ORACLE + }; + new ServerStater(DBs); + + ExecutorService threadPool = Executors.newSingleThreadExecutor(); + test.testPostSQL(threadPool); + test.testMySQL(threadPool); + + } + + public void testMySQL(ExecutorService threadPool){ + Thread thread = new Thread(() -> { + while(ClientCache.getClient("1001", "mysql") == null){ + try { + Thread.sleep(1000); + }catch (Exception e){ + e.printStackTrace(); + } + } + Properties info = new Properties(); + info.setProperty("user", "root"); + info.setProperty("password", "850656"); + info.setProperty("agentID", "1001"); + info.setProperty("agentDBName", "mysql"); + + Connection conn = null; + Statement st = null; + PreparedStatement pst = null; + PreparedStatement pst2 = null; + ResultSet rs1 = null; + ResultSet rs2 = null; + ResultSet rs3 = null; + try { + Class.forName("com.fanruan.jdbc.driver.MyDriver"); + conn = DriverManager.getConnection("jdbc:mysql://127.0.0.1:3306/test", info); + st = conn.createStatement(); + rs1 = st.executeQuery("select * from `student`"); + + System.out.println("-----------"); + System.out.println("执行查询语句"); + while(rs1.next()) { + System.out.print(rs1.getInt("student_id") + " "); + System.out.print(rs1.getString("student_name")+ " "); + System.out.println(rs1.getString("student_address")+ " "); + } + + String sql = "select * from `student` where `student_name`= ?"; + pst = conn.prepareStatement(sql); + pst.setString(1, "张三"); + rs2 = pst.executeQuery(); + + System.out.println("-----------"); + System.out.println("执行预查询语句1"); + while(rs2.next()) { + System.out.print(rs2.getInt("student_id") + " "); + System.out.print(rs2.getString("student_name")+ " "); + System.out.println(rs2.getString("student_address")+ " "); + } + + sql = "select * from `student` where `student_address`= ?"; + pst2 = conn.prepareStatement(sql); + pst2.setString(1, "上海"); + rs3 = pst2.executeQuery(); + + System.out.println("-----------"); + System.out.println("执行预查询语句2"); + while(rs3.next()) { + System.out.print(rs3.getInt("student_id") + " "); + System.out.print(rs3.getString("student_name")+ " "); + System.out.println(rs3.getString("student_address")+ " "); + } + }catch (Exception e) { + e.printStackTrace(); + } finally { + // 7、关闭对象,回收数据库资源 + if (rs1 != null) { //关闭结果集对象 + try { + rs1.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if (rs2 != null) { //关闭结果集对象 + try { + rs2.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if (st != null) { // 关闭数据库操作对象 + try { + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if (pst != null) { // 关闭数据库操作对象 + try { + pst.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if (conn != null) { // 关闭数据库连接对象 + try { + if (!conn.isClosed()) { + conn.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + }); + threadPool.execute(thread); + } + + public void testPostSQL(ExecutorService threadPool){ + Thread thread = new Thread(() -> { + while(ClientCache.getClient("1001", "postgresql") == null){ + try { + Thread.sleep(1000); + }catch (Exception e){ + e.printStackTrace(); + } + } + Properties info = new Properties(); + info.setProperty("user", "postgres"); + info.setProperty("password", "850656"); + info.setProperty("agentID", "1001"); + info.setProperty("agentDBName", "postgresql"); + + Connection conn = null; + Statement st = null; + PreparedStatement pst = null; + PreparedStatement pst2 = null; + ResultSet rs1 = null; + ResultSet rs2 = null; + ResultSet rs3 = null; + try { + Class.forName("com.fanruan.jdbc.driver.MyDriver"); + conn = DriverManager.getConnection("jdbc:postgresql://127.0.0.1:5432/test", info); + st = conn.createStatement(); + rs1 = st.executeQuery("select * from student"); + + System.out.println("-----------"); + System.out.println("执行查询语句"); + while(rs1.next()) { + System.out.print(rs1.getInt("student_id") + " "); + System.out.print(rs1.getString("student_name")+ " "); + System.out.println(rs1.getString("student_address")+ " "); + } + + String sql = "select * from student where student_name= ?"; + pst = conn.prepareStatement(sql); + pst.setString(1, "张三"); + rs2 = pst.executeQuery(); + + System.out.println("-----------"); + System.out.println("执行预查询语句1"); + while(rs2.next()) { + System.out.print(rs2.getInt("student_id") + " "); + System.out.print(rs2.getString("student_name")+ " "); + System.out.println(rs2.getString("student_address")+ " "); + } + + sql = "select * from student where student_address = ?"; + pst2 = conn.prepareStatement(sql); + pst2.setString(1, "上海"); + rs3 = pst2.executeQuery(); + + System.out.println("-----------"); + System.out.println("执行预查询语句2"); + while(rs3.next()) { + System.out.print(rs3.getInt("student_id") + " "); + System.out.print(rs3.getString("student_name")+ " "); + System.out.println(rs3.getString("student_address")+ " "); + } + }catch (Exception e) { + e.printStackTrace(); + } finally { + // 7、关闭对象,回收数据库资源 + if (rs1 != null) { //关闭结果集对象 + try { + rs1.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if (rs2 != null) { //关闭结果集对象 + try { + rs2.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if (st != null) { // 关闭数据库操作对象 + try { + st.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if (pst != null) { // 关闭数据库操作对象 + try { + pst.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + if (conn != null) { // 关闭数据库连接对象 + try { + if (!conn.isClosed()) { + conn.close(); + } + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + }); + threadPool.execute(thread); + } +} diff --git a/service/src/test/java/TestUtils.java b/service/src/test/java/TestUtils.java new file mode 100644 index 0000000..8f91940 --- /dev/null +++ b/service/src/test/java/TestUtils.java @@ -0,0 +1,8 @@ +/** + * @author Yichen Dai + * @date 2022/8/16 11:36 + */ +public class TestUtils { + + +}