CXFFrame
封装CXF框架(发布平台),业务模块作为SDK插入平台
目的:
1.封装WebService发布过程
2.对外发布一个WebService服务,即可包含不同的业务模块,统一入口/出口 参数类型
3.WebService服务 与 业务模块代码 解耦
4.业务组开发人员不用再关心WebService的发布,只要关注SDK业务模块的业务处理即可
5.业务模块可插拔,更灵活
平台功能:
1.发布WebService服务
2.加载SDK业务模块程序
3.加载SDK业务模块配置信息
4.对传递的参数做基本校验功能
5.SDK私有模块的实例化
CXFFrame平台源码已经上传GitHub,如果代码更新就在这个上面(CSDN下载包不更新),地址:CXFFrame
CXFFrame Linux服务器安装包,地址:CXFFrame平台 模块SDK Linux安装包
CXFFrame平台-SDK源码,地址:CXFFrame平台-SDKDemo源码
在平台中有一个抽象类ServiceBase,SDK模块主类继承这个类之后,实现三个方法,分别是:(调用顺序也是以下顺序)
loadConfig(HashMap<String, String> configs):从平台中获取sdk私有模块配置信息
init():sdk私有模块参数/资源初始化
process(String params):sdk私有模块主方法
下面讲解下CXFFrame平台的代码:
cxfframe_conf.properties
#CXF发布端口 CXFFRAME_PORT=9099 #CXF发布服务名 CXFFRAME_RELEASE_NAME=CXFFrame_QueryService
这个是平台发布WebService的必要参数,这里只要指定发布名称和端口,平台默认读取安装机器的IP地址
cxfframe_log4j.properties
log4j.rootCategory=DEBUG,stdout log4j.addivity.org.apache=true log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %l - %m%n log4j.logger.cxfframe=DEBUG,cxfframe log4j.appender.cxfframe=org.apache.log4j.RollingFileAppender log4j.appender.cxfframe.File=./log/cxfframe.log log4j.appender.cxfframe.Threshold=DEBUG log4j.appender.cxfframe.MaxBackupIndex=5 log4j.appender.cxfframe.MaxFileSize=1KB log4j.appender.cxfframe.layout=org.apache.log4j.PatternLayout log4j.appender.cxfframe.layout.ConversionPattern=%d %p %l - %m%n log4j.logger.sample=DEBUG,sample log4j.appender.sample=org.apache.log4j.RollingFileAppender log4j.appender.sample.File=./log/sample.log log4j.appender.sample.Threshold=DEBUG log4j.appender.sample.MaxBackupIndex=5 log4j.appender.sample.MaxFileSize=1KB log4j.appender.sample.layout=org.apache.log4j.PatternLayout log4j.appender.sample.layout.ConversionPattern=%d %p %l - %m%n这个是平台和sdk私有模块的log4j配置,当添加一个sdk后,需要在这个里面配置对应的logger配置。
这个配置是根据不同模块打印不同的地址信息。注意看模块名以及输出路径
平台加载log4j配置的时候,加入了log4j的监听器,支持动态修改log4j配置文件。
Modules.xml
<?xml version="1.0" encoding="UTF-8" ?> <!-- 配置模块SDK信息 --> <modules> <!-- 模块名和class全路径 --> <!-- 注意,模块名必须与模块工程名(Linux模块名)完全一致 --> <module name="sample" class="com.module.sample.Sample" /> <module name="gis" class="com.module.gis.GisServer" /> </modules>这个是平台获取sdk主类的配置,每次添加sdk模块,都需要在这里加上对应的配置信息。
name 就是 sdk模块的名字,这个名字很重要,很多地方都要保持一直,比如说安装服务器后再modules目录下的目录名,以及客户端调用服务端传的模块名
class 就是 sdk模块的主类全路径
CommConstans.java
package com.cxfframe.server.common; import com.cxfframe.server.util.CommonUtils; /** * 常量 * * @author CYX * @create 2017-05-24-21:41 */ public class CommConstans { //判断相对路径根路径 static { if (CommonUtils.isWindowsOS()) { SYSTEN_ROOT_PATH = "./"; } else { SYSTEN_ROOT_PATH = "../"; } } /** * 相对路径根路径 * Windows(./) Linux(../) */ public static String SYSTEN_ROOT_PATH; /** * CXFFrame配置文件路径 */ public static final String CXFFRAME_CONF_PATH = SYSTEN_ROOT_PATH + "conf/cxfframe_conf.properties"; /** * CXFFrame_log4j配置文件路径 */ public static final String CXFFRAME_LOG4J_CONF_PATH = SYSTEN_ROOT_PATH + "conf/cxfframe_log4j.properties"; /** * modules目录的路径 */ public static final String MODULES_DIR_PATH = SYSTEN_ROOT_PATH + "modules"; /** * Modules.xml配置文件路径 */ public static final String MODULES_XML_PATH = SYSTEN_ROOT_PATH + "conf/Modules.xml"; }这里配置的是成员变量,主要是平台配置文件的路径
ExceptionInfoConstans.java
package com.cxfframe.server.common; /** * Exception错误信息 * * @author CYX */ public class ExceptionInfoConstans { /** * moduleName和Params参数错误 */ public static final String MODULENAME_PARAMS_EXCEPTION = "[ErrorCode-01] Error information : Server receives the information , moduleName or params is null , please check it "; /** * moduleName找不到对应模块 */ public static final String MODULENAME_CLASS_EXCEPTION = "[ErrorCode-02] Error information : The server can not find the corresponding module"; /** * 实例化模块错误 */ public static final String INSTANTIATE_MODULE_EXCEPTION = "[ErrorCode-03] Error information : Instantiate module error"; /** * 服务端模块处理错误 */ public static final String SERVER_PROCESS_EXCEPTION = "[ErrorCode-04] Error information : Server module processing error"; }这个配置的是平台/sdk处理异常后,返回给客户端的错误信息
KeyConstans.java
package com.cxfframe.server.common; /** * 配置文件中的key常量 * * @author CYX * @create 2017-05-24-22:15 */ public class KeyConstans { public static final String CXFFRAME_PORT = "CXFFRAME_PORT"; public static final String CXFFRAME_RELEASE_NAME = "CXFFRAME_RELEASE_NAME"; }这里配置的是properties配置文件中的key,常量
QueryService.java
package com.cxfframe.server.inter; import javax.jws.WebMethod; import javax.jws.WebParam; import javax.jws.WebService; /** * Created by CYX on 2017/5/24. */ @WebService public interface QueryService { @WebMethod String queryServerInformation(@WebParam(name = "ModuleName") String moduleName, @WebParam(name = "params") String params); }CXF发布的接口,这个不多说,使用过CXF或者WebService都知道,不了解的先去看之前的webService系列文章
QueryServiceImpl.java
package com.cxfframe.server.impl; import com.cxfframe.server.common.ExceptionInfoConstans; import com.cxfframe.server.inter.QueryService; import com.cxfframe.server.load.LoadConfig; import com.cxfframe.server.module.ModuleFactory; import com.cxfframe.server.module.ServiceBase; import com.cxfframe.service.CXFFrameRelease; import org.apache.cxf.common.util.StringUtils; import org.apache.log4j.Logger; /** * WebService接口实现 * * @author CYX * @create 2017-05-24-21:15 */ public class QueryServiceImpl implements QueryService { private final Logger logger = CXFFrameRelease.logger; public String queryServerInformation(String moduleName, String params) { // 参数为空,直接返回错误信息 if (StringUtils.isEmpty(moduleName) & StringUtils.isEmpty(params)) { logger.info(ExceptionInfoConstans.MODULENAME_PARAMS_EXCEPTION); return ExceptionInfoConstans.MODULENAME_PARAMS_EXCEPTION; } // 根据模块名找class,找不到直接返回错误信息 LoadConfig loadConfig = LoadConfig.getInstance(); if (loadConfig.getModulesConfigWithInfomation().get(moduleName).isEmpty()) { logger.info("module name : " + moduleName + " can not find the corresponding module"); return ExceptionInfoConstans.MODULENAME_CLASS_EXCEPTION;// moduleName找不到对应模块 } logger.info("==================== The server begins processing ===================="); logger.info("==================== The server begins processing ===================="); logger.info("module name : " + moduleName + " , params : " + params); String responseResult = ""; try { // 根据模块名获取模块实例,加载模块配置并初始化 ServiceBase serviceBase = ModuleFactory.getModuleInstance(moduleName); //实例化模块失败,返回错误信息 if (serviceBase == null) { logger.info(ExceptionInfoConstans.INSTANTIATE_MODULE_EXCEPTION); return ExceptionInfoConstans.INSTANTIATE_MODULE_EXCEPTION; } // 调用私有模块主方法 responseResult = serviceBase.process(params); } catch (Exception e) { logger.error(e.getMessage(), e); return ExceptionInfoConstans.SERVER_PROCESS_EXCEPTION; } logger.info("==================== The server is finished ===================="); logger.info("==================== The server is finished ===================="); return responseResult; } }CXF接口的实现。
在这个类中,会根据模块名,生成对应的模块实例,然后调用主方法,执行sdk业务,并返回指定数据
CXFFrameRelease.java
package com.cxfframe.service; import com.cxfframe.server.impl.QueryServiceImpl; import com.cxfframe.server.load.LoadConfig; import org.apache.log4j.Logger; import javax.xml.ws.Endpoint; /** * CXFFrame发布主程序 * * @author CYX * @create 2017-05-24-21:22 */ public class CXFFrameRelease { public static final Logger logger = Logger.getLogger("cxfframe"); public static void main(String[] args) { LoadConfig loadConfig = null; // 加载配置文件,出现异常,直接退出程序 try { loadConfig = LoadConfig.getInstance(); loadConfig.init(); } catch (Exception e) { logger.error(e.getMessage(), e); System.exit(1); } // 发布CXF服务(WebService) try { String address = "http://" + loadConfig.getCxfFrame_IP() + ":" + loadConfig.getCxfFrame_PORT() + "/" + loadConfig.getCxfFrame_ServiceName() + "?wsdl"; // CXFFrame主方法 Endpoint.publish(address, new QueryServiceImpl()); logger.info("CXFFrame release is success , address : " + address); } catch (Exception e) { logger.error(e.getMessage(), e); } } }CXFFrame平台发布主方法
LoadConfig.java
package com.cxfframe.server.load; import com.cxfframe.server.common.CommConstans; import com.cxfframe.server.common.KeyConstans; import com.cxfframe.service.CXFFrameRelease; import org.apache.commons.io.FileUtils; import org.apache.log4j.Logger; import org.apache.log4j.PropertyConfigurator; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import java.io.File; import java.io.FileFilter; import java.io.FileInputStream; import java.net.InetAddress; import java.util.HashMap; import java.util.List; import java.util.Properties; /** * 加载配置 * * @author CYX * @create 2017-05-24-21:39 */ public class LoadConfig { private final Logger logger = CXFFrameRelease.logger; //单例 private static class LoadConfigHolder { private static final LoadConfig INSTANCE = new LoadConfig(); } public static final LoadConfig getInstance() { return LoadConfigHolder.INSTANCE; } private String cxfFrame_IP; private String cxfFrame_PORT; private String cxfFrame_ServiceName; /** * 模块名和class全路径的映射 */ private HashMap<String, String> moduleNameWithClass = new HashMap<String, String>(20); /** * 存放私有模块的配置信息 * 模块名-(配置文件名-配置文件内容) */ private HashMap<String, HashMap<String, String>> modulesConfigWithInfomation = new HashMap<String, HashMap<String, String>>(20); /** * CXFFrame初始化 * * @throws Exception */ public void init() throws Exception { // 加载logger loadLogger(); // 加载配置文件 loadCXFFrameCofig(); // 加载所有模块的私有配置 loadModulesCofig(); // 加载modules.xml loadModulesXML(); } /** * 加载logger 支持动态改变log4j配置(类似于热部署) * * @throws Exception */ private void loadLogger() throws Exception { PropertyConfigurator.configure(CommConstans.CXFFRAME_LOG4J_CONF_PATH); PropertyConfigurator.configureAndWatch(CommConstans.CXFFRAME_LOG4J_CONF_PATH, 1000); } /** * 加载CXFFrame配置文件 * * @throws Exception */ private void loadCXFFrameCofig() throws Exception { Properties pro = new Properties(); pro.load(new FileInputStream(new File(CommConstans.CXFFRAME_CONF_PATH))); // 默认获取本地IP cxfFrame_IP = InetAddress.getLocalHost().getHostAddress(); cxfFrame_PORT = pro.getProperty(KeyConstans.CXFFRAME_PORT); cxfFrame_ServiceName = pro.getProperty(KeyConstans.CXFFRAME_RELEASE_NAME); logger.debug("cxfFrame_IP : " + cxfFrame_IP); logger.debug("cxfFrame_PORT : " + cxfFrame_PORT); logger.debug("cxfFrame_ServiceName : " + cxfFrame_ServiceName); } /** * 加载modules.xml * * @throws Exception */ private void loadModulesXML() throws Exception { String modulesResult = FileUtils.readFileToString(new File(CommConstans.MODULES_XML_PATH), "UTF-8"); // 解析modules.xml handleModulesXML(modulesResult); } /** * 解析modules.xml * * @param modulesResult * @throws Exception */ private void handleModulesXML(String modulesResult) throws Exception { Document document = DocumentHelper.parseText(modulesResult); Element rootElement = document.getRootElement(); // 获取modules节点下所有的module节点 List<Element> modules = rootElement.elements("module"); for (Element module : modules) { String moduleName = module.attributeValue("name");// 获取name的属性值 String moduleClass = module.attributeValue("class");// 获取class的属性值 moduleNameWithClass.put(moduleName, moduleClass); } } /** * 加载modules目录下所有模块的私有配置 * * @throws Exception */ private void loadModulesCofig() throws Exception { // modules目录下,所有的子模块 File[] moduleDirs = new File(CommConstans.MODULES_DIR_PATH).listFiles(new FileFilter() { public boolean accept(File pathname) { return pathname.isDirectory(); } }); for (File moduleDir : moduleDirs) { // 加载单个私有模块的配置信息 loadPrivatelyOwnedModuleConf(moduleDir); } } /** * 加载单个私有模块的配置 */ private void loadPrivatelyOwnedModuleConf(File moduleDir) throws Exception { String moduleName = moduleDir.getName();// 模块名 // 获取私有模块conf目录下所有配置文件 File[] configFiles = new File(moduleDir.getPath() + "/conf").listFiles(new FileFilter() { public boolean accept(File pathname) { return pathname.isFile(); } }); // 循环读取conf目录下的配置文件 HashMap<String, String> configFileNameWithinfo = new HashMap<String, String>(); if (null != configFiles) { for (File configFile : configFiles) { String configFileName = configFile.getName();// 文件名 String configInformation = FileUtils.readFileToString(configFile, "UTF-8");// 文件内容 // 文件名-文件内容,存入map configFileNameWithinfo.put(configFileName, configInformation); } } else { logger.info("module Name : " + moduleName + " , configFiles is null"); } // 模块名-(配置文件名-配置文件内容) modulesConfigWithInfomation.put(moduleName, configFileNameWithinfo); logger.info("All modules private configuration" + modulesConfigWithInfomation); } public String getCxfFrame_IP() { return cxfFrame_IP; } public String getCxfFrame_PORT() { return cxfFrame_PORT; } public String getCxfFrame_ServiceName() { return cxfFrame_ServiceName; } public HashMap<String, String> getModuleNameWithClass() { return moduleNameWithClass; } public HashMap<String, HashMap<String, String>> getModulesConfigWithInfomation() { return modulesConfigWithInfomation; } }这个类是加载平台配置信息,以及SDK私有模块配置信息的。
上面代码注释写的已经很清楚了。
ModuleFactory.java
package com.cxfframe.server.module; import com.cxfframe.server.load.LoadConfig; import com.cxfframe.service.CXFFrameRelease; import org.apache.cxf.common.util.StringUtils; import org.apache.log4j.Logger; import java.util.HashMap; /** * 模块工厂,负责创建模块对象 * * @author CYX * @create 2017-05-25-11:00 */ public class ModuleFactory { private static final Logger logger = CXFFrameRelease.logger; /** * 获取模块实例 * * @param modeulName * @return */ public static ServiceBase getModuleInstance(String moduleName) { LoadConfig loadConfig = LoadConfig.getInstance(); ServiceBase serviceBase = null; // 实例化模块 Object obj = null; try { // 获取模块对应的class路径 String moduleClass = loadConfig.getModuleNameWithClass().get(moduleName); logger.info("module : " + moduleName + " , class : " + moduleClass); if (StringUtils.isEmpty(moduleClass)) { logger.info("module : " + moduleName + " , class is null"); return null; } // 通过反射获取模块实例 Class exampleClass = Class.forName(moduleClass); obj = exampleClass.newInstance(); serviceBase = (ServiceBase) obj; // 加载模块并初始化 // 根据模块名,取出模块对应的配置文件 HashMap<String, String> moduleConfigNameWithInfo = loadConfig.getModulesConfigWithInfomation().get(moduleName); // 私有模块加载配置 serviceBase.loadConfig(moduleConfigNameWithInfo); // 私有模块初始化 serviceBase.init(); logger.info("===== Load Configuration Initialize the private module is complete ====="); } catch (Exception e) { logger.error(e.getMessage(), e); return null; } return serviceBase; } }这个类是根据模块名声称对应模块实例的主要方法
ServiceBase.java
package com.cxfframe.server.module; import java.util.HashMap; /** * 基类 * * @author CYX * @create 2017-05-25-11:01 */ public abstract class ServiceBase { /** * CXFFrame将模块对应的配置文件,传给对应私有模块 * * @param moduleConf * @throws Exception */ public abstract void loadConfig(HashMap<String, String> moduleConf) throws Exception; /** * 初始化 * * @throws Exception */ public abstract void init() throws Exception; /** * 模块主处理方法(子模块返回建议不要返回null) * * @param information * @return * @throws Exception */ public abstract String process(String information) throws Exception; }这个是给SDK私有模块主类继承用的
CommonUtils.java
package com.cxfframe.server.util; /** * 公共方法 * * @author CYX * @create 2017-05-24-21:43 */ public class CommonUtils { /** * 判断是否是windows操作系统 * * @return String */ public static boolean isWindowsOS() { boolean isWindowsOS = false; String osName = System.getProperty("os.name"); if (osName.toLowerCase().indexOf("windows") > -1) { isWindowsOS = true; } return isWindowsOS; } }公共方法
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com</groupId> <artifactId>CXF_Frame</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>CXF_Frame</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <cxf.version>3.1.11</cxf.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-frontend-jaxws</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http</artifactId> <version>${cxf.version}</version> </dependency> <!-- Jetty is needed if you're are not using the CXFServlet --> <dependency> <groupId>org.apache.cxf</groupId> <artifactId>cxf-rt-transports-http-jetty</artifactId> <version>${cxf.version}</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>dom4j</groupId> <artifactId>dom4j</artifactId> <version>1.6.1</version> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>commons-collections</groupId> <artifactId>commons-collections</artifactId> <version>3.2.2</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> </configuration> </execution> </executions> </plugin> </plugins> </build> </project>
这里平台的代码就讲解到这里...
这里贴上服务器上CXFFrame的结构图
SDK开发样例
在上面的安装包中,有详细的开发文档,欢迎互相讨论