java调用WCF问题

简介: 过去微软.NET的ASMX Web Service已被大家广泛应用﹐但在信息安全日愈重视之下﹐微软有意以WCF取代原有的 ASMX Web Service。WCF 具有许多先进的技术﹐而跨平台作业已是现在不可避免的问题﹐同样是微软的 Solution之下如何使用WCF应该不是什么问题﹐但在不同的平台上是否有那么容易呢?因此这里以 Java 实作如何来调用具有使用身份验证的 WCF﹐

过去微软.NET的ASMX Web Service已被大家广泛应用﹐但在信息安全日愈重视之下﹐微软有意以WCF取代原有的 ASMX Web Service。WCF 具有许多先进的技术﹐而跨平台作业已是现在不可避免的问题﹐同样是微软的 Solution之下如何使用WCF应该不是什么问题﹐但在不同的平台上是否有那么容易呢?因此这里以 Java 实作如何来调用具有使用身份验证的 WCF﹐并以WCF 预设的wsHttpBinding 及一般常使用的 basicHttpBinding 的系结方式实作。

我本身并非专研 Java﹐但既然日后使用了 WCF 也势必面临 Java 或其它平台的呼叫﹐Java 是Open Source 具有多种 Framework﹐且有多种开发工具﹐参考了网络上许多范例与讨论﹐Java 对于WCF比较常用的是 ASIX 和 Metro 套件﹐因此这里主要使用 NetBeans IDE 搭配 Metro﹐eclipse 搭配asix 这两种﹐不过因为不是专研 Java﹐故仍有些地方不是实作的很完全﹐这里就抛砖引玉﹐期待高手来解惑了。

 

