package org.zbus.server.mq.store; import java.io.IOException; import java.io.InputStream; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.List; import java.util.Properties; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import org.zbus.common.json.JSON; import org.zbus.common.logging.Logger; import org.zbus.common.logging.LoggerFactory; import org.zbus.common.protocol.MessageMode; import org.zbus.common.protocol.MqInfo; import org.zbus.common.remoting.Message; import org.zbus.common.remoting.MessageCodec; import org.zbus.common.remoting.nio.IoBuffer; import org.zbus.server.mq.MessageQueue; import org.zbus.server.mq.RequestQueue; public class MessageStoreSql implements MessageStore { private static final Logger log = LoggerFactory.getLogger(MessageStoreSql.class); private static final MessageCodec codec = new MessageCodec(); private final static String CONFIG_FILE = "sql.properties"; private final Properties props = new Properties(); private Connection connection; private String driver = "org.hsqldb.jdbcDriver"; private String url = "jdbc:hsqldb:db/zbus"; private String user = "sa"; private String password = ""; private String sqlMsgs = "CREATE TABLE IF NOT EXISTS msgs(id VARCHAR(128), msg_str VARCHAR(10240000), PRIMARY KEY(id) )"; private String sqlMqMsgs = "CREATE TABLE IF NOT EXISTS mq_msgs(mq_id VARCHAR(128), msg_id VARCHAR(128) )"; private String sqlMqs = "CREATE TABLE IF NOT EXISTS mqs(id VARCHAR(512), mq_info VARCHAR(10240000), PRIMARY KEY(id) )"; private final String brokerKey; public MessageStoreSql(String broker) throws Exception { this.brokerKey = broker; //从配置文件中读取配置信息 InputStream stream = getClass().getClassLoader().getResourceAsStream(CONFIG_FILE); try { if(stream != null){ props.load(stream); } else { log.warn("missing properties: "+ CONFIG_FILE); } } catch (IOException e) { log.error(e.getMessage(), e); } } private Connection getConnection(){ try { url = props.getProperty("url", url).trim(); user = props.getProperty("sa", user).trim(); password = props.getProperty("password", password).trim(); return DriverManager.getConnection(this.url, this.user, this.password); } catch (Exception e) { log.error(e.getMessage(), e); } return null; } public void start() throws Exception{ driver = props.getProperty("driver", driver).trim(); Class.forName(driver); connection = getConnection(); this.initDbTable(); } public void shutdown() throws Exception{ if(connection == null) return; try{ Statement st = connection.createStatement(); st.execute("SHUTDOWN"); connection.close(); }catch(Exception e){ log.error(e.getMessage(), e); } } private String msgKey(Message msg){ return msg.getMsgId(); } private String mqKey(String mq){ return String.format("%s%s", brokerKey, mq); } @Override public void saveMessage(Message msg) { try{ String msgId = msgKey(msg); String mqId = mqKey(msg.getMq()); this.update("INSERT INTO msgs(id, msg_str) VALUES(?,?)",msgId, msg.toString()); this.update("INSERT INTO mq_msgs(mq_id, msg_id) VALUES(?,?)", mqId, msgId); if(log.isDebugEnabled()){ log.debug("save " + msgId); } } catch(Exception e){ log.error(e.getMessage(), e); } } @Override public void removeMessage(Message msg) { try{ String msgId = msgKey(msg); this.update("DELETE FROM msgs WHERE id=?", msgId); this.update("DELETE FROM mq_msgs WHERE msg_id=?", msgId); if(log.isDebugEnabled()){ log.debug("delete " + msgId); } } catch(Exception e){ log.error(e.getMessage(), e); } } @Override public void onMessageQueueCreated(MessageQueue mq) { try{ String mqId = mqKey(mq.getName()); String json = JSON.toJSONString(mq.getMqInfo()); this.update("INSERT INTO mqs(id, mq_info) VALUES(?,?)", mqId, json); } catch(Exception e){ log.error(e.getMessage(), e); } } @Override public void onMessageQueueRemoved(MessageQueue mq) { try{ String mqId = mqKey(mq.getName()); this.update("DELETE FROM mqs WHERE id=?", mqId); } catch(Exception e){ log.error(e.getMessage(), e); } } @Override public ConcurrentMap<String, MessageQueue> loadMqTable() throws SQLException { ConcurrentHashMap<String, MessageQueue> res = new ConcurrentHashMap<String, MessageQueue>(); ResultSet mqRs = this.query("SELECT * FROM mqs"); while(mqRs.next()) { String mqId = mqRs.getString("id"); String mqName = mqId.substring(brokerKey.length()); String mqInfoString = mqRs.getString("mq_info"); MqInfo info = JSON.parseObject(mqInfoString, MqInfo.class); int mode = info.getMode(); if(!MessageMode.isEnabled(mode, MessageMode.MQ)){ log.warn("message queue mode not support"); continue; } RequestQueue mq = new RequestQueue(info.getBroker(), mqName, null, mode); mq.setCreator(info.getCreator()); mq.setMessageStore(this); String sql = String.format("SELECT msg_str FROM mq_msgs, msgs WHERE mq_msgs.msg_id = msgs.id AND mq_msgs.mq_id='%s'", mqId); ResultSet msgRs = this.query(sql); List<Message> msgs = new ArrayList<Message>(); while(msgRs.next()){ String msgString = msgRs.getString("msg_str"); IoBuffer buf = IoBuffer.wrap(msgString); Message msg = (Message) codec.decode(buf); if(msg != null){ msgs.add(msg); } else { log.error("message decode error"); } } msgRs.close(); mq.loadMessageList(msgs); res.put(mqName, mq); } mqRs.close(); return res; } private void initDbTable() throws SQLException{ sqlMsgs = props.getProperty("sql_msgs", sqlMsgs).trim(); sqlMqMsgs = props.getProperty("sql_mq_msgs", sqlMqMsgs).trim(); sqlMqs = props.getProperty("sql_mqs", sqlMqs).trim(); this.update(sqlMsgs); this.update(sqlMqMsgs); this.update(sqlMqs); } private synchronized ResultSet query(String sql) throws SQLException { if(this.connection == null) return null; Statement st = connection.createStatement(); return st.executeQuery(sql); } private synchronized void update(String sql, Object... args) throws SQLException { if(this.connection == null) return; PreparedStatement st = connection.prepareStatement(sql); for(int i=0;i<args.length;i++){ st.setObject(i+1, args[i]); } int i = st.executeUpdate(); if (i == -1) { log.error("db error : " + sql); } st.close(); } }