/* * Copyright 2015 Liu Huanting. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * in compliance with the License. You may obtain a copy of the License at * * 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 fm.liu.timo; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Map; import java.util.Map.Entry; import java.util.concurrent.locks.ReentrantLock; import org.pmw.tinylog.Logger; import fm.liu.timo.backend.Node; import fm.liu.timo.backend.Source; import fm.liu.timo.config.loader.ServerConfigLoader; import fm.liu.timo.config.loader.SystemConfigLoader; import fm.liu.timo.config.model.Database; import fm.liu.timo.config.model.Datanode; import fm.liu.timo.config.model.Datasource; import fm.liu.timo.config.model.SystemConfig; import fm.liu.timo.config.model.User; import fm.liu.timo.net.connection.Variables; import fm.liu.timo.util.TimeUtil; /** * @author Liu Huanting 2015年5月10日 */ public class TimoConfig { private volatile SystemConfig system; private volatile Map<String, User> users; private volatile Map<String, Database> databases; private volatile Map<Integer, Node> nodes; private volatile Map<Integer, Datasource> datasources; private ReentrantLock lock = new ReentrantLock(); private long lastReloadTime; public TimoConfig() { this.system = new SystemConfigLoader().getSystemConfig(); ServerConfigLoader conf = new ServerConfigLoader(system.getUrl(), system.getUsername(), system.getPassword()); this.users = conf.getUsers(); this.databases = conf.getDatabases(); this.datasources = conf.getDatasources(); this.nodes = initDatanodes(conf.getDatanodes(), conf.getDatasources(), conf.getHandovers()); } private Map<Integer, Node> initDatanodes(Map<Integer, Datanode> datanodes, Map<Integer, Datasource> datasources, Map<Integer, ArrayList<Integer>> handovers) { Variables variables = new Variables(); variables.setCharset(system.getCharset()); variables.setIsolationLevel(system.getTxIsolation()); Map<Integer, Source> sources = new HashMap<>(); for (Entry<Integer, Datasource> datasource : datasources.entrySet()) { sources.put(datasource.getKey(), new Source(datasource.getValue(), variables, system.getHeartbeatPeriod())); } for (Integer id : handovers.keySet()) { ArrayList<Source> backups = new ArrayList<>(); for (Integer handover : handovers.get(id)) { backups.add(sources.get(handover)); } sources.get(id).setBackups(backups); } Map<Integer, Node> nodes = new HashMap<Integer, Node>(); for (Datanode datanode : datanodes.values()) { ArrayList<Source> sourceList = new ArrayList<Source>(); for (Integer i : datanode.getDatasources()) { sourceList.add(sources.get(i)); } Node node = new Node(datanode.getID(), datanode.getStrategy(), sourceList); nodes.put(datanode.getID(), node); } return nodes; } private volatile SystemConfig _system; private volatile Map<String, User> _users; private volatile Map<String, Database> _databases; private volatile Map<Integer, Node> _nodes; private volatile Map<Integer, Datasource> _datasources; /** * reload @@config */ public boolean reload() { boolean success = false; _system = this.system; _users = this.users; _databases = this.databases; _datasources = this.datasources; _nodes = this.nodes; lock.lock(); try { this.system = new SystemConfigLoader().getSystemConfig(); ServerConfigLoader conf = new ServerConfigLoader(system.getUrl(), system.getUsername(), system.getPassword()); this.users = conf.getUsers(); this.databases = conf.getDatabases(); this.datasources = conf.getDatasources(); this.nodes = initDatanodes(conf.getDatanodes(), conf.getDatasources(), conf.getHandovers()); for (Node node : nodes.values()) { if (!node.init()) { throw new Exception( "node " + node.getID() + " init failed in config reloading."); } } success = true; } catch (Exception e) { this.system = _system; this.users = _users; this.databases = _databases; this.datasources = _datasources; this.nodes = _nodes; Logger.warn("reload config failed due to {}", e.getMessage()); } finally { lock.unlock(); } if (success) { _nodes.values().parallelStream() .forEach(node -> node.clear("clear old node due to reload config")); lastReloadTime = TimeUtil.currentTimeMillis(); } return success; } /** * rollback @@config */ public boolean rollback() { if (lastReloadTime == 0) { return false; } Collection<Node> backNodes = this.nodes.values(); boolean success = false; lock.lock(); try { for (Node node : _nodes.values()) { if (!node.init()) { throw new Exception( "node " + node.getID() + " init failed in config reloading."); } } this.system = _system; this.users = _users; this.databases = _databases; this.datasources = _datasources; this.nodes = _nodes; success = true; } catch (Exception e) { for (Node node : _nodes.values()) { node.clear("clear back nodes due to rollback config failure"); } } finally { lock.unlock(); } if (success) { backNodes.parallelStream() .forEach(node -> node.clear("clear backnodes due to rollback config")); } return success; } public SystemConfig getSystem() { return system; } public Map<String, User> getUsers() { return users; } public Map<String, Database> getDatabases() { return databases; } public Map<Integer, Node> getNodes() { return nodes; } public ReentrantLock getLock() { return lock; } public Map<Integer, Datasource> getDatasources() { return datasources; } }