这里使用的 NetBeans 版本为 NetBeans IDE 7.2 (Build 201207301726)﹐JDK 是1.6.0_37。NetBeans可至Oracle官网(http://netbeans.org/downloads/index.html)下载。

在 Viusal Studio 上建立WCF 项目时﹐预设所产生的就是使用 wsHttpBinding 的系结。wsHttpBinding 预设的安全性模式为 Message(讯息模式加密)﹐在 WCF 中也是普遍的被使用。当我想以 Java 调用时却发现在网络上有许多人询问﹐但很少看到一个完整的范例。同时所看到的讨论回复都很片断﹐因此实作过程并不容易。在这里分别以 Java Application 与 Web应用程序当Client程序调用WCF为范例。

这一篇是搭配使用者认证的WCF服务—wsHttpBinding系结所建置的WCF 服务。

1. 自订使用者账号/密码﹐Java Application client 不以Glassfish为container

1.1. 汇入凭证档

因为WCF自订使用者账号/密码认证是需要X.509凭证﹐因此在开始之前必须先取得凭证放置到Java可以读取的位置。

这里的凭证继续延用WCF自定义用户账号密码之使用者认证的WCF服务wsHttpBinding总结这一篇中的凭证﹐其凭证主体为 MyWCFCert。首先使用windows凭证管理将之前制作WCF时所制作的凭证先汇出。

 \
 

将汇出的凭证档档名命名为 MyWCFCert.cer﹐接着使用 JDK 所提供的工具 keytool 指令建立放置凭证的 keystore 或汇入已存在的 keystore(金钥库)。Keytool.exe是java的凭证管理工具。

指令:

把一个凭证档导入到指定的keystore www.it165.net

keytool -import -file MyWCFCert.cer -keystore my.TrustStore -alias wcfsvrkey

用-keystore 参数指定 keystore 档案﹐my.TrustStore 是我自己建立的 keystore﹐如果不存在﹐会自动建立同时会询问keystore的密码。

-alias则是为这个汇入的凭证建立一个别名。

 

汇入成功后应该做一下检查﹐同样使用keytool指令

指令:

列出keystore中的内容信息

keytool -list -v -keystore my.TrustStore

执行之后可以检视这个keystore所有的凭证档。

 

\
 

如果有删除凭证的需要时同样使用keytool

指令:

删除指定的keystore中的凭证

keytool -delete -alias wcfsvrkey -keystore my.TrustStore

 

1.2. 下载 METRO 2.2.1

我由Oracle 官网下载使用的NetBeans IDE是7.2 版﹐内附 METRO 是 2.0 版﹐对于要使用具有使用者账号密码认证的WCF服务在国外论坛上有不少﹐有许多人都说必须将Metro更新到2.0版才行﹐不过经过我实测后发现必须使用 2.2.1版才行。

Metro 2.2.1 版可至 http://metro.java.net/2.2.1/ 此处下载。将下载的metro-standalone-2.2.1.zip档案解压至自订的路径之下﹐例如以我个人放置到D:\Java\metro-2_2_1。然后开启NetBeans﹐到工具/链接库将 METRO 2.2.1 加入链接库。

 

\

在链接库管理器中﹐按下[加入JAR/数据夹]指向Metor 2.2.1的位置﹐之后在NetBeans 中就可以直接选择了。

 

\
 

1.3. 建立项目

开启NetBeans新增项目﹐左侧选择Java﹐右侧选择Java Application﹐[下一步]继续。

 

\
 

输入项目名称﹑项目位置﹑Class文件等各项信息﹐按下[完成]。

 

\
 

专案名称:wsHttpClient

Class名称:JavaClient

 

1.4. 加入 Web Service Client

项目建立后﹐在该项目的名称上以鼠标右键选择 New/Web Service Client。

 

\
 

完成后回到NetBeans IDE接口等一下﹐NetBeans正在产生WSDL档及自动产生一些相关的设定档和程序代码。NetBeans跑完后大约如下图。

 

\
 

1.5. 档案转换为 UT-8 格式

检视项目之下有个Generated Source﹐将其展开后会如下﹐在Generated Source是NetBeans依WSDL所自动产生的class檔。

 

\
 

点选开启 GetProduct.java﹐画面会出现警告﹐这是因为NetBeans自动Generated的档案为ANSI格式﹐但这个档案中有中文﹐而NetBeans要读取的档案为UTF-8﹐故这里NetBeans跳出了警告。

 

\
 

因为档案带有中文字﹐因此必须要先将档案改存为UTF-8才行﹐不然后续做 Builde Project 时是会出错的。可以用记事本开启档案﹐再以另存新档方式变更格式再回存。不过因为档案不只一个﹐这样改比较慢﹐可以去网络找一个老牌的工具 ConverZ 做批次转换。 www.it165.net

 

1.6. 编辑 Web 服务属性

接着在Web服务参照下的MyProducts按下鼠标右键﹐选择[编辑Web服务属性]。

 

\
 

开出的是Web服务属性的画面﹐这里我们只要设定Quality Of Service页签下关于安全的部分。

 

\
 

在编辑Web服务属性的设定画面上有一个[使用开发默认值]的复选框﹐点击一下﹐画面会跳出一些讯息。

 

\
 

这个讯息是询问我们,是否使用METRO Library﹐如果要使用则会移除JAX-WS library﹐因为JAX-WS已被包含于METRO之中。按下Yes则NetBeans会帮我们项目加上METRO。如下图﹐回到Project中展开链接库﹐就可以看到METRO 2.0已被加入。

 

\
 

再回到编辑Web服务属性设定画面﹐刚刚所点击的[使用开发默认值]的复选框如果已经有被勾选了﹐请将勾选取消。然后先离开编辑Web服务属性设定画面。

 

1.7. 加入CallbackHandler 档案

这里需要加入一个继承CallbackHandler的档案

TrustStoreCallbackHandler.java

 

01. public class TrustStoreCallbackHandler implements CallbackHandler {
02. KeyStore keyStore = null
03. String password = "123456";  // keystore的密码
04. public TrustStoreCallbackHandler() { 
05. System.out.println("Truststore CBH.CTOR Called.........."); 
06. InputStream is null
07. try 
08. keyStore = KeyStore.getInstance("JKS"); 
09. String keystoreURL = "C:\\Java\\Certificate\\myTrustStore"//放置凭证档的keystore
10. is new FileInputStream(keystoreURL); 
11. keyStore.load(is, password.toCharArray()); 
12. catch (IOException ex) { 
13. Logger.getLogger(TrustStoreCallbackHandler.class.getName()).log(Level.SEVERE,null, ex); 
14. throw new RuntimeException(ex); 
15. catch (NoSuchAlgorithmException ex) { 
16. Logger.getLogger(TrustStoreCallbackHandler.class.getName()).log(Level.SEVERE,null, ex); 
17. throw new RuntimeException(ex); 
18. catch (CertificateException ex) { 
19. Logger.getLogger(TrustStoreCallbackHandler.class.getName()).log(Level.SEVERE,null, ex); 
20. throw new RuntimeException(ex); 
21. catch (KeyStoreException ex) { 
22. Logger.getLogger(TrustStoreCallbackHandler.class.getName()).log(Level.SEVERE,null, ex); 
23. throw new RuntimeException(ex); 
24. finally 
25. try 
26. is.close(); 
27. catch (IOException ex) { 
28. Logger.getLogger(TrustStoreCallbackHandler.class.getName()).log(Level.SEVERE,null, ex); 
29.
30.
31. }
32.  
33. public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { 
34. System.out.println("Truststore CBH.handle() Called.........."); 
35. for (int i = 0; i < callbacks.length; i++) {
36. if (callbacks[i] instanceof KeyStoreCallback) { 
37. KeyStoreCallback cb = (KeyStoreCallback) callbacks[i]; 
38. print(cb.getRuntimeProperties()); 
39. cb.setKeystore(keyStore); 
40. else 
41. throw new UnsupportedCallbackException(callbacks[i]); 
42.
43.
44. }
45.  
46. private void print(Map context) { 
47. Iterator it = context.keySet().iterator(); 
48. while (it.hasNext()) { 
49. System.out.println("Prop " + it.next()); 
50.
51. }
52. }


 

1.8. 重回编辑 Web 服务属性

重新开启 编辑Web服务属性

 

\
 

将[认证凭证]选择动态。变更完后画面会改变。

 

\
 

在[使用者名称回呼处理程序]及[密码回呼处理程序]后方的浏览键按下后选择刚刚所建立的CallbackHandler档案TrustStoreCallbackHandler.java。

接下来点选画面上的[信任库]按键。

 

\
 

画面将出现信任库配置的画面。



 

\
 

位置请选择凭证文件所在的keystor﹐同时在信任库密码输入keystore的密码。这时候如果要去选择别名是选不到的﹐请先按下「OK」键﹐并离开编辑Web服务属性的画面回到NetBeans IDE画面。

观察Project之下在原始码套件/META-INF之下多了两个档案﹐MyProducts.svc.xml及wsit-client.xml﹐这是刚刚的设定之后产生的。

 

\
 

现在必须先开启MyProducts.svc.xml做些修改。修改<sc:TrustStore>下的type属性﹐type 必须改为 JKS。

MyProducts.svc.xml 取需要修改的部分

 

01. <wsp1:Policy wsu:Id="WSHttpBinding_IProductServicePolicy">
02. <wsp1:ExactlyOne>
03. <wsp1:All>
04. <sc:TrustStore wspp:visibility="private" type="JKS" storepass="123456"location="C:\Java\Certificate\myTrustStore" />
05. <sc:CallbackHandlerConfiguration wspp:visibility="private">
06. <sc:CallbackHandler name="usernameHandler"classname="wshttpclient.TrustStoreCallbackHandler"/>
07. <sc:CallbackHandler name="passwordHandler"classname="wshttpclient.TrustStoreCallbackHandler"/>
08. </sc:CallbackHandlerConfiguration>
09. </wsp1:All>
10. </wsp1:ExactlyOne>
11. </wsp1:Policy>


 

上述修改之后﹐再次回到Web服务属性编辑画面并点选信任库﹐这时再去拉选别名﹐就可以选择到凭证档的别名了﹐按下OK后并离开Web服务属性编辑画面﹐再次检视刚刚刚的MyProducts.svc.xml可以发现设定多了peeralais的设定。

<sc:TrustStore wspp:visibility="private" type="JKS" storepass="123456"

        location="C:\Java\Certificate\myTrustStore" peeralias="win7svrkey"/>

 
1.9. 撰写 Client 程序

前面的设定已完成了工作中的大部分﹐剩下client程序。

wcfClient.java

 

01. public class wcfClient {
02. public static void main(String[] args) {
03. org.tempuri.ProductService client;
04. org.tempuri.IProductService port;
05.  
06. Product product;
07. try{
08. client=new org.tempuri.ProductService();
09. port=client.getWSHttpBindingIProductService();
10.  
11. //设定呼叫WCF的使用者账号/密码
12. ((BindingProvider)port).getRequestContext().put(BindingProvider.USERNAME_PROPERTY,"testman");
13. ((BindingProvider)port).getRequestContext().put(BindingProvider.PASSWORD_PROPERTY,"a0987");
14.  
15. String result= port.saySomething("Hello~~");
16. System.out.println(result);
17. System.out.println();
18.  
19. product=port.getProduct("P-001");
20. System.out.println("产品编号:"+product.getNo().getValue());
21. System.out.println("产品名称:"+product.getName().getValue());
22. System.out.println("单价:"+product.getPrice());
23. System.out.println("数量:"+product.getQuantity());
24. }catch(Exception er){
25. System.out.println(er.getMessage());
26. }
27. }
28. }

1.10. 测试程序

执行Build Project﹐如果没有任何错误﹐那么就直接执行程序。结果﹐失败~~

 

01. java.util.logging.ErrorManager: 5
02. java.lang.NullPointerException
03. at java.util.PropertyResourceBundle.handleGetObject(PropertyResourceBundle.java:136)
04. at java.util.ResourceBundle.getObject(ResourceBundle.java:368)
05. at java.util.ResourceBundle.getString(ResourceBundle.java:334)
06. at java.util.logging.Formatter.formatMessage(Formatter.java:108)
07. at java.util.logging.SimpleFormatter.format(SimpleFormatter.java:63)
08. at java.util.logging.StreamHandler.publish(StreamHandler.java:177)
09. at java.util.logging.ConsoleHandler.publish(ConsoleHandler.java:88)
10. at java.util.logging.Logger.log(Logger.java:478)
11. at java.util.logging.Logger.doLog(Logger.java:500)
12. at java.util.logging.Logger.log(Logger.java:589)
13. at com.sun.xml.ws.security.impl.policy.CertificateRetriever.digestBST(CertificateRetriever.java:136)
14. at com.sun.xml.wss.jaxws.impl.SecurityClientTube.processRequest(SecurityClientTube.java:211)
15. at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:629)
16. at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:588)
17. at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:573)
18. com.sun.org.apache.xml.internal.security.exceptions.Base64DecodingException: It should be divisible by four
19. at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:470)
20. at com.sun.xml.ws.client.Stub.process(Stub.java:319)
21. at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:157)
22. at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:109)
23. at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:89)
24. at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:140)
25. at $Proxy43.saySomething(Unknown Source)
26. at wshttpclient.wcfClient.main(wcfClient.java:31)

\
 

接着在[链接库]上按鼠标右键选择[Add Library…]﹐点选之前所加入的Metro 2.2.1按下[加入链接库]就可以了。

 

\   \ 

\
 

重新再Build一次程序后﹐再一次执行。结果﹐再次失败~~~

错误的讯息

2012/12/22 上午 10:44:09 [com.sun.xml.ws.policy.parser.PolicyConfigParser]  parse
信息: WSP5018: 已从档案 file:/D:/MyProject/WCF/WCFSite/WCFSolution/Java/NetBeansProjects/wsHttpClient/build/classes/META-INF/wsit-client.xml 载入 WSIT 组态.
Truststore CBH.CTOR Called..........
Truststore CBH.CTOR Called..........
2012/12/22 上午 10:44:12 com.sun.xml.wss.jaxws.impl.SecurityClientTube processClientResponsePacket
严重的: WSSTUBE0025: 验证输入讯息中的安全性时发生错误.
com.sun.xml.wss.impl.PolicyViolationException: ERROR: No security header found in the message
at com.sun.xml.wss.impl.policy.verifier.MessagePolicyVerifier.verifyPolicy(MessagePolicyVerifier.java:138)
at com.sun.xml.ws.security.opt.impl.incoming.SecurityRecipient.createMessage(SecurityRecipient.java:1016)
at com.sun.xml.ws.security.opt.impl.incoming.SecurityRecipient.validateMessage(SecurityRecipient.java:252)
at com.sun.xml.wss.jaxws.impl.SecurityTubeBase.verifyInboundMessage(SecurityTubeBase.java:455)
at com.sun.xml.wss.jaxws.impl.SecurityClientTube.processClientResponsePacket(SecurityClientTube.java:434)
at com.sun.xml.wss.jaxws.impl.SecurityClientTube.processResponse(SecurityClientTube.java:362)
at com.sun.xml.ws.api.pipe.Fiber.__doRun(Fiber.java:1074)
at com.sun.xml.ws.api.pipe.Fiber._doRun(Fiber.java:979)
at com.sun.xml.ws.api.pipe.Fiber.doRun(Fiber.java:950)
at com.sun.xml.ws.api.pipe.Fiber.runSync(Fiber.java:825)
at com.sun.xml.ws.client.Stub.process(Stub.java:443)
at com.sun.xml.ws.client.sei.SEIStub.doProcess(SEIStub.java:174)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:119)
at com.sun.xml.ws.client.sei.SyncMethodHandler.invoke(SyncMethodHandler.java:102)
WSSTUBE0025: 验证输入讯息中的安全性时发生错误.
at com.sun.xml.ws.client.sei.SEIStub.invoke(SEIStub.java:154)
at $Proxy41.saySomething(Unknown Source)
at wshttpclient.wcfClient.main(wcfClient.java:31)
成功建置 (总时间:3 秒)
失败的原因是什么呢?这里必须回到 WCF 的设定﹐检视 WCF 下的 wcf.config 中的 Binding 设定

