这次开发过程中要给其他的系统提供数据,因为以前在开发接口的过程中有两个接口使用到了Web Service,但主要是我们调用别的系统提供的接口,而没有我们写Web Service给别的系统调用。不过也好,在上次开发的经验中知道了一点关于如何调用Web Service的一些经验。
在没有接触到Web Service的时候,曾经觉得Web Service是那么的高不可攀,觉得能写出Web Service是非常难的。因为每次见到Web Service的时候,总会想到远程调用,然后还会想到SOAP。上次开发调用其他系统的Web Service接口的时候,还专门借了一本SOAP的书,但是没有看几页就把书就还回去了(实在太复杂,没有继续往下看)。在网上看到可以使用Axis简化Web Service的开发,也从网上找到了一些调用Web Service的例子。
那么到底Web Service是什么呢?我不能很明确的说出来,这里就我这几天写的几个可以调用的Web Service来说一下自己感受,用Axis来开发Web Service的时候,没有必要把Web Service放在心上,因为在开发的过程中根本没有必要考虑这些Web Service,而且在实现类中也没有与Axis相关的内容。也就是说Web Service就是把一个普通的类,通过一个配置文件,发布在Axis框架上,就可以在外面用Axis提供的接口来获取返回结果。当然Axis能处理的数据类型也许有一些限制,Axis对于用户自定义类型也是可以支持的,只是需要单独的配置而已,对于自定义类型,我也没有实现过,Axis的user guider上有一些说明。
下面就说说开发Web Service的准备工作和开发的代码片断以及如何测试,并且说了一点自己在开发这个Web Service时的一些收获。
注意:因为发布Web服务是发布到某一个web应用中,为了方便下面的例子中都是针对http://localhost/bjah的。
配置Web应用使用Axis,需要在web.xml中进行相应的配置,配置信息如下:
在web.xml文件中添加了上面的配置信息以后,web应用就可以支持Web Service了。
这jar文件包括:
activation.jar axis-ant.jar axis-schema.jar axis.jar commons-discovery-0.2.jar commons-logging-1.0.4.jar jaxrpc.jar log4j-1.2.8.jar mailapi.jar saaj.jar wsdl4j-1.5.1.jar xmlsec-1.3.0.jar
进行了上述的配置之后,启动Web应用,就可以使用下面的命令来查看是否成功了,查看指定Web应用中已经发布的Web Service可以使用如下命令:
java org.apache.axis.client.AdminClient -lhttp://localhost/bjah/services/AdminService list
注意:运行这个命令的时候需要设置CLASSPATH,并且在CLASSPATH包括上面说到的Axis需要的jar文件。
If you want to deploy or undeploy the Web Service, you should use org.apache.client.AdminClient, and you should write a file contain the information about the Web Service, for example the Web Service’s name and the className of the Web Service, there are two example files, the deploy.wsdd file is for deploy the Web Service and the undeploy.wsdd file is for Undeploy. The content of the files are as below:
deploy.wsdd
undeploy.wsdd
The commands for deploy and undeploy the Web Service are:
java org.apache.axis.client.AdminClient -lhttp://localhost/bjah/services/AdminService deploy.wsdd
java org.apache.axis.client.AdminClient -lhttp://localhost/bjah/services/AdminService undeploy.wsdd
Actually deploy and undeploy is the same, just different at the configure files.
As I have described the implement of the Web Service is just a Java class which finish some work and return the result.
The simple examples are everywhere in the samples of the Axis’s example. I will show an example which return a byte[] to the client, which can return a binary file to the client. I show this example just because in my implement I need to return a letter to the client, and the letter may have attachments, I should return attachments to the client also, so return the binary file to the client is necessary.
For this requirement, the flow of my Web Service is get the parameter of the letter id, and query the letter from the database, then get the attachments from the database if it has. The content of the letter is stored in a xml file, and the attachment’s information is also in the xml file, the letter content and attachment’s xml file are stored in a temporary directory as the attachments are stored in the same directory. After all these are done, compress the directory to a zip file, then send the zip file to the client.
The class name is: BjahWebServicesImpl.java
Here is the code of the function getLetterContent
/** * * 获取信件内容,包括原始信件、信件附件、子信件、信件回复信息等 * * @param letId * 要获取信息的信件标识 * @return 获取的信件内容,这是一个zip文件的二进制数据 */ public byte[] getLetterContent(String letId) { log.info("get the letter's content"); String tmpDir = System.getProperty("user.dir"); String tmpDirName = "letdir" + UniversalFun.getTimeString(null, "yyyyMMddHHmmssSSS"); File letTmpDir = new File(tmpDir, tmpDirName); letTmpDir.mkdir(); log.debug("The letter's temp dir path is:" + letTmpDir.getAbsolutePath()); getOriginalLetter(letId, letTmpDir); getLetterAttachment(letId, letTmpDir); getSubLetters(letId, letTmpDir); getLetterReplys(letId, letTmpDir); // Create the zip file and return the bytearray of the zip file try { File tmpZipFile = File.createTempFile("let", "zip"); FileUtils.makeZipFile(letTmpDir.getAbsolutePath(), tmpZipFile .getAbsolutePath()); FileInputStream fis = new FileInputStream(tmpZipFile); byte[] buffer = new byte[1024]; ByteArrayOutputStream bos = new ByteArrayOutputStream(2048); int byteReaded = 0; for (; (byteReaded = fis.read(buffer)) > 0;) { bos.write(buffer, 0, byteReaded); } fis.close(); byte[] zipFileContent = bos.toByteArray(); bos.close(); tmpZipFile.deleteOnExit(); // delete the temp dir FileUtils.deleteFile(letTmpDir.getAbsolutePath()); return zipFileContent; } catch (IOException e) { e.printStackTrace(); } return null; }
There are some other functions I am not show here.
After compile the BjahWebServicesImpl.java, put all the classes this class needed to the WEB-INF/classes(class file) or WEB-INF/lib(jar file), and deploy the Web Service. Then you can invoke the Web Service you just deployed.
How to test the Web Service, you should write a tester to invoke the Web Service, and get the return value.
The following is my test code for the getLetterContent interface.
private static String endPoint = "http://localhost/bjah/services/BjahWebServices"; /** * 测试获取信件内容,包括获取信件的附件信息,子信件信息,信件回复信息等 * */ public static void testGetLetterContent() { String letId = "30002995"; try { Service service = new Service(); Call call = (Call) service.createCall(); call.setTargetEndpointAddress(new java.net.URL(endPoint)); call.setOperationName(new QName("http://webservice.bjah.com/", "getLetterContent")); call.addParameter("letId", org.apache.axis.Constants.XSD_STRING, javax.xml.rpc.ParameterMode.IN); call.setReturnType(org.apache.axis.Constants.XSD_BASE64); try { byte[] ret = (byte[]) call.invoke(new Object[] { letId }); if (ret != null) { File zipFile = new File("E:/Stephen/orilet" + letId + ".zip"); System.out.println(zipFile.getAbsolutePath()); FileOutputStream fos = new FileOutputStream(zipFile); fos.write(ret); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (ServiceException e) { e.printStackTrace(); } }
First create a Service object, and use the service to create a Call object, set the call the endpoint which is the Web Service we deployed, and set the parameters and the parameter’s data type and the return type, then use the call’s invoke method to call the Web Service and it will return the result the Web Service returned.
在学习Web Service的开发过程中确实学到了一些东西,比如测试Web Service的功能,因为Web Service和普通的java类是一样的,因此第一步测试的时候就不用发布到Web应用上,然后写一个测试程序调用Web Service来测试,而是可以直接写一个测试程序,调用Web Service提供程序,这样在测试功能方面会提高很多的效率。因为发布Web Service是很简单的,主要是环境的配置,然后还有需要使用到的包存放位置等。主要是第一次的发布,第一次发布成功以后,后面的发布就会简单多了。
在我测试的过程中,因为开发的时候是在Tomcat上开发的,而实际应用的时候却是需要在WebSphere上发布,在这个转移过程中遇到了一些问题,主要还是自己的配置文件写的有问题,因为一时没有发现,而导致走了很多的弯路。因为有些类需要放在AppServer/lib下面,有些类只要放在WEB-INF/lib或者WEB-INF/classes下就可以了。关键是要指定好这些类的具体存放位置,如果实在不知道,把jar文件都放在WEB-INF/lib下,然后根据日志文件看看哪些类找不到,把相应的jar文件拷贝到AppServer/lib下就可以了。
再则就是连接数据库类的更新,应用程序都需要连接数据库,每次开发的时候可能连接数据库的方法还不同,而且连接的数据库也可能不同,因此要在程序开发过程中方便的切换连接数据库的方法和连接不同的数据库,我曾经写了一个工具类,这个类是根据配置文件获取数据库连接,可以根据jndi来获取数据库连接还可以通过jdbc使用apache的DBCP作为数据库连接池来获取连接,对于这个类当初设计出来的时候,感动很自豪,因为可以很方便的操作数据库了。现在因为我们的程序是在普元EOS上开发的,因此获取数据库连接又要通过它们提供的来操作,否则当数据库改变的时候,将要单独改变,这一次再一次修改了这个工具类,并且加了一个接口,这个接口有一个getConnection的方法,这样在适应不同的应用程序开发中又有了一些改进。自己还是比较满意的。
操作文件的工具类的生成,对文件操作也许并没有什么复杂,但是每次操作文件都单独来写的话,会很烦琐,而且也造成了相同功能的重复定义,或者只是代码的复制粘贴,这样对系统的维护也不是很好,很高心这次把对文件操作的类进行了工具化,主要有1. 删除文件或者文件夹的功能,这个函数进行的是递归删除;2. 指定要生成的zip文件名称以及指定的文件夹名称,把文件夹中的内容压缩到指定的zip文件中;3. 获取一个文件的扩展名,对于web应用来说,这个功能可能是比较有用的,因为有时下载文件的时候,需要根据文件扩展名来确定文件的类型,以及显示图片的时候,也要根据扩展名来判断是否图片;4. 保存字符串到文件中,这个功能也比较有用,有时我们得到一个字符串,要把这个字符串保存到指定的文件中,就会写一段不是很长的代码来保存文件到指定的文件中,把这个功能抽取出来,也可以减少代码量,也实现了代码的复用。