一个WebService+Ejb 3.0的简单实现
WebService这个术语相信近两年来大家都不陌生,关于它的含义以及影响我在此就不作描述,本篇文章只是配合EJB 3.0 实现一个webService的小例子,这个例子是在上篇文章《一个简单 的Ejb 3.0实现》基础上的实现的,因为我不得不承认在写这篇文章的时候关于怎么去配置环境还是比较麻烦的,每个人所用的环境和配置都可能有区别,所以我只是统一一下本文所用的环境,不多叙述,进入正题 .
一:准备工作
在《一个简单 的Ejb 3.0实现》一文中,我们提到它需要六个jar包,现在我们再增加8个,它们是:
jbossws-client.jar
mail.jar
activation.jar
jbossall-client.jar
wsdl4j.jar
jboss-xml-binding.jar
xercesImpl.jar
jboss-common.jar
关于这8个jar包,在jboss 4.0.5 里面都可以找到,她们可能不在一个文件夹下,但肯定有的。至于build.xml打包应用时,可以不打进去,因为不要把你的应用搞的那么大(本人观点,仅作参考)。
二:服务器端
/**/ /*
*
* Copyright 2007-2008 neil
*
*/
package com.qy.webservice.bean;
import java.rmi.Remote;
import javax.jws.WebMethod;
import javax.jws.WebService;
import javax.jws.soap.SOAPBinding;
import javax.jws.soap.SOAPBinding.Style;
/** */ /**
*
* @author neil
* @version 1.0
*
*/
@WebService
@SOAPBinding(style = Style.RPC)
public interface LoginDao extends Remote ... {
@WebMethod
public boolean isLogin(String name, String password);
}
在LoginDao类中要注意的是Remote接口是要实现的,@SOAPBinding(style=Style.RPC),Soap的绑定方式也是需要的,不然在客户端是找不到LoginDao 的。其它的annotation 的含义可以去到网上查,本文也不多作描述。
/**/ /*
*
* Copyright 2007-2008 neil
*
*/
package com.qy.webservice.bean;
import java.util.List;
import javax.ejb.Stateless;
import javax.jws.WebService;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
/** */ /**
*
* @author neil
* @version 1.0
*
*/
@Stateless
@WebService(endpointInterface = " com.qy.webservice.bean.LoginDao " )
public class LoginDaoBean ... {
@PersistenceContext
protected EntityManager em;// the manager of entity
public boolean isLogin(String name, String password)...{
// define query sentence
StringBuffer hql = new StringBuffer();
hql.append("from User u where u.name='" + name + "'");
hql.append(" and u.password='" + password + "'");
// create the query
Query query = em.createQuery(hql.toString());
List queryList = query.getResultList();
// if the result is null
if (queryList.size() == 0) ...{
return false;
}
// if the user's length greater 1
if (queryList.size() > 1) ...{
return false;
}
// return single user
return true;
}
}
至此服务器端的类是建好了,这里又两个问题,需要说明一下
A. 两个类的方法中都没有抛出异常,可不可以抛出呢? 可以。但到实现SOA的时候会有一些问题,就是异常在经axis 通过WSDL2Java 生成后,在程序运行时有可能会出现找不到的情形。所以本文为了让大家在仿照这个例子时都能成功,所以也就抛弃了异常的抛出。
B. 两个类的方法的返回值是基本类型,而不是自定义类型,为什么会这样呢,自定义类型可以不可以呢,可以,肯定可以,在作SOA时也肯定可以,但是呢,它在实现这个webService的时候就多少有些复杂,既然是简单的应用,我就本着简单的原则,来向大家描述它。
三:客户端
/**/ /*
*
* Copyright 2007-2008 neil
*
*/
package com.qy.webservice.client;
import java.net.URL;
import javax.xml.namespace.QName;
import javax.xml.rpc.Service;
import javax.xml.rpc.ServiceFactory;
import com.qy.webservice.bean.LoginDao;
/** */ /**
*
* @author neil
* @version 1.0
*
*/
public class LoginClient ... {
public static void main(String[] args) throws Exception ...{
String userName = args[0];
String password = args[1];
URL url = new URL("http://xue:8080/empdEjb/LoginDaoBean?wsdl");
QName qname = new QName("http://bean.webservice.qy.com/jaws",
"LoginDaoService");
ServiceFactory factory = ServiceFactory.newInstance();
Service service = factory.createService(url, qname);
LoginDao loginDao = (LoginDao) service.getPort(LoginDao.class);
boolean isExists = loginDao.isLogin(userName, password);
if (isExists) ...{
System.out.println("hello " + userName);
} else ...{
System.out.println("sorry " + userName
+ ", you are not user in the system!");
}
}
}
2. 注意问题
当你把服务器端发布出去以后,服务器端会自动发布一个webService,并且生成并发布一个WSDL文件,通过访问http://xue:8080/empdEjb/LoginDaoBean?wsdl这个网址是可以找到的,但是更通用的是http://localhost:8080/empdEjb/LoginDaoBean?wsdl,当然这是针对在服务器端和客户端在一个机子上的情况下。
http://bean.webservice.qy.com/jaws 和 LoginDaoService 字符串,在客户端程序当中出现了上面两个字符串,它们在你生成的wsdl 文件中有详细的描述。这里你所需要做的是按照你自己的情况编写你自己的客户端程序。
至于userName 和password, 是你自己在数据库中的值,你可以按照你自己程序的逻辑和想要的结果去修改这两个值。这个环境的配置以及数据库的创建在《一个简单 的Ejb 3.0实现》中有详细的介绍,如果不清楚由来,可以去查看。
启动服务器后,在机子上运行客户端程序就可以了。别说不知道带main方法的java类怎么运行哦。
我机子上的结果是,hello liyh
webService就这么简单,这就是一个标准的webService,很久以前我不明白webService为什么会这么简单,在我的概念当中,它应该是复杂的,但是请你记住,任何好的技术,实现功能强大的技术,都是由很普通的技术组成,它就是这么简单。我当时总认为表现形式太变态了,既然是webService,为什么会是这样的,它难道不是通过网页的表现形式,让人们使用起来更智能吗,难道不是打开一个网页,输入我不同的值,显现不同的结果吗,当时我做第一个webService的时候,我没有文档,没有人帮我,我拼命的在google上搜索,所能做出来的就是这个样子,但我总认为它达不到我心中的要求,达不到我所认为的智能。于是我在不断的去思考,到最后我决定用axis去解析wsdl,然后去编写自己的jsp去实现它,重新发布一个应用,只是我不知道那个技术已经不是webService,那个实现就是社会上叫的SOA,只是缺少了总线的概念,也就是BUS,目前BEA,IBM在做SOA的时候都有自己的总线,利用总线可以把你发布出去的webService转化得更为通用。
当你把自己的WEBSERVICE通过UDDI(Universal Description、 Discovery and Integeration)进行注册发布后,全世界的人就可以根据你发布的WEBSERVICE,编写自己的客户端你访问你的程序。
下一篇文章我会介绍一下SOA,但也是根据这个程序来的,尤其是WSDL文件,虽然它是另外新建了一个工程,但是你如果不好好理解这篇文章,你将会不名所以。
在这篇文章已经写完的时候,我忽然觉得有必要将《一个简单 的Ejb 3.0实现》中所用到的build.xml文件向大家再作一些说明,以帮助那些刚开始运用此技术的人。因为由于加入了这个功能,那个build.xml文件已经作了一些修改,下面是修改后的文件,请作参考。
< property name ="app.home" value ="." />
< property name ="app.name" value ="empd" />
< property name ="javadoc.pkg.top" value ="com" />
< property name ="src.home" value ="${app.home}/src" />
< property name ="lib.home" value ="${app.home}/lib" />
< property name ="classes.home" value ="${app.home}/classes" />
< property name ="deploy.home" value ="${app.home}/deploy" />
< property name ="doc.home" value ="${app.home}/doc" />
< property name ="web.home" value ="${app.home}/web" />
< property name ="build.home" value ="${app.home}/build" />
< property name ="build.classes" value ="${build.home}/WEB-INF/classes" />
< property name ="build.lib" value ="${build.home}/WEB-INF/lib" />
< property name ="jboss.deploy.home" value ="E:jboss-4.0.5serverdefaultdeploy" />
<!-- ==================== Compilation Classpath =========================== -->
<!--
This section creates the classpath for compilation.
-->
< path id ="compile.classpath" >
<!-- The object files for this application -->
< pathelement location ="${classes.home}" />
<!-- The lib files for this application -->
< fileset dir ="${lib.home}" >
< include name ="*.jar" />
< include name ="*.zip" />
</ fileset >
</ path >
<!-- ==================== Build Targets below here========================= -->
<!-- ==================== "help" Target =================================== -->
<!--
This is the default ant target executed if no target is specified.
This helps avoid users just typing 'ant' and running a
default target that may not do what they are anticipating...
-->
< target name ="help" >
< echo message ="Please specify a target! [usage: ant <targetname>]" />
< echo message ="Here is a list of possible targets: " />
< echo message =" clean-all.....Delete build dir, all .class and war files" />
< echo message =" prepare.......Creates directories if required" />
< echo message =" compile.......Compiles source files" />
< echo message =" build.........Build war file from .class and jar file other files" />
< echo message =" deploy........Copy war file to the webapps directory" />
< echo message =" javadoc.......Generates javadoc for this application" />
</ target >
<!-- ==================== "clean-all" Target ============================== -->
<!--
This target should clean up any traces of the application
so that if you run a new build directly after cleaning, all
files will be replaced with what's current in source control
-->
< target name ="clean-all" >
< delete dir ="${build.home}" />
< delete dir ="${deploy.home}" />
<!-- delete the javadoc -->
< delete dir ="${doc.home}" />
</ target >
<!-- ==================== "prepare" Target ================================ -->
<!--
This target is executed prior to any of the later targets
to make sure the directories exist. It only creates them
if they need to be created....
Other, similar, preparation steps can be placed here.
-->
< target name ="prepare" >
< mkdir dir ="${deploy.home}" />
< mkdir dir ="${doc.home}" />
< mkdir dir ="${doc.home}/api" />
< mkdir dir ="${build.home}" />
< mkdir dir ="${build.home}/WEB-INF" />
< mkdir dir ="${build.home}/WEB-INF/classes" />
< mkdir dir ="${build.home}/WEB-INF/lib" />
</ target >
<!-- ==================== "compile" Target ================================ -->
<!--
This only compiles java files that are newer
than their corresponding .class files.
-->
< target name ="compile" depends ="prepare" >
< javac srcdir ="${src.home}" destdir ="${classes.home}" debug ="yes" >
< classpath refid ="compile.classpath" />
</ javac >
</ target >
<!-- ==================== "build" Target ================================== -->
<!--
This target builds the war file for the application
by first building the directory structure of the
application in ${build.home} and then creating the
war file using the ant <war> task
-->
< target name ="build" depends ="compile" >
<!-- Copy all the webapp content (jsp's, html, tld's, xml, etc. -->
<!-- Note that this also copies the META-INF directory -->
< copy todir ="${build.home}" >
< fileset dir ="${web.home}" />
</ copy >
<!-- Now, copy all the Java class files -->
< copy todir ="${build.home}/WEB-INF/classes" >
< fileset dir ="${classes.home}" >
< exclude name ="com/qy/ejb/**" />
< exclude name ="com/qy/webservice/bean/**" />
</ fileset >
</ copy >
<!-- Now, copy all the properties files, etc that go on the classpath -->
< copy todir ="${build.home}/WEB-INF/classes" >
< fileset dir ="${src.home}" >
< include name ="**/*.properties" />
< include name ="**/*.prop" />
</ fileset >
</ copy >
<!-- Now, copy all the jar files we need -->
< copy todir ="${build.home}/WEB-INF/lib" >
< fileset dir ="${lib.home}" >
< exclude name ="xercesImpl.jar" />
< exclude name ="wsdl4j.jar" />
< exclude name ="mail.jar" />
< exclude name ="jboss-xml-binding.jar" />
< exclude name ="jbossws-client.jar" />
< exclude name ="jboss-common.jar" />
< exclude name ="jbossall-client.jar" />
< exclude name ="activation.jar" />
</ fileset >
</ copy >
<!-- Create the <war> file -->
< jar jarfile ="${deploy.home}/${app.name}.war"
basedir ="${build.home}" />
< jar jarfile ="${deploy.home}/${app.name}Ejb.jar" >
< fileset dir ="${classes.home}" >
< include name ="com/qy/ejb/**" />
< include name ="com/qy/webservice/bean/**" />
</ fileset >
< metainf dir ="${app.home}/META-INF" >
< include name ="persistence.xml" />
</ metainf >
</ jar >
</ target >
<!-- ==================== "deploy" Target ================================= -->
<!--
This target simply copies the war file from the deploy
directory into the Tomcat webapp directory.
-->
< target name ="deploy" depends ="build" >
<!-- Copy the contents of the build directory -->
< copy todir ="${jboss.deploy.home}" file ="${deploy.home}/${app.name}.war" />
< copy todir ="${jboss.deploy.home}" file ="${deploy.home}/${app.name}Ejb.jar" />
</ target >
<!-- ==================== "doc" Target ==================================== -->
<!--
This task creates javadoc. It is dependent upon only the
'compile' target so it is not executed in a normal build.
As a result, the target needs to be run on its own.
-->
< target name ="javadoc" depends ="compile" >
< javadoc sourcepath = "${src.home}"
destdir = "${doc.home}/api"
packagenames = "${javadoc.pkg.top}.*" />
</ target >
</ project >