修改前

 

   1:  <bindings>
   2:     <wsHttpBinding>
   3:          <binding name="Product.wsHttpBinding">
   4:            <security>
   5:              <message clientCredentialType="UserName" />
   6:            </security>
   7:          </binding>
   8:    </wsHttpBinding>
   9:  </bindings>
修改后
   1:  <bindings>
   2:     <wsHttpBinding>
   3:        <binding name="Product.wsHttpBinding">
   4:            <security>
   5:              <message clientCredentialType="UserName"
   6:                       negotiateServiceCredential="false"
   7:                       algorithmSuite="Basic128"
   8:                       establishSecurityContext="false" />
   9:            </security>
  10:        </binding>
  11:    </wsHttpBinding>
  12:  </bindings>
在message标签中negotiateServiceCredential预设是true﹐这个属性和Windows认证有关﹐可以参考MSDN的文章http://msdn.microsoft.com/zh-tw/library/system.servicemodel.messagesecurityoverhttp.negotiateservicecredential.aspx 及http://paper.dic123.com/lunwen_234540427/ ﹐不是很好了解﹐这里Java要呼叫必须将 negotiateServiceCredential 设为 false。
另外algorithmSuite原本默认值为Basic256必须要修改为Basic128﹐理由是什么…我记得曾在一篇国外讨论区看到说Java不支持到256的长度﹐文章已遗落在茫茫网海中﹐这给Java高手去回答吧。establishSecurityContext默认值为true也必须改为false。
注意﹐WCF的web.config一旦有改变﹐原本呼叫此WCF的Client程序必须跟着修改设定。
之后重新执行Build Project﹐那么就直接执行程序。结果﹐这次总算成功~~

 \
 

