/* * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.lealone.plugins.vertx.server; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java.util.HashMap; import java.util.concurrent.ConcurrentSkipListMap; import org.lealone.client.jdbc.JdbcConnection; import org.lealone.common.logging.Logger; import org.lealone.common.logging.LoggerFactory; import org.lealone.common.util.CamelCaseHelper; import org.lealone.db.service.Service; import org.lealone.db.session.ServerSession; import io.vertx.core.Handler; import io.vertx.core.buffer.Buffer; import io.vertx.core.json.JsonArray; import io.vertx.core.json.JsonObject; import io.vertx.ext.web.handler.sockjs.SockJSSocket; public class ServiceHandler implements Handler<SockJSSocket> { private static final Logger logger = LoggerFactory.getLogger(ServiceHandler.class); private static final ConcurrentSkipListMap<Integer, Connection> currentConnections = new ConcurrentSkipListMap<>(); private static void removeConnection(Integer key) { currentConnections.remove(key); } @Override public void handle(SockJSSocket sockJSSocket) { sockJSSocket.endHandler(v -> { removeConnection(sockJSSocket.hashCode()); }); sockJSSocket.exceptionHandler(t -> { removeConnection(sockJSSocket.hashCode()); logger.error("sockJSSocket exception", t); }); sockJSSocket.handler(buffer -> { Buffer ret = ServiceHandler.handle(sockJSSocket, buffer.getString(0, buffer.length())); sockJSSocket.end(ret); }); } public static Buffer handle(Object lock, String command) { String a[] = command.split(";"); int type = Integer.parseInt(a[0]); if (type < 500) { return executeService(a, type); } else { return executeSql(lock, a, type); } } private static Buffer executeService(String a[], int type) { String serviceName = CamelCaseHelper.toUnderscoreFromCamel(a[1]); String json = null; if (a.length >= 3) { json = a[2]; } JsonArray ja = new JsonArray(); String result = null; switch (type) { case 1: try { result = Service.execute(serviceName, json); ja.add(2); } catch (Exception e) { ja.add(3); result = "failed to execute service: " + serviceName + ", cause: " + e.getMessage(); logger.error(result, e); } break; default: ja.add(3); result = "unknown request type: " + type + ", serviceName: " + serviceName; logger.error(result); } ja.add(a[1]); // 前端传来的方法名不一定是下划线风格的,所以用最初的 ja.add(result); return Buffer.buffer(ja.toString()); } private static Buffer executeSql(Object lock, String a[], int type) { JsonArray ja = new JsonArray(); try { Connection conn; synchronized (lock) { conn = currentConnections.get(lock.hashCode()); if (conn == null) { String url = System.getProperty("lealone.jdbc.url"); if (url == null) { throw new RuntimeException("'lealone.jdbc.url' must be set"); } conn = DriverManager.getConnection(url); currentConnections.put(lock.hashCode(), conn); } } PreparedStatement ps = null; if (a.length > 2) { ps = conn.prepareStatement(a[2]); JsonArray parms = new JsonArray(a[3]); for (int i = 0, size = parms.size(); i < size; i++) { ps.setObject(i + 1, parms.getValue(i)); } } String result = "ok"; ja.add(type); ja.add(a[1]); switch (type) { case 500: { ps.executeUpdate(); long rowId = ((ServerSession) ((JdbcConnection) conn).getSession()).getLastIdentity().getLong(); ja.add(rowId); break; } case 501: case 502: int count = ps.executeUpdate(); ja.add(count); break; case 503: { ResultSet rs = ps.executeQuery(); HashMap<String, Object> map = new HashMap<>(); if (rs.next()) { ResultSetMetaData md = rs.getMetaData(); for (int i = 1, len = md.getColumnCount(); i <= len; i++) { map.put(md.getColumnName(i), rs.getString(i)); } } JsonObject jo = new JsonObject(map); ja.add(jo); break; } case 504: { ResultSet rs = ps.executeQuery(); ResultSetMetaData md = rs.getMetaData(); JsonArray records = new JsonArray(); while (rs.next()) { HashMap<String, Object> map = new HashMap<>(); for (int i = 1, len = md.getColumnCount(); i <= len; i++) { map.put(md.getColumnName(i), rs.getString(i)); } JsonObject jo = new JsonObject(map); records.add(jo); } ja.add(records); break; } case 601: conn.setAutoCommit(false); ja.add(result); break; case 602: conn.commit(); conn.setAutoCommit(true); ja.add(result); break; case 603: conn.rollback(); conn.setAutoCommit(true); ja.add(result); break; default: ja.add(3); result = "unknown request type: " + type + ", sql: " + a[2]; logger.error(result); } } catch (SQLException e) { ja.add(3); logger.error(e); } return Buffer.buffer(ja.toString()); } }