├── .gitignore ├── ZkConfig使用说明.pdf ├── .settings ├── org.eclipse.wst.validation.prefs ├── org.eclipse.m2e.core.prefs ├── org.eclipse.core.resources.prefs ├── org.eclipse.wst.common.project.facet.core.xml ├── org.eclipse.wst.common.component └── org.eclipse.jdt.core.prefs ├── src ├── main │ └── java │ │ ├── com │ │ └── lashou │ │ │ └── v │ │ │ └── zkconfig │ │ │ ├── dirwatch │ │ │ ├── FileWatchProcessor.java │ │ │ ├── FileWatchProcessorImpl.java │ │ │ ├── PropertiesFileWatchProcessorImpl.java │ │ │ └── FileWatch.java │ │ │ ├── annotaion │ │ │ ├── ZkValue.java │ │ │ └── ZkConfig.java │ │ │ ├── common │ │ │ ├── ZkCconfigConstant.java │ │ │ ├── RegisterCollection.java │ │ │ ├── ElementCollection.java │ │ │ ├── SystemInit.java │ │ │ ├── ConnectionWatcher.java │ │ │ ├── ActiveKeyValueStore.java │ │ │ ├── ZkConfigParser.java │ │ │ ├── FileUtils.java │ │ │ └── MemoryDataSynchronizer.java │ │ │ ├── monitor │ │ │ ├── MonitorFactory.java │ │ │ ├── Monitor.java │ │ │ ├── ConfFileMonitor.java │ │ │ ├── DataMonitor.java │ │ │ └── PropertiesFileMonitor.java │ │ │ ├── bean │ │ │ └── ConfBeanMetaData.java │ │ │ ├── springplugin │ │ │ ├── spring-example.xml │ │ │ └── ZkConfigScannerConfigurer.java │ │ │ └── core │ │ │ ├── ZkConfigExecutor.java │ │ │ └── BeanRegisterCenter.java │ │ ├── zkconfig.properties │ │ ├── log4j.properties │ │ └── reademe.txt └── test │ └── java │ └── com │ └── lashou │ └── v │ └── zkconfig │ ├── TestMonitor.java │ ├── TestFileWatch.java │ ├── TestRegisterCenter.java │ ├── TestConfigurer.java │ ├── TestObj.java │ ├── TestAnnotation.java │ ├── ConfigUpdater.java │ └── ConfigWatcher.java ├── .classpath ├── .project └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | -------------------------------------------------------------------------------- /ZkConfig使用说明.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/QuanZhong/ZKConfig/HEAD/ZkConfig使用说明.pdf -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.validation.prefs: -------------------------------------------------------------------------------- 1 | disabled=06target 2 | eclipse.preferences.version=1 3 | -------------------------------------------------------------------------------- /.settings/org.eclipse.m2e.core.prefs: -------------------------------------------------------------------------------- 1 | activeProfiles= 2 | eclipse.preferences.version=1 3 | resolveWorkspaceProjects=true 4 | version=1 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.core.resources.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | encoding//src/main/java=UTF-8 3 | encoding//src/test/java=UTF-8 4 | encoding/=UTF-8 5 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.project.facet.core.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.settings/org.eclipse.wst.common.component: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/dirwatch/FileWatchProcessor.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.dirwatch; 2 | 3 | import java.io.File; 4 | 5 | /** 6 | * 目录监视处理器,当目录监视器发现目录中文件该变时候会回调process方法返回改变后的文件列表 7 | * @author quanzhong 8 | * 9 | */ 10 | public interface FileWatchProcessor { 11 | 12 | public Object process(File file); 13 | } 14 | -------------------------------------------------------------------------------- /src/main/java/zkconfig.properties: -------------------------------------------------------------------------------- 1 | #A comma separated.eg:127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 2 | hosts=127.0.0.1:2181 3 | 4 | sessionTimeout=5000 5 | 6 | #Your app name. 7 | app_name=cs 8 | 9 | #The conf file paths of your application.A comma separated.eg:/data/config/v-cs,/data/config/v-crm 10 | #default value ${app.instance.config} 11 | app_config_paths=${app.instance.config} 12 | 13 | #Directory monitoring frequency 14 | dir_watch_period=3000 -------------------------------------------------------------------------------- /src/test/java/com/lashou/v/zkconfig/TestMonitor.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig; 2 | 3 | import java.io.File; 4 | import java.util.List; 5 | 6 | import com.lashou.v.zkconfig.monitor.ConfFileMonitor; 7 | import com.lashou.v.zkconfig.monitor.Monitor; 8 | 9 | public class TestMonitor { 10 | 11 | public static void main(String[] args) { 12 | Monitor monitor = new ConfFileMonitor(); 13 | List path = monitor.getAllConfigPaths(); 14 | List files = monitor.getAllConfigFiles(); 15 | System.out.println(path); 16 | System.out.println(files); 17 | 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/annotaion/ZkValue.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.annotaion; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 用于解析配置文件的注解 10 | * @author QuanZhong 11 | * @version 12 | * @since JDK 1.6 13 | */ 14 | @Target(value={ElementType.FIELD}) 15 | @Retention(value = RetentionPolicy.RUNTIME) 16 | public @interface ZkValue { 17 | /** 18 | * 配置文件key 19 | * @return 20 | */ 21 | String key(); 22 | } 23 | -------------------------------------------------------------------------------- /src/test/java/com/lashou/v/zkconfig/TestFileWatch.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig; 2 | 3 | import com.lashou.v.zkconfig.dirwatch.FileWatch; 4 | import com.lashou.v.zkconfig.dirwatch.FileWatchProcessorImpl; 5 | 6 | public class TestFileWatch { 7 | 8 | public static void main(String[] args) throws InterruptedException { 9 | FileWatch watch = FileWatch.buildWatch(); 10 | FileWatchProcessorImpl processor = new FileWatchProcessorImpl(); 11 | 12 | watch.runWatch("d:/test/v-cs/db.properties", processor); 13 | 14 | // Thread.sleep(5000); 15 | // watch.stopWatch(); 16 | 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/annotaion/ZkConfig.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.annotaion; 2 | 3 | import java.lang.annotation.ElementType; 4 | import java.lang.annotation.Retention; 5 | import java.lang.annotation.RetentionPolicy; 6 | import java.lang.annotation.Target; 7 | 8 | /** 9 | * 此注解用于配置客户端连接服务器的参数信息 10 | */ 11 | @Target(value = { ElementType.TYPE })//用于类 12 | @Retention(value = RetentionPolicy.RUNTIME)//注解一直保留到运行时 13 | public @interface ZkConfig { 14 | /** 15 | * zookeeper服务器连接信息,如果是集群就配置集群格式的连接字符串 16 | * @return 17 | */ 18 | String hosts(); 19 | /** 20 | * session的超时时间,单位毫秒,默认值5000 21 | * @return 22 | */ 23 | int sessionTimeout() default 5000; 24 | 25 | } 26 | -------------------------------------------------------------------------------- /src/test/java/com/lashou/v/zkconfig/TestRegisterCenter.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig; 2 | 3 | import com.lashou.v.zkconfig.common.ElementCollection; 4 | import com.lashou.v.zkconfig.common.RegisterCollection; 5 | import com.lashou.v.zkconfig.core.BeanRegisterCenter; 6 | 7 | public class TestRegisterCenter { 8 | 9 | public static void main(String[] args) { 10 | TestObj instance = new TestObj();//实例化对象 11 | // Class clazz = instance.getClass(); 12 | 13 | BeanRegisterCenter brc = new BeanRegisterCenter(); 14 | brc.register(instance); 15 | 16 | System.out.println(RegisterCollection.getRegisterCollection()); 17 | System.out.println(ElementCollection.getElementCollection()); 18 | 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /.settings/org.eclipse.jdt.core.prefs: -------------------------------------------------------------------------------- 1 | eclipse.preferences.version=1 2 | org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled 3 | org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6 4 | org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve 5 | org.eclipse.jdt.core.compiler.compliance=1.6 6 | org.eclipse.jdt.core.compiler.debug.lineNumber=generate 7 | org.eclipse.jdt.core.compiler.debug.localVariable=generate 8 | org.eclipse.jdt.core.compiler.debug.sourceFile=generate 9 | org.eclipse.jdt.core.compiler.problem.assertIdentifier=error 10 | org.eclipse.jdt.core.compiler.problem.enumIdentifier=error 11 | org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning 12 | org.eclipse.jdt.core.compiler.source=1.6 13 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/common/ZkCconfigConstant.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.common; 2 | 3 | import java.nio.charset.Charset; 4 | 5 | /** 6 | * 系统常量 7 | * ClassName: ZkCconfigConstant
8 | * Function: TODO ADD FUNCTION.
9 | * Date: 2015年4月14日 下午4:38:48
10 | * 11 | * @author QuanZhong 12 | * @version 13 | * @since JDK 1.6 14 | */ 15 | public class ZkCconfigConstant { 16 | 17 | /** 18 | * 系统配置zk服务上的根节点 19 | */ 20 | public static final String ROOT_NODE = "/zkconfig"; 21 | /** 22 | * zk节点数据默认值 23 | */ 24 | public static final String DEFAULT_NODE_DATA = "-1"; 25 | /** 26 | * 系统默认字符编码 27 | */ 28 | public static final Charset CHARSET = Charset.forName("UTF-8"); 29 | } 30 | -------------------------------------------------------------------------------- /src/test/java/com/lashou/v/zkconfig/TestConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import org.apache.zookeeper.KeeperException; 8 | 9 | import com.lashou.v.zkconfig.springplugin.ZkConfigScannerConfigurer; 10 | 11 | public class TestConfigurer { 12 | 13 | public static void main(String[] args) throws IOException, InterruptedException, KeeperException { 14 | List confInstances = new ArrayList(); 15 | confInstances.add(new TestObj()); 16 | ZkConfigScannerConfigurer zk = new ZkConfigScannerConfigurer(); 17 | zk.setMonitorType(2); 18 | zk.setConfInstances(confInstances); 19 | zk.startWatch(); 20 | 21 | 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/common/RegisterCollection.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.common; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | 5 | /** 6 | * 保存配置类注册信息 7 | */ 8 | public class RegisterCollection extends ConcurrentHashMap> { 9 | /** 10 | * 11 | */ 12 | private static final long serialVersionUID = -2708705056582718018L; 13 | 14 | private static RegisterCollection instanceCollection = null; 15 | 16 | private RegisterCollection(){ 17 | } 18 | /** 19 | * 获取已注册的配置类集合对象key:对象实例;value class。 20 | * @return 21 | */ 22 | public static RegisterCollection getRegisterCollection(){ 23 | if(instanceCollection == null){ 24 | synchronized (RegisterCollection.class) { 25 | instanceCollection = new RegisterCollection(); 26 | } 27 | } 28 | return instanceCollection; 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/test/java/com/lashou/v/zkconfig/TestObj.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig; 2 | 3 | import com.lashou.v.zkconfig.annotaion.ZkValue; 4 | 5 | //@ZkConfig(hosts = "127.0.0.1:2181") 6 | public class TestObj { 7 | 8 | @ZkValue(key = "ip") 9 | private String ip; 10 | @ZkValue(key = "port") 11 | private int port; 12 | private String sth; 13 | 14 | public String getIp() { 15 | return ip; 16 | } 17 | 18 | public void setIp(String ip) { 19 | this.ip = ip; 20 | } 21 | 22 | public int getPort() { 23 | return port; 24 | } 25 | 26 | public void setPort(int port) { 27 | this.port = port; 28 | } 29 | 30 | public String getSth() { 31 | return sth; 32 | } 33 | 34 | public void setSth(String sth) { 35 | this.sth = sth; 36 | } 37 | 38 | @Override 39 | public String toString() { 40 | return "TestObj [ip=" + ip + ", port=" + port + ", sth=" + sth + "]"; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/common/ElementCollection.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.common; 2 | 3 | import java.util.concurrent.ConcurrentHashMap; 4 | 5 | import com.lashou.v.zkconfig.bean.ConfBeanMetaData; 6 | 7 | /** 8 | * 获取已注册的配置属性集合key:属性名称;value:对象实例(instanceCollection中key的值) 9 | */ 10 | public class ElementCollection extends ConcurrentHashMap { 11 | /** 12 | * 13 | */ 14 | private static final long serialVersionUID = -2708705056582718018L; 15 | 16 | private static ElementCollection elementCollection = null; 17 | 18 | private ElementCollection(){ 19 | } 20 | /** 21 | * 获取已注册的配置属性集合,key:ZkConfig中标记的名称;value:对象实例(RegisterCollection中key的值) 22 | * @return 23 | */ 24 | public static ElementCollection getElementCollection(){ 25 | if(elementCollection == null){ 26 | synchronized (ElementCollection.class) { 27 | elementCollection = new ElementCollection(); 28 | } 29 | } 30 | return elementCollection; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/monitor/MonitorFactory.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.monitor; 2 | 3 | public class MonitorFactory { 4 | public enum MonitorType{ 5 | CONF_FILE_MONITOR(1), 6 | CONF_PROPERTIES_FILE_MONITOR(2), 7 | CONF_XML_FILE_MONITOR(3), 8 | NONE(0); 9 | 10 | private final int intValue; 11 | MonitorType(int intValue){ 12 | this.intValue = intValue; 13 | } 14 | public int getIntValue() { 15 | return intValue; 16 | } 17 | public static MonitorType getMonitorType(int value){ 18 | for(MonitorType type : MonitorType.values()){ 19 | if(value == type.getIntValue()){ 20 | return type; 21 | } 22 | } 23 | return MonitorType.NONE; 24 | } 25 | 26 | } 27 | public static Monitor getMonitor(MonitorType type){ 28 | 29 | switch(type.intValue){ 30 | case 1:return new ConfFileMonitor(); 31 | case 2:return new PropertiesFileMonitor(); 32 | default: throw new RuntimeException("MonitorType暂未实现!type="+type.intValue); 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/dirwatch/FileWatchProcessorImpl.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.dirwatch; 2 | 3 | import java.io.File; 4 | 5 | import org.apache.log4j.Logger; 6 | 7 | import com.lashou.v.zkconfig.common.ActiveKeyValueStore; 8 | import com.lashou.v.zkconfig.common.FileUtils; 9 | 10 | public class FileWatchProcessorImpl implements FileWatchProcessor { 11 | static Logger logger = Logger.getLogger(FileWatchProcessorImpl.class); 12 | protected ActiveKeyValueStore store; 13 | @Override 14 | public Object process(File file) { 15 | try { 16 | logger.info("process file :"+file.getAbsolutePath()); 17 | //读取配置文件数据,写入zk服务器。 18 | byte[] data = FileUtils.readFile(file); 19 | String path = FileUtils.getFriendlyPath(file.getAbsolutePath()); 20 | this.store.writeByte(FileUtils.getZkPathByConfigPath(path), data); 21 | } catch (Exception e) { 22 | logger.error("更新变更到zk出错。",e); 23 | } 24 | return file; 25 | } 26 | public ActiveKeyValueStore getStore() { 27 | return store; 28 | } 29 | public void setStore(ActiveKeyValueStore store) { 30 | this.store = store; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/test/java/com/lashou/v/zkconfig/TestAnnotation.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig; 2 | 3 | import java.lang.reflect.Field; 4 | 5 | import com.lashou.v.zkconfig.annotaion.ZkConfig; 6 | 7 | public class TestAnnotation { 8 | 9 | public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException{ 10 | TestObj testObj = new TestObj();//实例化对象 11 | 12 | Class c = testObj.getClass(); 13 | 14 | //1、读取类配置 15 | boolean isAnnPres = c.isAnnotationPresent(ZkConfig.class); 16 | if(isAnnPres){ 17 | ZkConfig zkconfig = c.getAnnotation(ZkConfig.class); 18 | String hosts = zkconfig.hosts(); 19 | int session = zkconfig.sessionTimeout(); 20 | System.out.println(hosts+"="+session); 21 | } 22 | //2\读取属性配置 23 | Field[] fielsd = c.getDeclaredFields(); 24 | for(Field field : fielsd){ 25 | field.setAccessible(true); 26 | field.getType(); 27 | // field.set(testObj, 1); 28 | System.out.println(field.getType()+",fieldName="+field.getName()+",filedValue="+field.get(testObj)); 29 | } 30 | //3\读取方法配置 31 | 32 | System.out.println(Boolean.parseBoolean("true")); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/test/java/com/lashou/v/zkconfig/ConfigUpdater.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig; 2 | 3 | import java.io.IOException; 4 | import java.util.Random; 5 | import java.util.concurrent.TimeUnit; 6 | 7 | import org.apache.zookeeper.KeeperException; 8 | 9 | import com.lashou.v.zkconfig.common.ActiveKeyValueStore; 10 | 11 | public class ConfigUpdater { 12 | public static final String PATH = "/config"; 13 | 14 | private ActiveKeyValueStore store; 15 | private Random random = new Random(); 16 | public ConfigUpdater(String hosts) throws IOException, InterruptedException{ 17 | store = new ActiveKeyValueStore(); 18 | store.connect(hosts); 19 | } 20 | 21 | public void run() throws KeeperException, InterruptedException{ 22 | while(true){ 23 | String value = random.nextInt(100)+""; 24 | store.write(PATH, value); 25 | System.out.printf("Set %s to %s\n",PATH,value); 26 | TimeUnit.SECONDS.sleep(random.nextInt(10)); 27 | } 28 | } 29 | 30 | public static void main(String[] args) throws IOException, InterruptedException, KeeperException { 31 | String hosts = "127.0.0.1:2181"; 32 | ConfigUpdater configUpdater = new ConfigUpdater(hosts); 33 | configUpdater.run(); 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/common/SystemInit.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.common; 2 | 3 | import org.apache.zookeeper.CreateMode; 4 | import org.apache.zookeeper.KeeperException; 5 | import org.apache.zookeeper.ZooDefs.Ids; 6 | import org.apache.zookeeper.data.Stat; 7 | 8 | /** 9 | * 10 | 执行部分系统参数的初始化工作 11 | * @author QuanZhong 12 | * @version 13 | * @since JDK 1.6 14 | */ 15 | public class SystemInit { 16 | 17 | /** 18 | * 系统数据初始化工作 19 | * @param store 20 | * @throws KeeperException 21 | * @throws InterruptedException 22 | */ 23 | public static void init(ActiveKeyValueStore store) throws KeeperException, InterruptedException{ 24 | initRootNode(store); 25 | 26 | } 27 | /** 28 | * 初始化系统根节点 29 | * @param store 30 | * @throws KeeperException 31 | * @throws InterruptedException 32 | */ 33 | public static void initRootNode(ActiveKeyValueStore store) throws KeeperException, InterruptedException{ 34 | Stat stat = store.zk.exists(ZkCconfigConstant.ROOT_NODE, false); 35 | if(stat == null){ 36 | store.zk.create(ZkCconfigConstant.ROOT_NODE, ZkCconfigConstant.DEFAULT_NODE_DATA.getBytes(ZkCconfigConstant.CHARSET), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | v-zkconfig 4 | 5 | 6 | 7 | 8 | 9 | org.eclipse.wst.common.project.facet.core.builder 10 | 11 | 12 | 13 | 14 | org.eclipse.jdt.core.javabuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.wst.validation.validationbuilder 20 | 21 | 22 | 23 | 24 | org.springframework.ide.eclipse.core.springbuilder 25 | 26 | 27 | 28 | 29 | org.eclipse.m2e.core.maven2Builder 30 | 31 | 32 | 33 | 34 | 35 | org.springframework.ide.eclipse.core.springnature 36 | org.eclipse.jem.workbench.JavaEMFNature 37 | org.eclipse.wst.common.modulecore.ModuleCoreNature 38 | org.eclipse.jdt.core.javanature 39 | org.eclipse.m2e.core.maven2Nature 40 | org.eclipse.wst.common.project.facet.core.nature 41 | 42 | 43 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/dirwatch/PropertiesFileWatchProcessorImpl.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.dirwatch; 2 | 3 | import java.io.File; 4 | import java.util.Properties; 5 | import java.util.Set; 6 | 7 | import org.apache.log4j.Logger; 8 | 9 | import com.lashou.v.zkconfig.common.ActiveKeyValueStore; 10 | import com.lashou.v.zkconfig.common.FileUtils; 11 | import com.lashou.v.zkconfig.common.ZkConfigParser; 12 | 13 | public class PropertiesFileWatchProcessorImpl implements FileWatchProcessor { 14 | static Logger logger = Logger.getLogger(PropertiesFileWatchProcessorImpl.class); 15 | protected ActiveKeyValueStore store; 16 | @Override 17 | public Object process(File file) { 18 | try { 19 | logger.info("process file :"+file.getAbsolutePath()); 20 | String zkPath = FileUtils.getZkPathByConfigPath(FileUtils.getFriendlyPath(file.getAbsolutePath())); 21 | //读取配置文件数据,写入zk服务器。 22 | Properties prop = ZkConfigParser.parserFile(file.getAbsolutePath()); 23 | if(prop != null){ 24 | Set set = prop.keySet(); 25 | for(Object key : set){ 26 | String value = prop.getProperty((String) key); 27 | String zkValue = this.store.readStringWithoutWatcher(zkPath+"/"+key); 28 | if(!value.equals(zkValue)){//发生变更,则更新 29 | this.store.updatePathWithOutACL(zkPath+"/"+key, value); 30 | } 31 | } 32 | 33 | } 34 | } catch (Exception e) { 35 | logger.error("更新变更到zk出错。",e); 36 | } 37 | return file; 38 | } 39 | public ActiveKeyValueStore getStore() { 40 | return store; 41 | } 42 | public void setStore(ActiveKeyValueStore store) { 43 | this.store = store; 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/log4j.properties: -------------------------------------------------------------------------------- 1 | # 2 | # ZooKeeper Logging Configuration 3 | # 4 | 5 | # Format is " (, )+ 6 | 7 | # DEFAULT: console appender only 8 | log4j.rootLogger=INFO, CONSOLE 9 | 10 | # Example with rolling log file 11 | #log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE 12 | 13 | # Example with rolling log file and tracing 14 | #log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE 15 | 16 | # 17 | # Log INFO level and above messages to the console 18 | # 19 | log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender 20 | log4j.appender.CONSOLE.Threshold=INFO 21 | log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout 22 | log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n 23 | 24 | # 25 | # Add ROLLINGFILE to rootLogger to get log file output 26 | # Log DEBUG level and above messages to a log file 27 | log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender 28 | log4j.appender.ROLLINGFILE.Threshold=DEBUG 29 | log4j.appender.ROLLINGFILE.File=zookeeper.log 30 | 31 | # Max log file size of 10MB 32 | log4j.appender.ROLLINGFILE.MaxFileSize=10MB 33 | # uncomment the next line to limit number of backup files 34 | #log4j.appender.ROLLINGFILE.MaxBackupIndex=10 35 | 36 | log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout 37 | log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n 38 | 39 | 40 | # 41 | # Add TRACEFILE to rootLogger to get log file output 42 | # Log DEBUG level and above messages to a log file 43 | log4j.appender.TRACEFILE=org.apache.log4j.FileAppender 44 | log4j.appender.TRACEFILE.Threshold=TRACE 45 | log4j.appender.TRACEFILE.File=zookeeper_trace.log 46 | 47 | log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout 48 | ### Notice we are including log4j's NDC here (%x) 49 | log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n 50 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/monitor/Monitor.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.monitor; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.List; 6 | 7 | import org.apache.zookeeper.KeeperException; 8 | 9 | import com.lashou.v.zkconfig.common.ActiveKeyValueStore; 10 | 11 | public interface Monitor { 12 | 13 | /** 14 | * 执行此方法来注册事件监听 15 | * @throws KeeperException 16 | * @throws InterruptedException 17 | */ 18 | public void displayValue() throws KeeperException, InterruptedException; 19 | /** 20 | * 获取所有的系统配置文件,考虑到后期目录监控的便利以及配置的简单性,暂时不支持获取子目录中文件 21 | * @return 22 | */ 23 | public List getAllConfigFiles(); 24 | /** 25 | * 获取所有的系统配置文件路径 26 | * @return 27 | */ 28 | public List getAllConfigPaths(); 29 | 30 | /** 31 | * 通过配置文件路径,获取zk服务器上的配置路径 32 | * @param path 33 | * @return 34 | */ 35 | public String getZkPathByConfigPath(String path); 36 | /** 37 | * 通过zkpath获取配置文件路径 38 | * @param zkpath 39 | * @return 40 | */ 41 | public String getConfigPathByZkPath(String zkpath); 42 | /** 43 | * 执行monitor业务方法 44 | */ 45 | public void monitor(ActiveKeyValueStore store, String filePath); 46 | /** 47 | * 读取磁盘中配置文件内容 48 | * @param file 49 | * @return 50 | */ 51 | public byte[] readFile(File file)throws IOException; 52 | /** 53 | * 想磁盘写入文件内容 54 | * @param data 55 | * @param filePath 56 | * @return 57 | */ 58 | public int writeFile(byte[] data,String filePath); 59 | /** 60 | * 初始化,可根据需要具体实现 61 | * @param store 62 | * @param filePath 63 | */ 64 | public void init(ActiveKeyValueStore store, String filePath) throws KeeperException, InterruptedException ; 65 | /** 66 | * 初始化,将配置文件信息读入zk节点,可根据需要具体实现 67 | */ 68 | public void initDataToZk(ActiveKeyValueStore store, String filePath) throws KeeperException, InterruptedException, IOException; 69 | /** 70 | * 监听磁盘配置文件变动 71 | */ 72 | public void watchFiles(String filePath); 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/bean/ConfBeanMetaData.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.bean; 2 | 3 | import java.io.Serializable; 4 | 5 | /** 6 | * 配置属性元数据类 7 | * ClassName: ConfBeanMetaData
8 | * Function: TODO ADD FUNCTION.
9 | * Date: 2015年4月15日 上午10:00:29
10 | * 11 | * @author QuanZhong 12 | * @version 13 | * @since JDK 1.6 14 | */ 15 | public class ConfBeanMetaData implements Serializable { 16 | 17 | /** 18 | * 19 | */ 20 | private static final long serialVersionUID = 2410106113334309756L; 21 | 22 | /** 23 | * 配置文件key值 24 | */ 25 | private String key; 26 | /** 27 | * 配置类,实体对象 28 | */ 29 | private Object instance; 30 | /** 31 | * 配置类(实体对象)属性名 32 | */ 33 | private String fieldName; 34 | /** 35 | * 配置类(实体对象)当前属性值 36 | */ 37 | private Object fieldValue; 38 | /** 39 | * 配置类(实体对象)当前属性类型 40 | */ 41 | private Class fieldType; 42 | 43 | 44 | public ConfBeanMetaData() { 45 | super(); 46 | } 47 | 48 | public ConfBeanMetaData(Object instance,String key, String fieldName, Object fieldValue, 49 | Class fieldType) { 50 | super(); 51 | this.instance = instance; 52 | this.key = key; 53 | this.fieldName = fieldName; 54 | this.fieldValue = fieldValue; 55 | this.fieldType = fieldType; 56 | } 57 | 58 | public String getKey() { 59 | return key; 60 | } 61 | public void setKey(String key) { 62 | this.key = key; 63 | } 64 | public String getFieldName() { 65 | return fieldName; 66 | } 67 | public void setFieldName(String fieldName) { 68 | this.fieldName = fieldName; 69 | } 70 | public Object getFieldValue() { 71 | return fieldValue; 72 | } 73 | public void setFieldValue(Object fieldValue) { 74 | this.fieldValue = fieldValue; 75 | } 76 | public Class getFieldType() { 77 | return fieldType; 78 | } 79 | public void setFieldType(Class fieldType) { 80 | this.fieldType = fieldType; 81 | } 82 | 83 | public Object getInstance() { 84 | return instance; 85 | } 86 | 87 | public void setInstance(Object instance) { 88 | this.instance = instance; 89 | } 90 | 91 | @Override 92 | public String toString() { 93 | return "ConfBeanMetaData [key=" + key + ", instance=" + instance 94 | + ", fieldName=" + fieldName + ", fieldValue=" + fieldValue 95 | + ", fieldType=" + fieldType + "]"; 96 | } 97 | 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/test/java/com/lashou/v/zkconfig/ConfigWatcher.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig; 2 | 3 | import java.io.IOException; 4 | import java.util.List; 5 | import java.util.concurrent.CountDownLatch; 6 | 7 | import org.apache.zookeeper.KeeperException; 8 | import org.apache.zookeeper.WatchedEvent; 9 | import org.apache.zookeeper.Watcher; 10 | import org.apache.zookeeper.Watcher.Event.EventType; 11 | 12 | import com.lashou.v.zkconfig.common.ActiveKeyValueStore; 13 | 14 | public class ConfigWatcher implements Watcher { 15 | 16 | private ActiveKeyValueStore store; 17 | 18 | public ConfigWatcher() throws IOException, InterruptedException, KeeperException{ 19 | store = new ActiveKeyValueStore(); 20 | store.connect(); 21 | 22 | } 23 | 24 | public ConfigWatcher(String hosts) throws IOException, InterruptedException, KeeperException{ 25 | store = new ActiveKeyValueStore(); 26 | store.connect(hosts); 27 | 28 | } 29 | public void displayConfig() throws KeeperException, InterruptedException{ 30 | String value = store.read(ConfigUpdater.PATH, this); 31 | System.out.printf("Read %s as %s\n", ConfigUpdater.PATH,value); 32 | List children = store.getChildren(ConfigUpdater.PATH, 33 | new Watcher(){ 34 | 35 | @Override 36 | public void process(WatchedEvent event) { 37 | if(event.getType() == Watcher.Event.EventType.NodeDataChanged){//当前节点数据发生变化 38 | System.out.println("子节点发生了变化1,"+event.getPath()); 39 | }else if(event.getType() == EventType.NodeChildrenChanged){ 40 | System.out.println("子节点发生了变化2,"+event.getPath()); 41 | 42 | } 43 | 44 | } 45 | 46 | } 47 | ); 48 | System.out.printf("Read %s children nodes as %s\n", ConfigUpdater.PATH,children); 49 | } 50 | @Override 51 | public void process(WatchedEvent event) { 52 | if(event.getType() == Watcher.Event.EventType.NodeDataChanged){//当前节点数据发生变化 53 | try { 54 | this.displayConfig(); 55 | } catch (KeeperException e) { 56 | e.printStackTrace(); 57 | } catch (InterruptedException e) { 58 | e.printStackTrace(); 59 | } 60 | }else if(event.getType() == EventType.NodeChildrenChanged){ 61 | System.out.println("子节点发生了变化0,"+event.getPath()); 62 | } 63 | 64 | } 65 | 66 | public static void main(String[] args) throws IOException, InterruptedException, KeeperException { 67 | CountDownLatch connectedSignal = new CountDownLatch(1); 68 | 69 | // String hosts = "127.0.0.1:2181"; 70 | ConfigWatcher configWatcher = new ConfigWatcher(); 71 | configWatcher.displayConfig(); 72 | connectedSignal.await(); 73 | 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/springplugin/spring-example.xml: -------------------------------------------------------------------------------- 1 | 2 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | testConfig1 26 | testConfig2 27 | 28 | 29 | 30 | 31 | 32 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/reademe.txt: -------------------------------------------------------------------------------- 1 | ZkConfig使用说明 2 | 3 | 一、简介 4 | ZkConfig是为zookeeper开发的配置服务工具包,能与现有的Java系统进行良好的集成,也可以使用与非java系统以独立进程运行。提供与spring进行集成的插件。采用注解方式对需要动态更新的内存数据对象进行标注。 5 | ZkConfig用于解决在系统集群中配置文件的实时同步。当任意一台服务器配置文件发生变化的时候,所有集群服务器配置文件实现同步更新,并且在不启动web容器的情况下,实现内存配置对象的实时更新。 6 | 目前支持所有种类配置文件的同步更新,仅支持扩展名为.properties与.cfg结尾的健值对文件格式的内存数据对象实时同步。其余配置文件仅支持磁盘数据同步。 7 | 二、使用说明 8 | 使用步骤: 9 | 1、增加虚拟机启动参数配置 10 | 运行zkconfig需要增加虚拟机运行参数 app.instance.config 的配置。app.instance.config 值为配置文件目录,将zkconfig.properties文件置于该目录下。 11 | 2、为需要动态更新的配置类对象增加@ZkValue注解 12 | 例: 13 | //@ZkConfig(hosts = "127.0.0.1:2181") 14 | public class TestObj { 15 | 16 | @ZkValue(key = "ip") 17 | private String ip; 18 | @ZkValue(key = "port") 19 | private int port; 20 | private String sth; 21 | 22 | //getter,setter略 23 | } 24 | @ZkValue注解中key值为配置properties文件中的key值,仅仅有此注解的属性会被实时更新。 25 | @ZkConfig注解与配置对象上,非必须,这里配置zookeeper服务器连接信息,此配置可以配置与zkconfig.properties文件中。 26 | 3、配置zkconfig.properties文件 27 | 将zkconfig.properties文件复制到虚拟机运行参数 app.instance.config 所配置的目录下。 28 | hosts=127.0.0.1:2181 29 | Zookeeper集群连接字符串,以逗号分隔eg:127.0.0.1:2181,127.0.0.1:2182,127.0.0.1:2183 30 | sessionTimeout=5000 31 | 会话超时时间 32 | app_name=cs 33 | 当前系统的应用名称,此项必填用于区分不同系统的配置文件 34 | app_config_paths=${app.instance.config} 35 | 需要同步的系统配置文件目录集合,以逗号分隔eg:/data/config/v-crm,/data/config/v-cs 36 | dir_watch_period=3000 37 | 文件目录监控频率单位毫秒,用于监控配置文件的变化 38 | 4、向ZkConfig注册内存配置对象实例(这里注册是实例,不是类型) 39 | //创建注册中心 40 | BeanRegisterCenter brc = new BeanRegisterCenter(); 41 | //注册实例,有多个就注册多个 42 | brc.register(new TestObj()); 43 | 此步骤非必须,如果不想更新内存数据,此步骤与步骤2都可以忽略。 44 | 5、启动数据监听 45 | //2、执行监听 46 | MonitorType type = MonitorFactory.MonitorType.CONF_PROPERTIES_FILE_MONITOR; 47 | ZkConfigExecutor.execute(type); 48 | MonitorType 为监听类型枚举,目前支持通用文件类型监听以及Properties配置文件监听,后续可二次扩展。 49 | 示范代码(方式1): 50 | /** 51 | * run local example 52 | * @param args 53 | */ 54 | public static void main(String[] args) { 55 | CountDownLatch connectedSignal = new CountDownLatch(1); 56 | try { 57 | //1、注册配置所需配置类 58 | BeanRegisterCenter brc = new BeanRegisterCenter();//执行监听 59 | brc.register(new TestObj()); 60 | //2、执行监听 61 | MonitorType type = MonitorFactory.MonitorType.CONF_PROPERTIES_FILE_MONITOR; 62 | ZkConfigExecutor.execute(type); 63 | 64 | connectedSignal.await(); 65 | } catch (Exception e) { 66 | e.printStackTrace(); 67 | } 68 | } 69 | 也可以采用方式二运行 70 | 71 | public static void main(String[] args) throws IOException, InterruptedException, KeeperException { 72 | List confInstances = new ArrayList(); 73 | confInstances.add(new TestObj()); 74 | ZkConfigScannerConfigurer zk = new ZkConfigScannerConfigurer(); 75 | zk.setMonitorType(2); 76 | zk.setConfInstances(confInstances); 77 | zk.startWatch(); 78 | 79 | 80 | } 81 | 82 | 三、与spring集成 83 | 在spring.xml配置文件中加入如下配置即可实现集成: 84 | 85 | 86 | 87 | 88 | testConfig 89 | 90 | 91 | 92 | 93 | 94 | testConfig为内存数据同步对象。confInstanceNames为内存同步对象名称集合。monitorType为监听文件类型,1:通用文件类型;2、properties文件类型。 95 | 96 | 四、注意事项 97 | 在于spring集成时候,内存配置对象尽量采用配置文件的方式,因spring在类实例化时候会优先实例化配置文件,其次是注解方式。如果延迟实例化可能会导致无法获取的内存配置对象实例。 -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/common/ConnectionWatcher.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.common; 2 | 3 | import java.io.IOException; 4 | import java.util.concurrent.CountDownLatch; 5 | 6 | import org.apache.log4j.Logger; 7 | import org.apache.zookeeper.WatchedEvent; 8 | import org.apache.zookeeper.Watcher; 9 | import org.apache.zookeeper.ZooKeeper; 10 | 11 | import com.lashou.v.zkconfig.annotaion.ZkConfig; 12 | import com.lashou.v.zkconfig.core.BeanRegisterCenter; 13 | 14 | /** 15 | * 用于等待建立zookeeper连接的辅助类 16 | @author QuanZhong 17 | * @version 18 | * @since JDK 1.6 19 | */ 20 | public class ConnectionWatcher implements Watcher { 21 | protected Logger logger = Logger.getLogger(ConnectionWatcher.class); 22 | /** 23 | * 会话超时时间 24 | */ 25 | private static int sessionTimeout = 5000; 26 | private static String hosts; 27 | protected ZooKeeper zk; 28 | private CountDownLatch connectedSignal = new CountDownLatch(1); 29 | 30 | public ConnectionWatcher(){ 31 | //优先读取注解配置信息 32 | BeanRegisterCenter registerCenter = new BeanRegisterCenter(); 33 | if(registerCenter.hasZkConfigAnnotationed()){ 34 | ZkConfig zkConfig = registerCenter.getZkConfig(); 35 | if(zkConfig != null){ 36 | hosts = zkConfig.hosts(); 37 | sessionTimeout = zkConfig.sessionTimeout(); 38 | } 39 | } 40 | //其次读取配置文件配置信息 41 | if(hosts == null){ 42 | hosts = ZkConfigParser.getProperty("hosts"); 43 | String sto = ZkConfigParser.getProperty("sessionTimeout"); 44 | if(sto != null && !"".equals(sto.trim())){ 45 | try { 46 | sessionTimeout = Integer.parseInt(sto); 47 | } catch (NumberFormatException e) { 48 | logger.error("zkconfig.properties中配置参数sessionTimeout有误!",e); 49 | } 50 | } 51 | } 52 | 53 | } 54 | 55 | /** 56 | * zk连接字符串 ip:poort(指定主机的方式连接,此方式不会修改默认配置方式,单独对此次连接管用) 57 | * @param hosts 58 | * @throws IOException 59 | * @throws InterruptedException 60 | */ 61 | public void connect(String hosts) throws IOException, InterruptedException{ 62 | zk = new ZooKeeper(hosts,sessionTimeout,this); 63 | connectedSignal.await();//利用锁存器来等待连接成功,(只有连接成功后续zk对象才能正常使用) 64 | 65 | } 66 | /** 67 | * zk连接 需要提前配置hosts,如果hosts为空将异常 68 | * @param hosts 69 | * @throws IOException 70 | * @throws InterruptedException 71 | */ 72 | public void connect() throws IOException, InterruptedException{ 73 | zk = new ZooKeeper(hosts,sessionTimeout,this); 74 | connectedSignal.await();//利用锁存器来等待连接成功,(只有连接成功后续zk对象才能正常使用) 75 | 76 | } 77 | 78 | @Override 79 | public void process(WatchedEvent event) { 80 | logger.info("method process be called in class ConnectionWatcher."); 81 | if(event.getState() == Watcher.Event.KeeperState.SyncConnected){ 82 | logger.info("The client connect to the zookeeper server successfully!"); 83 | connectedSignal.countDown(); 84 | } 85 | } 86 | 87 | public void close() throws InterruptedException{ 88 | zk.close(); 89 | } 90 | 91 | public static String getHosts() { 92 | return hosts; 93 | } 94 | 95 | public static void setHosts(String hosts) { 96 | ConnectionWatcher.hosts = hosts; 97 | } 98 | 99 | public static int getSessionTimeout() { 100 | return sessionTimeout; 101 | } 102 | 103 | public static void setSessionTimeout(int sessionTimeout) { 104 | ConnectionWatcher.sessionTimeout = sessionTimeout; 105 | } 106 | 107 | } 108 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/common/ActiveKeyValueStore.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.common; 2 | 3 | import java.nio.charset.Charset; 4 | import java.util.List; 5 | 6 | import org.apache.zookeeper.CreateMode; 7 | import org.apache.zookeeper.KeeperException; 8 | import org.apache.zookeeper.Watcher; 9 | import org.apache.zookeeper.ZooDefs.Ids; 10 | import org.apache.zookeeper.data.Stat; 11 | 12 | public class ActiveKeyValueStore extends ConnectionWatcher { 13 | public static final Charset CHARSET = ZkCconfigConstant.CHARSET; 14 | 15 | public void write(String path,String value) throws KeeperException, InterruptedException{ 16 | Stat stat = zk.exists(path, false); 17 | if(stat == null){ 18 | zk.create(path, value.getBytes(CHARSET), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 19 | }else{ 20 | zk.setData(path, value.getBytes(CHARSET), -1); 21 | } 22 | } 23 | public String read(String path, Watcher watch) throws KeeperException, InterruptedException{ 24 | byte[] data = zk.getData(path, watch, null/**stat*/); 25 | return new String(data,0,data.length,CHARSET); 26 | 27 | } 28 | 29 | public void writeByte(String path,byte[] data) throws KeeperException, InterruptedException{ 30 | String t = new String(data,0,data.length,CHARSET); 31 | logger.info(t+"abc"); 32 | Stat stat = zk.exists(path, false); 33 | if(stat == null){ 34 | zk.create(path, data, Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 35 | }else{ 36 | zk.setData(path, data, -1); 37 | } 38 | } 39 | public byte[] readByte(String path, Watcher watch) throws KeeperException, InterruptedException{ 40 | byte[] data = zk.getData(path, watch, null); 41 | return data; 42 | } 43 | public List getChildren(final String path, Watcher watcher) throws KeeperException, InterruptedException{ 44 | return zk.getChildren(path, watcher); 45 | } 46 | public void register(Watcher watcher){ 47 | zk.register(watcher); 48 | } 49 | public byte[] readByteWithoutWatcher(String path) throws KeeperException, InterruptedException{ 50 | byte[] data = zk.getData(path, false, null); 51 | return data; 52 | } 53 | public String readStringWithoutWatcher(String path) throws KeeperException, InterruptedException{ 54 | byte[] data = zk.getData(path, false, null); 55 | return new String(data,0,data.length,CHARSET); 56 | } 57 | public void createPathWithOutACL(String zkpath) throws KeeperException, InterruptedException{ 58 | zk.create(zkpath,ZkCconfigConstant.DEFAULT_NODE_DATA.getBytes(ZkCconfigConstant.CHARSET), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 59 | } 60 | public void updatePathWithOutACL(String zkpath, String value) throws KeeperException, InterruptedException{ 61 | Stat stat = zk.exists(zkpath, false); 62 | if(stat == null){ 63 | zk.create(zkpath,value.getBytes(CHARSET), Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT); 64 | }else{ 65 | zk.setData(zkpath, value.getBytes(CHARSET), -1); 66 | } 67 | } 68 | /** 69 | * 按照path目录递归初始化节点,默认数据-1; 70 | * @param zkpath 71 | * @throws InterruptedException 72 | * @throws KeeperException 73 | */ 74 | public void initZkPath(String zkpath) throws KeeperException, InterruptedException{ 75 | if(zkpath!=null && !"".equals(zkpath.trim())){ 76 | String[] pathArray = zkpath.split("/"); 77 | if(pathArray != null && pathArray.length > 0){ 78 | String tmpPath = ""; 79 | for(String p : pathArray){ 80 | if(p != null && !"".equals(p.trim())){ 81 | tmpPath += "/"+p; 82 | Stat stat = zk.exists(tmpPath, false); 83 | if(stat == null){ 84 | this.createPathWithOutACL(tmpPath); 85 | } 86 | } 87 | 88 | } 89 | } 90 | } 91 | 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/common/ZkConfigParser.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.common; 2 | 3 | import java.io.ByteArrayInputStream; 4 | import java.io.DataInputStream; 5 | import java.io.File; 6 | import java.io.FileInputStream; 7 | import java.io.FileNotFoundException; 8 | import java.io.FileOutputStream; 9 | import java.io.IOException; 10 | import java.io.InputStream; 11 | import java.io.OutputStream; 12 | import java.util.Properties; 13 | 14 | import org.apache.log4j.Logger; 15 | 16 | /** 17 | * zkconfig.properties配置文件解析辅助类 18 | * 虚拟机需要配置app.instance.config启动参数,值为存放zkconfig.properties的文件目录 19 | * @author QuanZhong 20 | * @version 21 | * @since JDK 1.6 22 | */ 23 | public class ZkConfigParser { 24 | private static String appInstanceConfig; 25 | private static Logger logger = Logger.getLogger(ZkConfigParser.class); 26 | private static Properties config = new Properties(); 27 | static { 28 | String propPath = System.getProperty("app.instance.config"); 29 | if(propPath == null){ 30 | propPath = appInstanceConfig; 31 | } 32 | if(propPath == null){ 33 | logger.error("未设置虚拟机启动参数'app.instance.config'!"); 34 | throw new RuntimeException("未设置虚拟机启动参数'app.instance.config'!"); 35 | }else{ 36 | try { 37 | InputStream in = new FileInputStream(propPath+File.separator+"zkconfig.properties"); 38 | config.load(in); 39 | in.close(); 40 | } catch (FileNotFoundException e) { 41 | logger.error("路劲"+propPath+"下未找到配置文件zkconfig.properties!",e); 42 | } catch (IOException e) { 43 | logger.error("读取配置文件zkconfig.properties失败!路劲"+propPath,e); 44 | } 45 | } 46 | } 47 | public static String getProperty(String key){ 48 | return config.getProperty(key); 49 | } 50 | public static String getAppInstanceConfig() { 51 | return appInstanceConfig; 52 | } 53 | public static void setAppInstanceConfig(String appInstanceConfig) { 54 | ZkConfigParser.appInstanceConfig = appInstanceConfig; 55 | } 56 | public static String getAppConfigPaths(){ 57 | String value = config.getProperty("app_config_paths"); 58 | if(value == null || "".equals(value.trim()) || value.indexOf("app.instance.config") > 0){ 59 | value = System.getProperty("app.instance.config"); 60 | } 61 | if(value == null){ 62 | logger.warn("zkconfig.properties配置文件缺少参数app_config_paths,或者参数配置不正确!"); 63 | } 64 | return value; 65 | } 66 | public static Properties parserFile(String filePath){ 67 | Properties prop = new Properties(); 68 | try { 69 | InputStream in = new FileInputStream(filePath); 70 | prop.load(in); 71 | in.close(); 72 | return prop; 73 | }catch (Exception e) { 74 | logger.error("读取配置文件失败!filePath="+filePath,e); 75 | return null; 76 | } 77 | } 78 | 79 | public static Properties parserData(byte[] data){ 80 | Properties prop = new Properties(); 81 | try { 82 | ByteArrayInputStream input = new ByteArrayInputStream(data, 0, data.length); 83 | InputStream in = new DataInputStream(input); 84 | prop.load(in); 85 | in.close(); 86 | return prop; 87 | }catch (Exception e) { 88 | logger.error("数据读取失败!",e); 89 | return null; 90 | } 91 | } 92 | public static synchronized void updateProperties(String profilepath, String keyname,String keyvalue) { 93 | Properties props = new Properties(); 94 | try { 95 | InputStream in = new FileInputStream(profilepath); 96 | props.load(in); 97 | in.close(); 98 | OutputStream fos = new FileOutputStream(profilepath); 99 | props.setProperty(keyname, keyvalue); 100 | props.store(fos, null); 101 | fos.close(); 102 | 103 | } catch (Exception e) { 104 | logger.error("属性文件更新错误",e); 105 | } 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/dirwatch/FileWatch.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.dirwatch; 2 | 3 | import java.io.File; 4 | import java.io.FileNotFoundException; 5 | 6 | import org.apache.log4j.Logger; 7 | 8 | import com.lashou.v.zkconfig.common.ZkConfigParser; 9 | 10 | /** 11 | * 用于监视文件变动 12 | * 13 | * @author quanzhong 14 | * 15 | */ 16 | public class FileWatch { 17 | static Logger logger = Logger.getLogger(FileWatch.class); 18 | private long lm = 0; 19 | private boolean isRunning = false; 20 | private String cmd = ""; 21 | private boolean isUpdateFromZk = false; 22 | /** 23 | * 获取fileWatch实例 24 | * 25 | * @return 26 | */ 27 | public static FileWatch buildWatch() { 28 | return new FileWatch(); 29 | } 30 | 31 | public synchronized long getLastModified(){ 32 | return lm; 33 | } 34 | public synchronized void setLastModified(long lm){ 35 | this.lm = lm; 36 | } 37 | 38 | public synchronized boolean isUpdateFromZk() { 39 | return isUpdateFromZk; 40 | } 41 | 42 | public synchronized void setUpdateFromZk(boolean isUpdateFromZk) { 43 | this.isUpdateFromZk = isUpdateFromZk; 44 | } 45 | 46 | /** 47 | * 启动目录监视(建议:最好在单独线程里执行此方法,因:此方法会阻塞主线程,导致后续程序无法执行) 48 | * 49 | * @param dirPath 50 | * 目录路劲 51 | * @param period 52 | * 单位毫秒,表示监控时间间隔 53 | * @throws Exception 54 | */ 55 | public void startWatch(String filePath, long period, FileWatchProcessor processor) throws Exception { 56 | if (filePath == null) { 57 | throw new NullPointerException("文件路径不可以为空"); 58 | } 59 | if(period <= 0){ 60 | throw new Exception("参数period必须大于0"); 61 | } 62 | if(processor == null){ 63 | throw new NullPointerException("fileWatchProcessor 对象不能为空。"); 64 | } 65 | File file = new File(filePath); 66 | if(!file.exists()){ 67 | throw new FileNotFoundException("文件路径不存在:"+filePath); 68 | } 69 | if (!isRunning) { 70 | this.setLastModified(file.lastModified()); 71 | boolean fileExists; 72 | while (fileExists = file.exists() && !"quite".equals(cmd)) { 73 | logger.info("正在对文件进行监视...filePath="+filePath); 74 | isRunning = true; 75 | long nlm = file.lastModified(); 76 | if (nlm > this.getLastModified()) { 77 | logger.info("文件内容发生变化filePath=" + filePath); 78 | processor.process(new File(filePath)); 79 | this.setLastModified(nlm); 80 | this.setUpdateFromZk(false); 81 | } 82 | Thread.sleep(period); 83 | } 84 | if(!fileExists && isRunning){ 85 | isRunning = false; 86 | throw new FileNotFoundException("文件"+filePath+"无法访问。"); 87 | } 88 | } 89 | } 90 | /** 91 | * /** 92 | * 启动目录监视 93 | * 94 | * @param dirPath 95 | * 目录路劲 96 | * @param period 97 | * 单位毫秒,表示监控时间间隔 98 | * @throws Exception 99 | */ 100 | public void runWatch(final String filePath, final long period, final FileWatchProcessor processor){ 101 | new Thread(new Runnable(){ 102 | @Override 103 | public void run() { 104 | try { 105 | startWatch(filePath, period, processor); 106 | } catch (Exception e) { 107 | logger.error("启动目录监视出错:filePath="+filePath); 108 | } 109 | } 110 | 111 | }).start(); 112 | } 113 | 114 | /** 115 | * /** 116 | * 启动目录监视 117 | * 118 | * @param dirPath 119 | * 目录路劲 120 | * @param period 121 | * 单位毫秒,表示监控时间间隔 122 | * @throws Exception 123 | */ 124 | public void runWatch(final String filePath, final FileWatchProcessor processor){ 125 | new Thread(new Runnable(){ 126 | @Override 127 | public void run() { 128 | try { 129 | int period = 1000*15;//默认十五秒 130 | String dwp = ZkConfigParser.getProperty("dir_watch_period"); 131 | if(dwp !=null && !"".equals(dwp.trim())){ 132 | period = Integer.parseInt(dwp); 133 | } 134 | startWatch(filePath, period, processor); 135 | } catch (Exception e) { 136 | logger.error("启动目录监视出错:filePath="+filePath); 137 | } 138 | } 139 | 140 | }).start(); 141 | } 142 | /** 143 | * 停止目录监视 144 | */ 145 | public void stopWatch() { 146 | isRunning = false; 147 | cmd = "quite"; 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/monitor/ConfFileMonitor.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.monitor; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import org.apache.zookeeper.KeeperException; 7 | import org.apache.zookeeper.WatchedEvent; 8 | import org.apache.zookeeper.Watcher; 9 | import org.apache.zookeeper.Watcher.Event.EventType; 10 | 11 | import com.lashou.v.zkconfig.common.ActiveKeyValueStore; 12 | import com.lashou.v.zkconfig.common.MemoryDataSynchronizer; 13 | import com.lashou.v.zkconfig.dirwatch.FileWatch; 14 | import com.lashou.v.zkconfig.dirwatch.FileWatchProcessorImpl; 15 | 16 | public class ConfFileMonitor extends DataMonitor { 17 | protected ActiveKeyValueStore store; 18 | protected String path; 19 | protected FileWatch fileWatch; 20 | 21 | 22 | @Override 23 | public void updateConfigFile(String zkpath) { 24 | //这里应该判断文件是否发生了变化,只有发生了变化才执行更新,这样效率高些,也比较安全(防止写入死循环)。 25 | String filePath = this.getConfigPathByZkPath(zkpath); 26 | try { 27 | logger.info("正在更新配置文件数据,filePath=" + filePath); 28 | byte[] data = store.readByteWithoutWatcher(zkpath); 29 | this.writeFile(data, filePath); 30 | File file = new File(filePath); 31 | fileWatch.setLastModified(file.lastModified());//修改文件监视器的时间戳 32 | } catch (Exception e) { 33 | logger.error("新配置文件数据出错,filePath=" + filePath, e); 34 | } 35 | } 36 | 37 | @Override 38 | public void updateMemoryData(String zkpath) { 39 | try { 40 | if(zkpath.endsWith(".properties") || zkpath.endsWith(".cfg")){//目前仅实现java系key:value健值对配置文件的内存数据更新 41 | logger.info("正在更新内存配置项数据,zkpath="+zkpath); 42 | byte[] data = store.readByteWithoutWatcher(zkpath); 43 | MemoryDataSynchronizer.synchronize(data); 44 | } 45 | }catch (Exception e) { 46 | logger.error("新内存数据配置出错,zkpath="+zkpath,e); 47 | } 48 | 49 | } 50 | 51 | @Override 52 | public void displayValue() throws KeeperException, InterruptedException { 53 | String value = store.read(path, this); 54 | logger.info("Read {" + path + "} as {" + value+ "}"); 55 | } 56 | 57 | @Override 58 | public void process(WatchedEvent event) { 59 | if (event.getType() == Watcher.Event.EventType.NodeDataChanged) {// 当前节点数据发生变化 60 | logger.info("处理NodeDataChanged事件——>path=" + event.getPath()); 61 | //数据发生变化时候,根据需要更新内存配置数据,以及对应配置文件数据。 62 | this.updateConfigFile(event.getPath()); 63 | this.updateMemoryData(event.getPath()); 64 | try { 65 | this.displayValue();////重新注册监听 66 | } catch (KeeperException e) { 67 | e.printStackTrace(); 68 | } catch (InterruptedException e) { 69 | e.printStackTrace(); 70 | } 71 | } else if (event.getType() == EventType.NodeChildrenChanged) { 72 | logger.info("处理NodeChildrenChanged——path=" + event.getPath()); 73 | } 74 | 75 | } 76 | @Override 77 | public void init(ActiveKeyValueStore store, String filePath) throws KeeperException, InterruptedException { 78 | this.store = store; 79 | //1 根据文件目录组织zk存储节点目录 80 | this.path = this.getZkPathByConfigPath(filePath); 81 | //2 初始化zk存储节点目录 82 | this.store.initZkPath(path); 83 | 84 | } 85 | @Override 86 | public void initDataToZk(ActiveKeyValueStore store, String filePath) throws KeeperException, InterruptedException, IOException { 87 | //4 配置文件初始数据读入zk服务器。 88 | byte[] data = this.readFile(new File(filePath)); 89 | this.store.writeByte(path, data); 90 | 91 | } 92 | @Override 93 | public void watchFiles(String filePath) { 94 | fileWatch = FileWatch.buildWatch(); 95 | FileWatchProcessorImpl processor = new FileWatchProcessorImpl(); 96 | processor.setStore(store); 97 | fileWatch.runWatch(filePath, processor); 98 | 99 | } 100 | 101 | @Override 102 | public void monitor(ActiveKeyValueStore store, String filePath) { 103 | try { 104 | 105 | //1 根据文件目录组织zk存储节点目录 106 | //2 初始化zk存储节点目录 107 | this.init(store, filePath); 108 | //3 注册事件监听 109 | this.displayValue(); 110 | //4 配置文件初始数据读入zk服务器。 111 | this.initDataToZk(store, filePath); 112 | //5 监听磁盘配置文件变动 113 | this.watchFiles(filePath); 114 | } catch (Exception e) { 115 | logger.error("监听配置文件数据变化出错: in method monitor() of class ConfFileMonitor!", e); 116 | } 117 | 118 | } 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/common/FileUtils.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.common; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import org.apache.log4j.Logger; 13 | 14 | public class FileUtils { 15 | static Logger logger = Logger.getLogger(FileUtils.class); 16 | /** 17 | * 应用系统名称,或者系统代码。用来表示配置文件所属应用,同时也起到数据分组。 18 | */ 19 | private static String appName; 20 | /** 21 | * 应用系统配置文件路径,以逗号分隔的字符串 22 | */ 23 | private static String appConfigPaths; 24 | 25 | static { 26 | appName = ZkConfigParser.getProperty("app_name"); 27 | appConfigPaths = ZkConfigParser.getAppConfigPaths(); 28 | } 29 | 30 | 31 | public static byte[] readFile(File file) throws IOException { 32 | if(file != null){ 33 | FileReader reader = new FileReader(file); 34 | StringBuffer buffer = new StringBuffer(); 35 | char[] cbuf = new char[1024]; 36 | int legth = 0; 37 | while((legth = reader.read(cbuf)) != -1){ 38 | buffer.append(new String(cbuf,0,legth)); 39 | } 40 | reader.close(); 41 | return buffer.toString().getBytes(ZkCconfigConstant.CHARSET); 42 | } 43 | return null; 44 | } 45 | 46 | 47 | public static int writeFile(byte[] data, String filePath) { 48 | try { 49 | OutputStream out = new FileOutputStream(filePath); 50 | out.write(data, 0, data.length); 51 | try { 52 | out.close(); 53 | } catch (IOException e) { 54 | } 55 | return 0; 56 | } catch (Exception e) { 57 | logger.error("写入文件出错:filePath="+filePath,e); 58 | } 59 | return -1; 60 | } 61 | 62 | public static String getConfigPathByZkPath(String zkpath) { 63 | if(zkpath == null){ 64 | return null; 65 | } 66 | String zkp = ZkCconfigConstant.ROOT_NODE+"/"+ZkConfigParser.getProperty("app_name"); 67 | String path = zkpath.substring(zkp.length(), zkpath.length()); 68 | if(path.indexOf(":") > 0){//包含冒号说明是windows系统,返回路径时候需要去掉首字符 69 | path = path.substring(1,path.length()); 70 | } 71 | return path; 72 | } 73 | 74 | public static String getZkPathByConfigPath(String path) { 75 | if(path == null){ 76 | return null; 77 | } 78 | String zkpath = ZkCconfigConstant.ROOT_NODE+"/"+ZkConfigParser.getProperty("app_name");; 79 | String friendlyPath = getFriendlyPath(path); 80 | return zkpath + friendlyPath; 81 | } 82 | 83 | /** 84 | * 1、路劲处理,路径分隔符统统转换为/。 85 | * 2、如果是windows系统保留盘符部分。eg: /C:/Users/Administrator/Downloads 86 | * 3、如果开头没有/则增加 87 | * 4、如果结尾有/则删除 88 | * @param path 89 | * @return 90 | */ 91 | public static String getFriendlyPath(String path){ 92 | if(path != null){ 93 | path = path.replaceAll("\\\\", "/"); 94 | if(!path.startsWith("/")){ 95 | path = "/"+path; 96 | } 97 | if(path.endsWith("/")){ 98 | path = path.substring(0,path.length()-1); 99 | 100 | } 101 | return path; 102 | }else{ 103 | return null; 104 | } 105 | 106 | } 107 | 108 | public static List getAllConfigFiles() { 109 | List files = null; 110 | List confPaths = getAllConfigPaths(); 111 | if(confPaths != null && confPaths.size()> 0){ 112 | files = new ArrayList(); 113 | for(String path : confPaths){ 114 | File dirFile = new File(path); 115 | File[] fs = dirFile.listFiles(); 116 | if(fs != null && fs.length>0){ 117 | for(File f: fs){ 118 | if(f.isFile()){ 119 | files.add(f); 120 | } 121 | } 122 | } 123 | 124 | } 125 | } 126 | return files; 127 | } 128 | 129 | public static List getAllConfigPaths() { 130 | if(appConfigPaths == null){ 131 | return null; 132 | } 133 | String[] paths = appConfigPaths.split(","); 134 | return Arrays.asList(paths); 135 | } 136 | 137 | public static String getAppName() { 138 | return appName; 139 | } 140 | 141 | 142 | public static void setAppName(String appName) { 143 | FileUtils.appName = appName; 144 | } 145 | 146 | 147 | public static String getAppConfigPaths() { 148 | return appConfigPaths; 149 | } 150 | 151 | 152 | public static void setAppConfigPaths(String appConfigPaths) { 153 | FileUtils.appConfigPaths = appConfigPaths; 154 | } 155 | 156 | } 157 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/core/ZkConfigExecutor.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.core; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.List; 6 | import java.util.concurrent.CountDownLatch; 7 | 8 | import org.apache.log4j.Logger; 9 | import org.apache.zookeeper.KeeperException; 10 | 11 | import com.lashou.v.zkconfig.common.ActiveKeyValueStore; 12 | import com.lashou.v.zkconfig.common.FileUtils; 13 | import com.lashou.v.zkconfig.common.SystemInit; 14 | import com.lashou.v.zkconfig.monitor.Monitor; 15 | import com.lashou.v.zkconfig.monitor.MonitorFactory; 16 | import com.lashou.v.zkconfig.monitor.MonitorFactory.MonitorType; 17 | 18 | public class ZkConfigExecutor { 19 | private static Logger logger = Logger.getLogger(ZkConfigExecutor.class); 20 | /** 21 | * 连接zk服务器,初始化系统参数,返回 ActiveKeyValueStore对象。 22 | * @return 23 | * @throws KeeperException 24 | * @throws InterruptedException 25 | * @throws IOException 26 | */ 27 | private static ActiveKeyValueStore buildStore() throws KeeperException, InterruptedException, IOException{ 28 | ActiveKeyValueStore store = new ActiveKeyValueStore(); 29 | store.connect();//连接服务器 30 | SystemInit.init(store);//初始化系统参数 31 | return store; 32 | } 33 | 34 | /** 35 | * 对所有配置目录下所有文件执行监视 36 | * @param monitor 37 | * @throws IOException 38 | * @throws InterruptedException 39 | * @throws KeeperException 40 | */ 41 | public static void execute(MonitorType type) throws IOException, InterruptedException, KeeperException{ 42 | ActiveKeyValueStore store = buildStore(); 43 | List files = FileUtils.getAllConfigFiles(); 44 | if(files != null && files.size() > 0){ 45 | for(File file : files){ 46 | Monitor monitor = MonitorFactory.getMonitor(type); 47 | monitor.monitor(store,file.getAbsolutePath());//执行监听 48 | } 49 | 50 | }else{ 51 | logger.error("未获取到任何合法的配置文件,无法执行监控"); 52 | throw new IOException("未获取到任何合法的配置文件,无法执行监控"); 53 | } 54 | } 55 | /** 56 | * 对所有配置目录下所有文件执行监视 57 | * @param monitor 58 | * @throws IOException 59 | * @throws InterruptedException 60 | * @throws KeeperException 61 | */ 62 | public static void execute(int type) throws IOException, InterruptedException, KeeperException{ 63 | ActiveKeyValueStore store = buildStore(); 64 | List files = FileUtils.getAllConfigFiles(); 65 | if(files != null && files.size() > 0){ 66 | for(File file : files){ 67 | Monitor monitor = MonitorFactory.getMonitor(MonitorFactory.MonitorType.getMonitorType(type)); 68 | monitor.monitor(store,file.getAbsolutePath());//执行监听 69 | } 70 | 71 | }else{ 72 | logger.error("未获取到任何合法的配置文件,无法执行监控"); 73 | throw new IOException("未获取到任何合法的配置文件,无法执行监控"); 74 | } 75 | } 76 | /** 77 | * 监视指定配置文件 78 | * @param monitor 79 | * @param filePath 80 | * @throws IOException 81 | * @throws InterruptedException 82 | * @throws KeeperException 83 | */ 84 | public static void executeSpecial(MonitorType type,String filePath) throws IOException, InterruptedException, KeeperException{ 85 | ActiveKeyValueStore store = buildStore(); 86 | Monitor monitor = MonitorFactory.getMonitor(type); 87 | monitor.monitor(store,filePath);//执行监听 88 | } 89 | /** 90 | * 监视指定配置文件 91 | * @param monitor 92 | * @param filePath 93 | * @throws IOException 94 | * @throws InterruptedException 95 | * @throws KeeperException 96 | */ 97 | public static void executeSpecial(int type,String filePath) throws IOException, InterruptedException, KeeperException{ 98 | ActiveKeyValueStore store = buildStore(); 99 | Monitor monitor = MonitorFactory.getMonitor(MonitorFactory.MonitorType.getMonitorType(type)); 100 | monitor.monitor(store,filePath);//执行监听 101 | } 102 | 103 | /** 104 | * run local example 105 | * @param args 106 | */ 107 | public static void main(String[] args) { 108 | CountDownLatch connectedSignal = new CountDownLatch(1); 109 | try { 110 | //1、注册配置所需配置类 111 | BeanRegisterCenter brc = new BeanRegisterCenter();//执行监听 112 | // brc.register(new TestObj()); 113 | //2、执行监听 114 | ZkConfigExecutor.execute(MonitorFactory.MonitorType.CONF_PROPERTIES_FILE_MONITOR); 115 | 116 | connectedSignal.await(); 117 | } catch (IOException e) { 118 | e.printStackTrace(); 119 | } catch (InterruptedException e) { 120 | e.printStackTrace(); 121 | } catch (KeeperException e) { 122 | e.printStackTrace(); 123 | } 124 | 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/monitor/DataMonitor.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.monitor; 2 | 3 | import java.io.File; 4 | import java.io.FileOutputStream; 5 | import java.io.FileReader; 6 | import java.io.IOException; 7 | import java.io.OutputStream; 8 | import java.util.ArrayList; 9 | import java.util.Arrays; 10 | import java.util.List; 11 | 12 | import org.apache.log4j.Logger; 13 | import org.apache.zookeeper.Watcher; 14 | 15 | import com.lashou.v.zkconfig.common.ZkCconfigConstant; 16 | import com.lashou.v.zkconfig.common.ZkConfigParser; 17 | 18 | public abstract class DataMonitor implements Monitor,Watcher { 19 | 20 | protected static Logger logger = Logger.getLogger(DataMonitor.class); 21 | /** 22 | * 应用系统名称,或者系统代码。用来表示配置文件所属应用,同时也起到数据分组。 23 | */ 24 | protected String appName; 25 | /** 26 | * 应用系统配置文件路径,以逗号分隔的字符串 27 | */ 28 | protected String appConfigPaths; 29 | public DataMonitor(){ 30 | //初始化配置参数 31 | appName = ZkConfigParser.getProperty("app_name"); 32 | appConfigPaths = ZkConfigParser.getAppConfigPaths(); 33 | } 34 | @Override 35 | public List getAllConfigFiles() { 36 | List files = null; 37 | List confPaths = this.getAllConfigPaths(); 38 | if(confPaths != null && confPaths.size()> 0){ 39 | files = new ArrayList(); 40 | for(String path : confPaths){ 41 | File dirFile = new File(path); 42 | File[] fs = dirFile.listFiles(); 43 | if(fs != null && fs.length>0){ 44 | for(File f: fs){ 45 | if(f.isFile()){ 46 | files.add(f); 47 | } 48 | } 49 | } 50 | 51 | } 52 | } 53 | return files; 54 | } 55 | @Override 56 | public List getAllConfigPaths() { 57 | if(appConfigPaths == null){ 58 | return null; 59 | } 60 | String[] paths = appConfigPaths.split(","); 61 | return Arrays.asList(paths); 62 | } 63 | @Override 64 | public String getZkPathByConfigPath(String path) { 65 | if(path == null){ 66 | return null; 67 | } 68 | String zkpath = ZkCconfigConstant.ROOT_NODE+"/"+appName; 69 | String friendlyPath = getFriendlyPath(path); 70 | return zkpath + friendlyPath; 71 | } 72 | @Override 73 | public String getConfigPathByZkPath(String zkpath) { 74 | if(zkpath == null){ 75 | return null; 76 | } 77 | String zkp = ZkCconfigConstant.ROOT_NODE+"/"+appName; 78 | String path = zkpath.substring(zkp.length(), zkpath.length()); 79 | if(path.indexOf(":") > 0){//包含冒号说明是windows系统,返回路径时候需要去掉首字符 80 | path = path.substring(1,path.length()); 81 | } 82 | return path; 83 | } 84 | /** 85 | * 1、路劲处理,路径分隔符统统转换为/。 86 | * 2、如果是windows系统保留盘符部分。eg: /C:/Users/Administrator/Downloads 87 | * 3、如果开头没有/则增加 88 | * 4、如果结尾有/则删除 89 | * @param path 90 | * @return 91 | */ 92 | protected static String getFriendlyPath(String path){ 93 | if(path != null){ 94 | path = path.replaceAll("\\\\", "/"); 95 | // if(path.indexOf(":") > 0){ 96 | // path = path.substring(path.indexOf(":")+1, path.length()); 97 | // } 98 | if(!path.startsWith("/")){ 99 | path = "/"+path; 100 | } 101 | if(path.endsWith("/")){ 102 | path = path.substring(0,path.length()-1); 103 | 104 | } 105 | return path; 106 | }else{ 107 | return null; 108 | } 109 | 110 | } 111 | 112 | @Override 113 | public byte[] readFile(File file) throws IOException { 114 | if(file != null){ 115 | FileReader reader = new FileReader(file); 116 | StringBuffer buffer = new StringBuffer(); 117 | char[] cbuf = new char[1024]; 118 | int legth = 0; 119 | while((legth = reader.read(cbuf)) != -1){ 120 | buffer.append(new String(cbuf,0,legth)); 121 | } 122 | reader.close(); 123 | return buffer.toString().getBytes(ZkCconfigConstant.CHARSET); 124 | } 125 | return null; 126 | } 127 | 128 | @Override 129 | public int writeFile(byte[] data, String filePath) { 130 | try { 131 | OutputStream out = new FileOutputStream(filePath); 132 | out.write(data, 0, data.length); 133 | try { 134 | out.close(); 135 | } catch (IOException e) { 136 | } 137 | return 0; 138 | } catch (Exception e) { 139 | logger.error("写入文件出错:filePath="+filePath,e); 140 | } 141 | return -1; 142 | } 143 | /** 144 | * 当数据发生变化时候按需要更新对应配置文件数据 145 | * @param zkpath 146 | */ 147 | public abstract void updateConfigFile(String zkpath); 148 | /** 149 | * 当数据发生变化时候按需要更新对应内存对象数据 150 | * @param zkpath 151 | */ 152 | public abstract void updateMemoryData(String zkpath); 153 | } 154 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/monitor/PropertiesFileMonitor.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.monitor; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | import java.util.Properties; 6 | import java.util.Set; 7 | 8 | import org.apache.zookeeper.KeeperException; 9 | import org.apache.zookeeper.Watcher; 10 | 11 | import com.lashou.v.zkconfig.common.ActiveKeyValueStore; 12 | import com.lashou.v.zkconfig.common.MemoryDataSynchronizer; 13 | import com.lashou.v.zkconfig.common.ZkConfigParser; 14 | import com.lashou.v.zkconfig.dirwatch.FileWatch; 15 | import com.lashou.v.zkconfig.dirwatch.PropertiesFileWatchProcessorImpl; 16 | 17 | /** 18 | * Java配置文件专用监视器(支持key:value键值对格式配置的文件扩展名以.propeties | .cfg结尾的文件) 19 | * @author QuanZhong 20 | * @version 21 | * @since JDK 1.6 22 | */ 23 | public class PropertiesFileMonitor extends ConfFileMonitor { 24 | 25 | @Override 26 | public void updateConfigFile(String zkpath) { 27 | 28 | try { 29 | logger.info("正在更新配置文件数据,zkpath=" + zkpath); 30 | String key = zkpath.substring(zkpath.lastIndexOf("/")+1, zkpath.length()); 31 | String value = store.readStringWithoutWatcher(zkpath); 32 | String fpath = zkpath.substring(0,zkpath.lastIndexOf("/"));//截取掉key值Node 33 | String filePath = this.getConfigPathByZkPath(fpath); 34 | ZkConfigParser.updateProperties(filePath, key, value); 35 | File file = new File(filePath); 36 | fileWatch.setLastModified(file.lastModified());//修改文件监视器的时间戳 37 | } catch (Exception e) { 38 | logger.error("新配置文件数据出错,zkpath=" + zkpath, e); 39 | } 40 | } 41 | 42 | @Override 43 | public void updateMemoryData(String zkpath) { 44 | try { 45 | logger.info("正在更新内存配置项数据,zkpath="+zkpath); 46 | String key = zkpath.substring(zkpath.lastIndexOf("/")+1, zkpath.length()); 47 | String value = store.readStringWithoutWatcher(zkpath); 48 | MemoryDataSynchronizer.synchronize(key, value); 49 | }catch (Exception e) { 50 | logger.error("新内存数据配置出错,zkpath="+zkpath,e); 51 | } 52 | } 53 | @Override 54 | public void init(ActiveKeyValueStore store, String filePath) 55 | throws KeeperException, InterruptedException { 56 | super.init(store, filePath); 57 | /** 58 | * 让每一个key作为zk一个子node 59 | */ 60 | if(filePath.endsWith("properties") || filePath.endsWith("cfg")){ 61 | Properties prop = ZkConfigParser.parserFile(filePath); 62 | if(prop != null){ 63 | Set set = prop.keySet(); 64 | for(Object key : set){ 65 | String value = prop.getProperty((String) key); 66 | this.store.updatePathWithOutACL(this.path+"/"+key, value); 67 | } 68 | 69 | } 70 | }else{ 71 | throw new RuntimeException("PropertiesFileMonitor暂时无法处理此文件类型filePath="+filePath); 72 | } 73 | } 74 | 75 | @Override 76 | public void initDataToZk(ActiveKeyValueStore store, String filePath) 77 | throws KeeperException, InterruptedException, IOException { 78 | //super.initDataToZk(store, filePath); 79 | //这里读取数据过程已经在init中实现,这里覆盖父类实现。 80 | logger.info("PropertiesFileMonitor初始化数据到ZKServer..."); 81 | } 82 | 83 | 84 | @Override 85 | public void watchFiles(String filePath) { 86 | fileWatch = FileWatch.buildWatch(); 87 | PropertiesFileWatchProcessorImpl processor = new PropertiesFileWatchProcessorImpl(); 88 | processor.setStore(store); 89 | fileWatch.runWatch(filePath, processor); 90 | } 91 | public void displayPropValue(String zkpath, Watcher watch) throws KeeperException, InterruptedException { 92 | String value = store.read(zkpath, watch); 93 | logger.info("Read {" + zkpath + "} as {" + value+ "}"); 94 | } 95 | public void monitorProps(ActiveKeyValueStore store, String filePath) throws KeeperException, InterruptedException{ 96 | Properties prop = ZkConfigParser.parserFile(filePath); 97 | Set set = prop.keySet(); 98 | for(Object key : set){ 99 | // String value = prop.getProperty((String) key); 100 | String zkPath = this.path+"/"+key; 101 | PropertiesFileMonitor monitor = new PropertiesFileMonitor(); 102 | monitor.store = store; 103 | monitor.fileWatch = fileWatch; 104 | monitor.path = zkPath; 105 | this.displayPropValue(zkPath, monitor); 106 | } 107 | 108 | } 109 | 110 | @Override 111 | public void monitor(ActiveKeyValueStore store, String filePath) { 112 | try { 113 | //1 根据文件目录组织zk存储节点目录 114 | // 初始化zk存储节点目录 115 | this.init(store, filePath); 116 | //2监听磁盘配置文件变动 117 | this.watchFiles(filePath); 118 | //3 注册事件监听 119 | this.monitorProps(store, filePath); 120 | 121 | } catch (Exception e) { 122 | logger.error("监视启动出错:",e); 123 | } 124 | 125 | } 126 | 127 | } 128 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | 6 | maven-parent 7 | com.lashou 8 | 2.0 9 | 10 | 11 | com.lashou.v 12 | v-zkconfig 13 | 0.0.1-SNAPSHOT 14 | jar 15 | 16 | v-zkconfig 17 | http://maven.apache.org 18 | 19 | 20 | UTF-8 21 | 22 | 23 | 24 | 25 | com.lashou.common 26 | common-util 27 | 1.14 28 | 29 | 30 | 31 | org.springframework.amqp 32 | spring-rabbit 33 | 34 | 35 | spring-tx 36 | org.springframework 37 | 38 | 39 | spring-core 40 | org.springframework 41 | 42 | 43 | 44 | 45 | org.springframework.data 46 | spring-data-redis 47 | 48 | 49 | redis.clients 50 | jedis 51 | 2.4.2 52 | jar 53 | compile 54 | 55 | 56 | org.apache.poi 57 | poi-excelant 58 | 3.8 59 | 60 | 61 | 62 | org.quartz-scheduler 63 | quartz 64 | 1.8.5 65 | 66 | 67 | org.springframework 68 | spring-jdbc 69 | 70 | 71 | 72 | org.apache.velocity 73 | velocity 74 | 75 | 76 | javax.mail 77 | mail 78 | 1.4.7 79 | 80 | 81 | 82 | org.apache.httpcomponents 83 | httpclient 84 | 4.2.5 85 | 86 | 87 | 88 | junit 89 | junit-dep 90 | 91 | 92 | org.springframework 93 | spring-test 94 | 95 | 96 | com.h2database 97 | h2 98 | 99 | 100 | org.liquibase 101 | liquibase-core 102 | 3.0.8 103 | 104 | 105 | com.caucho 106 | hessian 107 | 4.0.37 108 | jar 109 | compile 110 | 111 | 112 | org.springframework 113 | spring-context-support 114 | 115 | 116 | org.springframework 117 | spring-aspects 118 | 119 | 120 | org.springframework 121 | spring-web 122 | ${spring.framework.version} 123 | jar 124 | provided 125 | 126 | 127 | commons-pool 128 | commons-pool 129 | 1.6 130 | jar 131 | compile 132 | 133 | 134 | org.apache.zookeeper 135 | zookeeper 136 | 3.3.6 137 | 138 | 139 | com.sun.jdmk 140 | jmxtools 141 | 142 | 143 | com.sun.jmx 144 | jmxri 145 | 146 | 147 | 148 | 149 | 150 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/springplugin/ZkConfigScannerConfigurer.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.springplugin; 2 | 3 | import java.io.IOException; 4 | import java.util.ArrayList; 5 | import java.util.List; 6 | 7 | import org.apache.log4j.Logger; 8 | import org.apache.zookeeper.KeeperException; 9 | import org.springframework.beans.BeansException; 10 | import org.springframework.beans.factory.InitializingBean; 11 | import org.springframework.context.ApplicationContext; 12 | import org.springframework.context.ApplicationContextAware; 13 | 14 | import com.lashou.v.zkconfig.core.BeanRegisterCenter; 15 | import com.lashou.v.zkconfig.core.ZkConfigExecutor; 16 | import com.lashou.v.zkconfig.monitor.MonitorFactory; 17 | 18 | public class ZkConfigScannerConfigurer implements InitializingBean,ApplicationContextAware { 19 | private Logger logger = Logger.getLogger(ZkConfigScannerConfigurer.class); 20 | 21 | /** 22 | * 想要执行的监控类型 23 | * @see MonitorFactory.MonitorType 24 | */ 25 | private int monitorType; 26 | /** 27 | * 指定执行的特定文件 28 | */ 29 | private String specialFilePath; 30 | /** 31 | * 配置文件对应实例对象 @ZkConfig注解的对象 32 | */ 33 | private List confInstances; 34 | 35 | /** 36 | * 配置文件对应实例对象名称,spring中的bean id 37 | */ 38 | private List confInstanceNames; 39 | 40 | ApplicationContext applicationContext; 41 | 42 | public void startWatch() throws IOException, InterruptedException, KeeperException{ 43 | //注册配置所需配置类 44 | BeanRegisterCenter brc = new BeanRegisterCenter();//执行监听 45 | if(confInstances != null && confInstances.size() > 0){ 46 | for(Object instance : confInstances){ 47 | brc.register(instance); 48 | } 49 | }else{ 50 | logger.warn("无任何配置类对象被注册!"); 51 | } 52 | if(monitorType > 0){ 53 | ZkConfigExecutor.execute(monitorType); 54 | }else{ 55 | logger.error("monitorType类型未指定,无法启动数据监听! "); 56 | throw new RuntimeException("monitorType类型未指定,无法启动数据监听! "); 57 | } 58 | 59 | } 60 | public void startSpecialWatch(String filePath) throws IOException, InterruptedException, KeeperException{ 61 | //注册配置所需配置类 62 | BeanRegisterCenter brc = new BeanRegisterCenter();//执行监听 63 | if(confInstances != null && confInstances.size() > 0){ 64 | for(Object instance : confInstances){ 65 | brc.register(instance); 66 | } 67 | }else{ 68 | logger.warn("无任何配置类对象被注册!"); 69 | } 70 | if(monitorType > 0){ 71 | ZkConfigExecutor.executeSpecial(monitorType, filePath); 72 | }else{ 73 | logger.error("monitorType类型未指定,无法启动数据监听! "); 74 | throw new RuntimeException("monitorType类型未指定,无法启动数据监听! "); 75 | } 76 | 77 | } 78 | 79 | public int getMonitorType() { 80 | return monitorType; 81 | } 82 | 83 | public void setMonitorType(int monitorType) { 84 | this.monitorType = monitorType; 85 | } 86 | 87 | public List getConfInstances() { 88 | return confInstances; 89 | } 90 | 91 | public void setConfInstances(List confInstances) { 92 | this.confInstances = confInstances; 93 | } 94 | 95 | public List getConfInstanceNames() { 96 | return confInstanceNames; 97 | } 98 | 99 | public void setConfInstanceNames(List confInstanceNames) { 100 | this.confInstanceNames = confInstanceNames; 101 | } 102 | 103 | public String getSpecialFilePath() { 104 | return specialFilePath; 105 | } 106 | public void setSpecialFilePath(String specialFilePath) { 107 | this.specialFilePath = specialFilePath; 108 | } 109 | 110 | //如果实现ApplicationContextAware,调用setApplicationContext设置ApplicationContext 111 | @Override 112 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 113 | this.applicationContext = applicationContext; 114 | 115 | } 116 | public void initParameters(){ 117 | if((confInstanceNames !=null && confInstanceNames.size() > 0) && (confInstances == null || confInstances.size() ==0) && applicationContext != null){ 118 | confInstances = new ArrayList(); 119 | for(String beanName : confInstanceNames){ 120 | Object confObj = applicationContext.getBean(beanName); 121 | confInstances.add(confObj); 122 | } 123 | 124 | } 125 | } 126 | //调用InitializingBean的afterPropertiesSet()方法; 127 | @Override 128 | public void afterPropertiesSet() throws Exception { 129 | initParameters(); 130 | new Thread(new Runnable(){ 131 | 132 | @Override 133 | public void run() { 134 | try { 135 | int counter = 0; 136 | while(true){//延迟十秒等待web容器类加载,防止由于类加载不成功导致系统无法运行。 137 | Thread.sleep(1000); 138 | counter+=10; 139 | logger.info("ZKConfig Server即将启动运行,正在初始化..."+counter+"%"); 140 | if(counter >=100){ 141 | break; 142 | } 143 | } 144 | if(specialFilePath == null || "".equals(specialFilePath.trim())){ 145 | startWatch(); 146 | }else{ 147 | startSpecialWatch(specialFilePath); 148 | } 149 | 150 | }catch (Exception e) { 151 | logger.error("系统启动监听出错:",e); 152 | } 153 | 154 | } 155 | 156 | }).start(); 157 | 158 | } 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | } 167 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/common/MemoryDataSynchronizer.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.common; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.Map; 5 | import java.util.Properties; 6 | import java.util.Set; 7 | 8 | import org.apache.log4j.Logger; 9 | 10 | import com.lashou.v.zkconfig.bean.ConfBeanMetaData; 11 | 12 | /** 13 | * 内存数据同步器,用于同步zk服务器数据变更到内存数据 14 | * @author QuanZhong 15 | * @version 16 | * @since JDK 1.6 17 | */ 18 | public class MemoryDataSynchronizer { 19 | static Logger logger = Logger.getLogger(MemoryDataSynchronizer.class); 20 | 21 | public static int synchronize(byte[] data){ 22 | try { 23 | Map elements = ElementCollection.getElementCollection(); 24 | Properties prop = ZkConfigParser.parserData(data); 25 | if(prop != null){ 26 | Set set = prop.keySet(); 27 | for(Object obj : set){ 28 | String value = prop.getProperty((String) obj); 29 | ConfBeanMetaData cbmd = elements.get((String) obj); 30 | if(cbmd != null && !value.equals(cbmd.getFieldValue())){//配置项存在,并且数据值发生变化 31 | cbmd.setFieldValue(value); 32 | Object instance = cbmd.getInstance(); 33 | logger.info("before update, instance:"+printToString(instance)); 34 | Field[] fielsd = instance.getClass().getDeclaredFields(); 35 | for(Field field : fielsd){ 36 | field.setAccessible(true); 37 | String fieldName = field.getName(); 38 | if(fieldName.equals(cbmd.getFieldName())){ 39 | //常用配置项类型int\long\short\String\boolean 40 | if(field.getType().equals(int.class) || field.getType().equals(Integer.class)){ 41 | if(value == null || "".equals(value.trim())){ 42 | field.set(instance,0); 43 | } 44 | field.set(instance,Integer.parseInt(value)); 45 | }else if(field.getType().equals(long.class) || field.getType().equals(Long.class)){ 46 | if(value == null || "".equals(value.trim())){ 47 | field.set(instance,0l); 48 | } 49 | field.set(instance,Long.parseLong(value)); 50 | }else if(field.getType().equals(short.class) || field.getType().equals(Short.class)){ 51 | if(value == null || "".equals(value.trim())){ 52 | field.set(instance,0); 53 | } 54 | field.set(instance,Short.parseShort(value)); 55 | }else if(field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)){ 56 | if(value == null || "".equals(value.trim()) || "0".equals(value.trim())){ 57 | field.set(instance,false); 58 | }else if("1".equals(value.trim())){ 59 | field.set(instance,true); 60 | }else{ 61 | field.set(instance,Boolean.parseBoolean(value)); 62 | } 63 | 64 | }else{ 65 | field.set(instance,value); 66 | } 67 | break; 68 | } 69 | } 70 | logger.info("after update, instance:"+ printToString(cbmd.getInstance())); 71 | } 72 | } 73 | 74 | } 75 | logger.info("更新内存配置数据成功!"); 76 | return 0; 77 | } catch (Exception e) { 78 | logger.error("更新内存数据失败:",e); 79 | } 80 | 81 | return -1; 82 | 83 | } 84 | 85 | /** 86 | * 针对单一属性进行更新内存 87 | * @param propValue 88 | * @return 89 | */ 90 | public static int synchronize(String key_, String propValue){ 91 | try { 92 | Map elements = ElementCollection.getElementCollection(); 93 | String value = propValue; 94 | ConfBeanMetaData cbmd = elements.get(key_); 95 | if(cbmd != null && !value.equals(cbmd.getFieldValue())){//配置项存在,并且数据值发生变化 96 | cbmd.setFieldValue(value); 97 | Object instance = cbmd.getInstance(); 98 | logger.info("before update, instance:"+printToString(instance)); 99 | Field[] fielsd = instance.getClass().getDeclaredFields(); 100 | for(Field field : fielsd){ 101 | field.setAccessible(true); 102 | String fieldName = field.getName(); 103 | if(fieldName.equals(cbmd.getFieldName())){ 104 | //常用配置项类型int\long\short\String\boolean 105 | if(field.getType().equals(int.class) || field.getType().equals(Integer.class)){ 106 | if(value == null || "".equals(value.trim())){ 107 | field.set(instance,0); 108 | } 109 | field.set(instance,Integer.parseInt(value)); 110 | }else if(field.getType().equals(long.class) || field.getType().equals(Long.class)){ 111 | if(value == null || "".equals(value.trim())){ 112 | field.set(instance,0l); 113 | } 114 | field.set(instance,Long.parseLong(value)); 115 | }else if(field.getType().equals(short.class) || field.getType().equals(Short.class)){ 116 | if(value == null || "".equals(value.trim())){ 117 | field.set(instance,0); 118 | } 119 | field.set(instance,Short.parseShort(value)); 120 | }else if(field.getType().equals(boolean.class) || field.getType().equals(Boolean.class)){ 121 | if(value == null || "".equals(value.trim()) || "0".equals(value.trim())){ 122 | field.set(instance,false); 123 | }else if("1".equals(value.trim())){ 124 | field.set(instance,true); 125 | }else{ 126 | field.set(instance,Boolean.parseBoolean(value)); 127 | } 128 | 129 | }else{ 130 | field.set(instance,value); 131 | } 132 | break; 133 | } 134 | } 135 | logger.info("after update, instance:"+ printToString(cbmd.getInstance())); 136 | } 137 | 138 | logger.info("更新内存配置数据成功!"); 139 | return 0; 140 | } catch (Exception e) { 141 | logger.error("更新内存数据失败:",e); 142 | } 143 | 144 | return -1; 145 | 146 | } 147 | /** 148 | * 用来返回对象的toString(),获取可读性 149 | * @param obj 150 | * @return 151 | */ 152 | public static String printToString(Object obj){ 153 | try { 154 | if(obj != null){ 155 | String str = obj.getClass().getSimpleName()+"["; 156 | Field[] fielsd = obj.getClass().getDeclaredFields(); 157 | for(Field field : fielsd){ 158 | field.setAccessible(true); 159 | String fieldPair = field.getName()+"="+field.get(obj); 160 | str += fieldPair+","; 161 | } 162 | str += "]"; 163 | return str; 164 | }else{ 165 | logger.error("input argument is null:"+obj); 166 | } 167 | }catch (Exception e) { 168 | logger.error(e); 169 | } 170 | return null; 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /src/main/java/com/lashou/v/zkconfig/core/BeanRegisterCenter.java: -------------------------------------------------------------------------------- 1 | package com.lashou.v.zkconfig.core; 2 | 3 | import java.lang.reflect.Field; 4 | import java.util.Map; 5 | import java.util.Map.Entry; 6 | import java.util.Set; 7 | 8 | import org.apache.log4j.Logger; 9 | 10 | import com.lashou.v.zkconfig.annotaion.ZkConfig; 11 | import com.lashou.v.zkconfig.annotaion.ZkValue; 12 | import com.lashou.v.zkconfig.bean.ConfBeanMetaData; 13 | import com.lashou.v.zkconfig.common.ElementCollection; 14 | import com.lashou.v.zkconfig.common.RegisterCollection; 15 | 16 | /** 17 | * 需要获取配置参数bean的注册中心 18 | * @author QuanZhong 19 | * @version 20 | * @since JDK 1.6 21 | */ 22 | public class BeanRegisterCenter { 23 | private Logger logger = Logger.getLogger(BeanRegisterCenter.class); 24 | private Map> registers = RegisterCollection.getRegisterCollection(); 25 | private Map elements = ElementCollection.getElementCollection(); 26 | 27 | 28 | /** 29 | * 注册所需要配置的累对象,支持同一类型,多个实列的配置 30 | * @param clazz 类型 31 | * @param instance 实例对象 32 | * @return 33 | */ 34 | public int register(Object instance,Class clazz){ 35 | if (instance != null) { 36 | registers.put(instance, clazz); 37 | try { 38 | this.createElements(instance, clazz); 39 | } catch (Exception e) { 40 | logger.error("对象注册失败,请重新注册:",e); 41 | registers.remove(instance); 42 | this.deleteElements(clazz); 43 | return -1; 44 | } 45 | return 0; 46 | } 47 | return -1; 48 | 49 | } 50 | /** 51 | * 注册所需要配置的累对象,支持同一类型,多个实列的配置 52 | * @param clazz 类型 53 | * @param instance 实例对象 54 | * @return 55 | */ 56 | public int register(Object instance){ 57 | if (instance != null) { 58 | Class clazz = instance.getClass(); 59 | registers.put(instance, clazz); 60 | try { 61 | this.createElements(instance, clazz); 62 | } catch (Exception e) { 63 | logger.error("对象注册失败,请重新注册:",e); 64 | registers.remove(instance); 65 | this.deleteElements(clazz); 66 | return -1; 67 | } 68 | return 0; 69 | } 70 | return -1; 71 | 72 | } 73 | /** 74 | * 删除已注册的配置对象 75 | * @param instance 76 | * @return 77 | */ 78 | public int unregister(Object instance){ 79 | if (instance != null) { 80 | Object obj = registers.remove(instance); 81 | if (obj != null) { 82 | this.deleteElements(instance.getClass()); 83 | return 0; 84 | } 85 | } 86 | return -1; 87 | } 88 | /** 89 | * 清空所有已注册的配置类对象 90 | */ 91 | public void clear(){ 92 | registers.clear(); 93 | elements.clear(); 94 | } 95 | 96 | /** 97 | * 检测注册中心是否有类配置了ZkConfig注解,如果有配置ZkConfig注解,则将优先以ZkConfg注解中的连接参数,来连接Zookeeper服务器集群 98 | */ 99 | public boolean hasZkConfigAnnotationed(){ 100 | boolean result = false; 101 | Set>> entrySet = registers.entrySet(); 102 | for(Entry> entry : entrySet){ 103 | Class clazz = entry.getValue(); 104 | if(clazz != null){ 105 | result = clazz.isAnnotationPresent(ZkConfig.class); 106 | break; 107 | } 108 | } 109 | return result; 110 | } 111 | /** 112 | * 获取注册中心ZkConfig配置对象,如果不存在返回null 113 | * @return 114 | */ 115 | public ZkConfig getZkConfig(){ 116 | Set>> entrySet = registers.entrySet(); 117 | for(Entry> entry : entrySet){ 118 | Class clazz = entry.getValue(); 119 | if(clazz != null && clazz.isAnnotationPresent(ZkConfig.class)){ 120 | return clazz.getAnnotation(ZkConfig.class); 121 | } 122 | } 123 | return null; 124 | } 125 | /** 126 | * 解析出对象中的配置项,并放入ElementCollection容器(新建,如果存在抛异常) 127 | * @param instance 128 | * @param clazz 129 | * @throws IllegalAccessException 130 | * @throws IllegalArgumentException 131 | */ 132 | public void createElements(Object instance,Class clazz) throws IllegalArgumentException, IllegalAccessException{ 133 | if(instance != null){ 134 | //读取属性配置 135 | Field[] fielsd = clazz.getDeclaredFields(); 136 | for(Field field : fielsd){ 137 | field.setAccessible(true); 138 | if(field.isAnnotationPresent(ZkValue.class)){ 139 | ZkValue zkValue = field.getAnnotation(ZkValue.class); 140 | String key = zkValue.key(); 141 | ConfBeanMetaData md = elements.get(key); 142 | if(md == null){ 143 | String fieldName = field.getName(); 144 | Object fieldValue = field.get(instance); 145 | Class fieldType = field.getType(); 146 | md = new ConfBeanMetaData(instance, key, fieldName, fieldValue, fieldType); 147 | elements.put(key, md); 148 | 149 | }else{ 150 | String msg = "系统中配置属性key重复,已有相同名称的key值配置。请修改@ZkValue中key属性值。key="+key; 151 | logger.warn(msg); 152 | throw new IllegalArgumentException(msg); 153 | } 154 | 155 | 156 | } 157 | } 158 | } 159 | } 160 | /** 161 | * 解析出对象中的配置项,并放入ElementCollection容器(更新,如果不存在则新建) 162 | * @param instance 163 | * @param clazz 164 | * @throws IllegalAccessException 165 | * @throws IllegalArgumentException 166 | */ 167 | public void updateElements(Object instance,Class clazz) throws IllegalArgumentException, IllegalAccessException{ 168 | if(instance != null){ 169 | //读取属性配置 170 | Field[] fielsd = clazz.getDeclaredFields(); 171 | for(Field field : fielsd){ 172 | field.setAccessible(true); 173 | if(field.isAnnotationPresent(ZkValue.class)){ 174 | ZkValue zkValue = field.getAnnotation(ZkValue.class); 175 | String key = zkValue.key(); 176 | ConfBeanMetaData md = elements.get(key); 177 | if(md == null){ 178 | String fieldName = field.getName(); 179 | Object fieldValue = field.get(instance); 180 | Class fieldType = field.getType(); 181 | md = new ConfBeanMetaData(instance, key, fieldName, fieldValue, fieldType); 182 | elements.put(key, md); 183 | 184 | }else{ 185 | String fieldName = field.getName(); 186 | Object fieldValue = field.get(instance); 187 | Class fieldType = field.getType(); 188 | md.setFieldName(fieldName); 189 | md.setFieldValue(fieldValue); 190 | md.setFieldType(fieldType); 191 | md.setInstance(instance); 192 | md.setKey(key); 193 | elements.put(key, md); 194 | } 195 | 196 | 197 | } 198 | } 199 | } 200 | } 201 | public void deleteElements(Class clazz){ 202 | 203 | Field[] fielsd = clazz.getDeclaredFields(); 204 | for(Field field : fielsd){ 205 | field.setAccessible(true); 206 | if(field.isAnnotationPresent(ZkValue.class)){ 207 | ZkValue zkValue = field.getAnnotation(ZkValue.class); 208 | String key = zkValue.key(); 209 | elements.remove(key); 210 | } 211 | } 212 | } 213 | } 214 | --------------------------------------------------------------------------------