网络上有许多人询问Java可否调用wsHttpBinding的WCF呢?根据实作后的经验﹐当然是可以﹐不过为什么会有人有疑惑?首先﹐WCF具有多项认证技术﹐例如Windows认证﹑SQL Membership﹑使用者账号密码…等﹐而在wsHttpBinding其预设是使用Windows认证﹐这在微软各项Solution中不会有什么太大的问题﹐但是像Java就不一定。在这一小节里提到negotiateServiceCredential这个属性必须改为false﹐就是因为这个设定和 Windows 认证有关﹐当然可以在WCF组态设定变更认证方式﹐但一奱更之后就跟随着必须提供凭证﹐例如本次的范例实作﹐这一部分是常被大家所混淆的。

2. 自订使用者账号/密码﹐Java Client 以Glassfish为container

2.1. 建立项目

档案/New Project/Java Web/Web应用程序﹐建立一个Web应用程序。这次就不再示范无认证的WCF﹐直接跑有认证的WCF。专案名称:wsHttpWebAppUseAuth

 

\ 

\
 

下一步之后这里要选择所使用的容器﹐这里选择的是GlassFish Server 3.1.2。

 

\
 

按下[完成]后﹐可以看到所产生的项目架构。

 

\
 

2.2. 加入 Web Service Client

