目录
前言
最近做了一个SOA相关的项目,用到了Fuse ESB,也就是ServiceMix的商业版本,其理论来源于JBI,因为是第一次做这样的想,遇到了一些麻烦,做个笔记,希望对后参与类似项目的同学有帮助.
一、JBI原理
JBI(Java 业务集成),是sun公司发布的一个用于java应用组件进行集成的一个标准,其核心是一下几个部分:
规格化消息路由(NM)
绑定组件(BC)
服务引擎(SE)
像servlet容器,EJB容器,JMS容器只是一个服务的提供者,并不是服务的集成者。
JBI利用BC专门来接收各种协议,再用BC转发给外部的服务提供者,只是作为一个代理。
SE组件不负责对外联系,只处理JBI容器内部的消息
各种BC和SE之间是不能直接通信的,他们把NM送到NMR,然后NMR再把NM送到不同的SE和BC。
JBI希望消除各种专有协议,JBI内部只有一种标准的消息,就是NM,在JBI内部不论是请求还是回答消息,都是先转个NMR,再由NMR来分发。
JBI把服务分为服务提供者和服务请求者,这2者不能直接通信,所有请求消息都通过传输通道(Deliver Channel)送到NMR,再有NMR通过DC转给消息提供者。
JBI内部的SE只是处理规格化了的消息,所以任何外部请求消息进入JBI环境之前要先进入BC,BC把非规格化的消息转换为规格化消息,再送到NMR。NMR把消息送到SE,SE可以做些消息规格的转换,或者集成一些服务后,将消息传给NMR,NMR再把消息给BC,BC将规格化消息转化为与外部服务提供者一致的传输协议,再转给外部服务提供者。
参考资料<SOA思想,技术与系统集成应用详解> 梁爱虎
二、经验总结
1. 作为一个初学者在用Fuse的CXF 开发web service时不用要复杂的package和文件夹层次结构,否在在配置sa的jbi.xml文件时可能会搞乱。
2. Sa.zip(相当于ear文件)文件可以包含多个bc su.zip(相当于ejb.jar和web.war)和se su.zip(相当于ejb.jar和web.war)文件,他们的配置文件都是META-INF目录下的jbi.xml文件
3. 在项目中我建议的文件目录结构是
clip_p_w_picpath002[4]
其中最外层的jbi.xml文件至关重要。稍有失误,则整个sa.zip文件将不能发布成功,出现找不到文件,或不能创建文件夹的错误。
4. webservice的发布端口和jbi容器的端口没有关系,可以随便写
5. 把打包好的zip文件拷贝到fuse servicemix下的hotdeploy目录下即可自动部署。如下图
clip_p_w_picpath004[4]
如上图所示,部署一个简单的服务需要如下文件结构:
3个jbi.xml文件,2个xbean.xml文件,接口和实现类的.class文件
主目录,即sazip下META-INF下的JBI文件,用例设置其下面的se su,bc su的信息,目录等。
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<jbi xmlns="http://java.sun.com/xml/ns/jbi" version="1.0">
<service-assembly>
<identification>
<name>teacher</name>
<description>teacher</description>
</identification>
<service-unit>
<identification>
<name>cxf-bcc-TeacherImplService</name>
<description>cxf-bcc-TeacherImplService
</description>
</identification>
<target>
<artifacts-zip>cxf-bcc-TeacherImplService.zip
</artifacts-zip>
<component-name>servicemix-cxf-bc</component-name>
</target>
</service-unit>
<service-unit>
<identification>
<name>cxf-se-koolearn.TeacherImpl</name>
<description>cxf-se-koolearn.TeacherImpl
</description>
</identification>
<target>
<artifacts-zip>cxf-se-koolearn.TeacherImpl.zip
</artifacts-zip>
<component-name>servicemix-cxf-se</component-name>
</target>
</service-unit>
</service-assembly>
</jbi>
Se su的META-INF下的jbi.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<jbi xmlns="http://java.sun.com/xml/ns/jbi" version="1.0">
<services binding-component="false"/>
</jbi>
可以跟bc su下的jbi.xml内容相同。
SE su下xbean.xml文件内容:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:cxfse="http://servicemix.apache.org/cxfse/1.0">
<cxfse:endpoint>
<cxfse:pojo>
<bean class="koolearn.TeacherImpl" />
</cxfse:pojo>
</cxfse:endpoint>
</beans>
BC su下xbean.xml文件内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:cxfbc="http://servicemix.apache.org/cxfbc/1.0"
xmlns:TeacherImplService="http://koolearn/">
<cxfbc:consumer wsdl="classpath:wsdl/teacher.wsdl"
targetService="TeacherImplService:TeacherImplService" targetEndpoint="TeacherImplService:TeacherImplPort" />
</beans>
要测试,还得写个客户端如:TeacherClient.java
代码如下:
package koolearn;
import javax.xml.namespace.QName;
import javax.xml.ws.Service;
import javax.xml.ws.soap.SOAPBinding;
public class TeacherClient {
public static void main(String[] args) throws Exception {
QName serviceName = new QName("http://koolearn/", "TeacherImplService");
QName portName = new QName("http://koolearn/", "TeacherImplPort");
Service service = Service. create(serviceName);
service.addPort(portName, SOAPBinding. SOAP11HTTP_BINDING,
"http://localhost:9090/TeacherImplPort");
koolearn.Teacher client = service.getPort(portName,
koolearn.Teacher. class);
System. out.println(client.sayHello("各位同事六一节快乐"));
}
}
Sazip的目录结构:
clip_p_w_picpath006[4]
Se suzip目录结构如下:
clip_p_w_picpath008[4]
Bc suzip的目录结构如下
clip_p_w_picpath010[4]
三、ServiceMix开发实例
3.1定义一个interface
package koolearn;
import javax.jws.WebMethod;
import javax.jws.WebParam;
import javax.jws.WebService;
@WebService(name = "Teacher", targetNamespace = "http://koolearn/")
public interface Teacher {
@WebMethod(operationName = "sayHello", action = "urn:SayHello")
public String sayHello(@WebParam(name = "name") String name);
}
5.2 做一个实现
6. package koolearn;
7.
8. import javax.jws.WebService;
9.
10.@WebService(name = "TeacherImpl", endpointInterface = "koolearn.Teacher", targetNamespace = "http://koolearn/", portName = "TeacherImplPort", serviceName = "TeacherImplService")
11. public class TeacherImpl implements Teacher {
12.
13. @Override
14. public String sayHello(String name) {
15.
16. return "Hello " + name;
17. }
18.
19. }
3.3通过以上接口和实现类,用CXF 2.2 的java2ws(2.1以前版本都是java2wsdl)生成wsdl文件。Wsdl文件就不贴了,我用的CXF是Fuse提供的封装版,这是为了尽可能保证和Fuse sevicemix的兼容性,开源软件没办法!
clip_p_w_picpath012
其Ant任务如下:
<target name="java2ws" depends="compile" description="通过java 接口生成wsdl文件">
<java classname="org.apache.cxf.tools.java2ws.JavaToWS" classpath="${build}/service" fork="true">
<arg value="-o" />
<arg value="${build}/service/koolearn/teacher.wsdl" />
<arg value="-wsdl" />
<arg value="koolearn.TeacherImpl" />
<classpath>
<path refid="cxf.classpath" />
</classpath>
</java>
</target>
其中红色的可以转为命令行下的java2ws 的参数。只要提供实现类就可以了,不用interface。
四.配置和获取ESB jndi数据源 1.首先要配置好数据源
esb/conf/jndi.xml文件
<entry key="java:comp/env/jdbc/koolearn">
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
<property name="url" value="jdbc:oracle:thin:@192.168.21.241:1521:orcl"/>
<property name="username" value = "koolearn"/>
<property name="password" value = "koolearn"/>
</bean>
</entry>
2.获取数据源
Hashtable<String, String> env = new Hashtable<String, String>();
InitialContext ctx;
env.put(Context.INITIAL_CONTEXT_FACTORY,
"org.apache.xbean.spring.jndi.SpringInitialContextFactory");
ctx = new InitialContext(env);
Properties properties = new Properties();
InputStream in = MediaResourceImpl.class.getClassLoader()
.getResourceAsStream("servicemixjndi.properties");
try {
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
}
DataSource ds = (DataSource) ctx.lookup(properties
.getProperty("datasource"));
3.Spring方式获取数据源
因为CXF基于spring,所以也可以采用spring 的方式访问数据源,在xbean.xml中做如下配置
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:cxfse="http://servicemix.apache.org/cxfse/1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<cxfse:endpoint>
<cxfse:pojo>
<bean class="com.koolearn.eclass.teacher.webservice.TeacherWebServiceImpl">
<property name="dataSource">
<ref bean="dataSource" />
</property>
</bean>
</cxfse:pojo>
</cxfse:endpoint>
<bean id="dataSource" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName">
<value>java:comp/env/jdbc/eclassdatasource</value>
</property>
</bean>
</beans>
五.其他常见问题 问题1: org.apache.cxf.interceptor.Fault: Could not send Message.
与esb服务器通讯不正常
解决办法:
1. 在IE中查看wsdl是否发布成功
2. ESB端口是否打开
3. 客户端调用的endpointAddress是否有拼写错误
问题2: ESB启动过程出现不能加载cglib的情况
解决办法:
esb在发布的时候也会出现缺少asm包和cglib jar包的时候,拷贝这2个jar包到$esb_home/lib/,比应用自身下面的jar好解决问题
问题3:客户端调用web serivce中遇到null指针异常
注意要在客户加入CXF的包不要太多,只要8个就好,过多的话,可能与其他包有冲突,注意不是jdk6自带的,虽然包名是一样的.
问题4:wsdl文件不能生成
在用FID生成wsdl过程中,需要确保,接口文件,JAXB相关类的XML配置没有任何错误,引用的jar包不能有冲突,负责可能不能生成wsdl,或者生成的wsdl不完全,
问题5:apache.cxf.binding.soap.SoapFault:
Could not parse message.Caused by: com.ctc.wstx.exc.WstxParsingException: Received non-all-whitespace CHARACTERS or CDATA event in nextTag(). at [row,col {unknown-source}]: [1,127]
客户端和服务器端的SOAP协议版本不一致
解决办法:
1:把SOAP改用1.1即可
2:在客户端指定用SOAP1.2版本,建议采用方法3在本项目中
问题6:在eclipse中不能配置Tomcat,即不能添加Tomcat6
原因是tomcat home目录下的conf文件夹
我添加了context.xml的内容有错误,所以不能添加了。
问题7:在linux下读取文件的灵异事件
在java io中不要使用缩写"~"应该使用全路径,如/home/achilles
否则不会创建文件,会在内存中存放,当时环境下,看不到文件
一会会出现文件,这次是照片事件
问题8:不能在web-inf/classes目录生成.class:
可能是引入的jar有错误,如引用路径不对等原因.
解决办法:在项目的properties的java build path里仔细检查jar路径,把有×××”!”的jar包重新配置.
问题9:为eclipse做个广告
Eclipse从3.5版本开始,已经具备了非常完善的Java EE 开发能力和可视化开发能力,如JSP,Servlet,JPA,JSF,EJB3,XML,javascript,properties文件等,可以称得上是一个完善的Java EE开发工具,已经达到了当年JBuilder2006的开发能力, 不再需要任何的非官方插件,如myeclipse,BEA workshop等,而且eclipse是免费的,各位同学可以用用,非常完美.
当然喜欢NetBeans,JDeveloper,IDEA,JBuilder2006及以前版本的同学除外.
Eclipse主要分为几个版本:Java EE版,C++版,PHP版,Plug-In版,报表版,模型开发版,方便了不同同学的不同需要.
如果需要插件:oracle的OEPE 11g, JBOSS的JBoss Tools 3也是非常好的选择.可以用来开发Struts,Seam等应用.