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开发样例