在项目名称上鼠标右键选择New/Web Service Client。

 

\
 

选择WSDL URL并输入具有认证的WCF URL。

WSDL URL:http://10.0.100.101:85/wsHttpUserAuth.webhost/MyProducts.svc?wsdl

 \
 

[完成]之后在项目中可以看到同样产生了Generated Sources还有服务参照。请记得Generated出来的class档带有中文﹐要先将档案格式转换成UTF-8﹐不然在最后做builder时会失败。

 

\
 

2.3. 编辑 Web 服务属性

在Web服务参照之下的MyProducts按下鼠标右键选择[编辑Web服务属性]。

 

\
 

到此和之前的Java Application的做法都相同﹐但这次是搭配GlassFish做container﹐因此不需要METRO﹐故接下来直接点[信任库]设定keystore就可以。

 

\
 

将位置改选择放置凭证的keystore档案﹐并输入keystore的密码。同样的现在是选不到别名的。请按下[OK]回到前一个画面后也按下[OK]离开Web服务属性的编辑。

 

\
 

重新检视项目﹐在原始码套件/META-INF之下多了MyProducts.svc.xml档案。

 

\
 

开启MyProducts.svc.dml﹐并修改<sc:TrustStrore>下的Type属性改为JKS。
修改后
   1:  <wsp1:Policy wsu:Id="WSHttpBinding_IProductServicePolicy">
   2:    <wsp1:ExactlyOne>
   3:      <wsp1:All>
   4:        <sc:TrustStore wspp:visibility="private" storepass="123456" type="JKS" location="C:\Java\Certificate\myTrustStore"/>
   5:      </wsp1:All>
   6:    </wsp1:ExactlyOne>
   7:  </wsp1:Policy>
再重新回到之前的Web服务属性的编辑并进入信任库中﹐这时就已经可以选择别名了。指定别名后回头看MyProducts.svc.xml的设定就会发现已有修改。如果指令很熟悉的人就不需要这么麻烦了﹐直接编写设定档即可。
 
2.4. 建立 Servlet
在原始码套件上以鼠标右键﹐选择New/Servlet﹐然后输入ClassName和Package﹐按下[完成]。

 

\
 

然后在所产生的wsService.java中输入以下程序代码

wsService.java

 

01. @WebServlet(name = "wsService", urlPatterns = {"/wsService"})
02. public class wsService extends HttpServlet {
03. /**
04. * Processes requests for both HTTP
05. * <code>GET</code> and
06. * <code>POST</code> methods.
07. *
08. * @param request servlet request
09. * @param response servlet response
10. * @throws ServletException if a servlet-specific error occurs
11. * @throws IOException if an I/O error occurs
12. */
13. protected void processRequest(HttpServletRequest request, HttpServletResponse response)
14. throws ServletException, IOException {
15. response.setContentType("text/html;charset=UTF-8");
16. PrintWriter out = response.getWriter();
17. org.tempuri.ProductService client=null;
18. org.tempuri.IProductService port=null;
19. Product product;
20. try {
21. client=new org.tempuri.ProductService();
22. port=client.getWSHttpBindingIProductService();
23. BindingProvider bp=(BindingProvider)port;
24. bp.getRequestContext().put(BindingProvider.USERNAME_PROPERTY, "testman");
25. bp.getRequestContext().put(BindingProvider.PASSWORD_PROPERTY, "a0987");
26.  
27. out.println("<html>");
28. out.println("<head>");
29. out.println("<title>Servlet wsService</title>");           
30. out.println("</head>");
31. out.println("<body>");
32. String result=port.saySomething("Hello~~");
33. out.println("<h1>Servlet wsService at " + result + "</h1>");
34. out.println("<br/>");
35.  
36. product=port.getProduct("P-001");
37. out.println("产品编号:"+product.getNo().getValue()+"<br>");
38. out.println("产品名称:"+product.getName().getValue()+"<br>");
39. out.println("单价:"+product.getPrice()+"<br>");
40. out.println("数量:"+product.getQuantity()+"<br>");
41.  
42. out.println("</body>");
43. out.println("</html>");
44. finally {           
45. out.close();
46. }
47. }
48. }

将项目builder之后执行应该可以得到如下的结果。

 

\
 

在撰写过程﹐花了最多时间是在Java Application 如何调用WCF 的部分﹐目前我所成功的就是 NetBeans + Metro 及 GlassFish﹐至于使用ASIX 2 还没成功过﹐不过如果是 basicHttpBinding 以 Transport 方式加密﹐使用 ASIX 倒是可以﹐这留待下一篇再继续了。


目录
相关文章
|
Java
java使用Quartz任务调用crontab表达式的时候报错:Based on configured schedule, the given trigger will never fire
java使用Quartz任务调用crontab表达式的时候报错:Based on configured schedule, the given trigger will never fire
402 0
java使用Quartz任务调用crontab表达式的时候报错:Based on configured schedule, the given trigger will never fire
|
Java 开发工具 C++
Java调用虹软SDK的错误
Java调用虹软SDK的错误
489 0
|
Java
UOS系统JAVA应用在任务栏显示类名的问题跟踪调用
UOS系统JAVA应用在任务栏显示类名的问题跟踪调用
74 0
|
Java Python
Python:jpype模块调用Java函数
Python:jpype模块调用Java函数
80 0
|
算法 Java Go
运筹优化学习21:Java调用Cplex实现求解Cuting Stock Porblem的列生成算法详解(下)
运筹优化学习21:Java调用Cplex实现求解Cuting Stock Porblem的列生成算法详解
运筹优化学习21:Java调用Cplex实现求解Cuting Stock Porblem的列生成算法详解(下)
|
算法 Java 决策智能
运筹优化学习21:Java调用Cplex实现求解Cuting Stock Porblem的列生成算法详解(中)
运筹优化学习21:Java调用Cplex实现求解Cuting Stock Porblem的列生成算法详解
运筹优化学习21:Java调用Cplex实现求解Cuting Stock Porblem的列生成算法详解(中)
|
算法 Java Go
运筹优化学习21:Java调用Cplex实现求解Cuting Stock Porblem的列生成算法详解(上)
运筹优化学习21:Java调用Cplex实现求解Cuting Stock Porblem的列生成算法详解
运筹优化学习21:Java调用Cplex实现求解Cuting Stock Porblem的列生成算法详解(上)
|
Java C# 决策智能
运筹优化学习09:一个示例带你入门如何使用C++、C#、Java、Python、Matlab调用Cplex(下)
运筹优化学习09:一个示例带你入门如何使用C++、C#、Java、Python、Matlab调用Cplex
运筹优化学习09:一个示例带你入门如何使用C++、C#、Java、Python、Matlab调用Cplex(下)
|
Java 测试技术 C#
运筹优化学习09:一个示例带你入门如何使用C++、C#、Java、Python、Matlab调用Cplex(上)
运筹优化学习09:一个示例带你入门如何使用C++、C#、Java、Python、Matlab调用Cplex
运筹优化学习09:一个示例带你入门如何使用C++、C#、Java、Python、Matlab调用Cplex(上)