天天看點

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﹐故仍有些地方不是實作的很完全﹐這裡就抛磚引玉﹐期待高手來解惑了。

在 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可以讀取的位置。

java調用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所有的憑證檔。

java調用WCF問題

如果有删除憑證的需要時同樣使用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 加傳入連結接庫。

java調用WCF問題

在連結庫管理器中﹐按下[加入jar/資料夾]指向metor 2.2.1的位置﹐之後在netbeans 中就可以直接選擇了。

java調用WCF問題

1.3. 建立項目

開啟netbeans新增項目﹐左側選擇java﹐右側選擇java application﹐[下一步]繼續。

java調用WCF問題

輸入項目名稱﹑項目位置﹑class檔案等各項資訊﹐按下[完成]。

java調用WCF問題

專案名稱:wshttpclient

class名稱:javaclient

1.4. 加入 web service client

項目建立後﹐在該項目的名稱上以滑鼠右鍵選擇 new/web service client。

java調用WCF問題

完成後回到netbeans ide接口等一下﹐netbeans正在産生wsdl檔及自動産生一些相關的設定檔和程式代碼。netbeans跑完後大約如下圖。

java調用WCF問題

1.5. 檔案轉換為 ut-8 格式

檢視項目之下有個generated source﹐将其展開後會如下﹐在generated source是netbeans依wsdl所自動産生的class檔。

java調用WCF問題

點選開啟 getproduct.java﹐畫面會出現警告﹐這是因為netbeans自動generated的檔案為ansi格式﹐但這個檔案中有中文﹐而netbeans要讀取的檔案為utf-8﹐故這裡netbeans跳出了警告。

java調用WCF問題

因為檔案帶有中文字﹐是以必須要先将檔案改存為utf-8才行﹐不然後續做 builde project 時是會出錯的。可以用記事本開啟檔案﹐再以另存新檔方式變更格式再回存。不過因為檔案不隻一個﹐這樣改比較慢﹐可以去網絡找一個老牌的工具 converz 做批次轉換。 www.it165.net

1.6. 編輯 web 服務屬性

接着在web服務參照下的myproducts按下滑鼠右鍵﹐選擇[編輯web服務屬性]。

java調用WCF問題

開出的是web服務屬性的畫面﹐這裡我們隻要設定quality of service頁簽下關于安全的部分。

java調用WCF問題

在編輯web服務屬性的設定畫面上有一個[使用開發預設值]的複選框﹐點選一下﹐畫面會跳出一些訊息。

java調用WCF問題

這個訊息是詢問我們,是否使用metro library﹐如果要使用則會移除jax-ws library﹐因為jax-ws已被包含于metro之中。按下yes則netbeans會幫我們項目加上metro。如下圖﹐回到project中展開連結庫﹐就可以看到metro 2.0已被加入。

java調用WCF問題

再回到編輯web服務屬性設定畫面﹐剛剛所點選的[使用開發預設值]的複選框如果已經有被勾選了﹐請将勾選取消。然後先離開編輯web服務屬性設定畫面。

1.7. 加入callbackhandler 檔案

這裡需要加入一個繼承callbackhandler的檔案

truststorecallbackhandler.java

<a target="_blank" href="http://www.it165.net/pro/html/201212/4453.html#viewsource">view source</a>

<code>01.</code><code>public</code> <code>class</code> <code>truststorecallbackhandler implements callbackhandler {</code>

<code>02.</code><code>keystore keystore = </code><code>null</code><code>; </code>

<code>03.</code><code>string password = </code><code>"123456"</code><code>;  </code><code>// keystore的密碼</code>

<code>04.</code><code>public</code> <code>truststorecallbackhandler() { </code>

<code>05.</code><code>system.</code><code>out</code><code>.println(</code><code>"truststore cbh.ctor called.........."</code><code>); </code>

<code>06.</code><code>inputstream </code><code>is</code> <code>= </code><code>null</code><code>; </code>

<code>07.</code><code>try</code> <code>{ </code>

<code>08.</code><code>keystore = keystore.getinstance(</code><code>"jks"</code><code>); </code>

<code>09.</code><code>string keystoreurl = </code><code>"c:\\java\\certificate\\mytruststore"</code><code>; </code><code>//放置憑證檔的keystore</code>

<code>10.</code><code>is</code> <code>= </code><code>new</code> <code>fileinputstream(keystoreurl); </code>

<code>11.</code><code>keystore.load(</code><code>is</code><code>, password.tochararray()); </code>

<code>12.</code><code>} </code><code>catch</code> <code>(ioexception ex) { </code>

<code>13.</code><code>logger.getlogger(truststorecallbackhandler.</code><code>class</code><code>.getname()).log(level.severe,</code><code>null</code><code>, ex); </code>

<code>14.</code><code>throw</code> <code>new</code> <code>runtimeexception(ex); </code>

<code>15.</code><code>} </code><code>catch</code> <code>(nosuchalgorithmexception ex) { </code>

<code>16.</code><code>logger.getlogger(truststorecallbackhandler.</code><code>class</code><code>.getname()).log(level.severe,</code><code>null</code><code>, ex); </code>

<code>17.</code><code>throw</code> <code>new</code> <code>runtimeexception(ex); </code>

<code>18.</code><code>} </code><code>catch</code> <code>(certificateexception ex) { </code>

<code>19.</code><code>logger.getlogger(truststorecallbackhandler.</code><code>class</code><code>.getname()).log(level.severe,</code><code>null</code><code>, ex); </code>

<code>20.</code><code>throw</code> <code>new</code> <code>runtimeexception(ex); </code>

<code>21.</code><code>} </code><code>catch</code> <code>(keystoreexception ex) { </code>

<code>22.</code><code>logger.getlogger(truststorecallbackhandler.</code><code>class</code><code>.getname()).log(level.severe,</code><code>null</code><code>, ex); </code>

<code>23.</code><code>throw</code> <code>new</code> <code>runtimeexception(ex); </code>

<code>24.</code><code>} </code><code>finally</code> <code>{ </code>

<code>25.</code><code>try</code> <code>{ </code>

<code>26.</code><code>is</code><code>.close(); </code>

<code>27.</code><code>} </code><code>catch</code> <code>(ioexception ex) { </code>

<code>28.</code><code>logger.getlogger(truststorecallbackhandler.</code><code>class</code><code>.getname()).log(level.severe,</code><code>null</code><code>, ex); </code>

<code>29.</code><code>} </code>

<code>30.</code><code>} </code>

<code>31.</code><code>}</code>

<code>32.</code> 

<code>33.</code><code>public</code> <code>void</code> <code>handle(callback[] callbacks) throws ioexception, unsupportedcallbackexception { </code>

<code>34.</code><code>system.</code><code>out</code><code>.println(</code><code>"truststore cbh.handle() called.........."</code><code>); </code>

<code>35.</code><code>for</code> <code>(</code><code>int</code> <code>i = 0; i &lt; callbacks.length; i++) {</code>

<code>36.</code><code>if</code> <code>(callbacks[i] instanceof keystorecallback) { </code>

<code>37.</code><code>keystorecallback cb = (keystorecallback) callbacks[i]; </code>

<code>38.</code><code>print(cb.getruntimeproperties()); </code>

<code>39.</code><code>cb.setkeystore(keystore); </code>

<code>40.</code><code>} </code><code>else</code> <code>{ </code>

<code>41.</code><code>throw</code> <code>new</code> <code>unsupportedcallbackexception(callbacks[i]); </code>

<code>42.</code><code>} </code>

<code>43.</code><code>} </code>

<code>44.</code><code>}</code>

<code>45.</code> 

<code>46.</code><code>private</code> <code>void</code> <code>print(map context) { </code>

<code>47.</code><code>iterator it = context.keyset().iterator(); </code>

<code>48.</code><code>while</code> <code>(it.hasnext()) { </code>

<code>49.</code><code>system.</code><code>out</code><code>.println(</code><code>"prop "</code> <code>+ it.next()); </code>

<code>50.</code><code>} </code>

<code>51.</code><code>}</code>

<code>52.</code><code>}</code>

1.8. 重回編輯 web 服務屬性

重新開啟 編輯web服務屬性

java調用WCF問題

将[認證憑證]選擇動态。變更完後畫面會改變。

java調用WCF問題

在[使用者名稱回呼處理程式]及[密碼回呼處理程式]後方的浏覽鍵按下後選擇剛剛所建立的callbackhandler檔案truststorecallbackhandler.java。

接下來點選畫面上的[信任庫]按鍵。

java調用WCF問題

畫面将出現信任庫配置的畫面。

java調用WCF問題

位置請選擇憑證檔案所在的keystor﹐同時在信任庫密碼輸入keystore的密碼。這時候如果要去選擇别名是選不到的﹐請先按下「ok」鍵﹐并離開編輯web服務屬性的畫面回到netbeans ide畫面。

觀察project之下在原始碼套件/meta-inf之下多了兩個檔案﹐myproducts.svc.xml及wsit-client.xml﹐這是剛剛的設定之後産生的。

java調用WCF問題

現在必須先開啟myproducts.svc.xml做些修改。修改&lt;sc:truststore&gt;下的type屬性﹐type 必須改為 jks。

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

<a target="_blank" href="http://www.it165.net/pro/html/201212/4453_2.html#viewsource">view source</a>

<code>01.</code><code>&lt;</code><code>wsp1:policy</code> <code>wsu:id</code><code>=</code><code>"wshttpbinding_iproductservicepolicy"</code><code>&gt;</code>

<code>02.</code><code>&lt;</code><code>wsp1:exactlyone</code><code>&gt;</code>

<code>03.</code><code>&lt;</code><code>wsp1:all</code><code>&gt;</code>

<code>04.</code><code>&lt;</code><code>sc:truststore</code> <code>wspp:visibility</code><code>=</code><code>"private"</code> <code>type</code><code>=</code><code>"jks"</code> <code>storepass</code><code>=</code><code>"123456"</code><code>location</code><code>=</code><code>"c:\java\certificate\mytruststore"</code> <code>/&gt;</code>

<code>05.</code><code>&lt;</code><code>sc:callbackhandlerconfiguration</code> <code>wspp:visibility</code><code>=</code><code>"private"</code><code>&gt;</code>

<code>06.</code><code>&lt;</code><code>sc:callbackhandler</code> <code>name</code><code>=</code><code>"usernamehandler"</code><code>classname</code><code>=</code><code>"wshttpclient.truststorecallbackhandler"</code><code>/&gt;</code>

<code>07.</code><code>&lt;</code><code>sc:callbackhandler</code> <code>name</code><code>=</code><code>"passwordhandler"</code><code>classname</code><code>=</code><code>"wshttpclient.truststorecallbackhandler"</code><code>/&gt;</code>

<code>08.</code><code>&lt;/</code><code>sc:callbackhandlerconfiguration</code><code>&gt;</code>

<code>09.</code><code>&lt;/</code><code>wsp1:all</code><code>&gt;</code>

<code>10.</code><code>&lt;/</code><code>wsp1:exactlyone</code><code>&gt;</code>

<code>11.</code><code>&lt;/</code><code>wsp1:policy</code><code>&gt;</code>

上述修改之後﹐再次回到web服務屬性編輯畫面并點選信任庫﹐這時再去拉選别名﹐就可以選擇到憑證檔的别名了﹐按下ok後并離開web服務屬性編輯畫面﹐再次檢視剛剛剛的myproducts.svc.xml可以發現設定多了peeralais的設定。

&lt;sc:truststore wspp:visibility="private" type="jks" storepass="123456"

        location="c:\java\certificate\mytruststore" peeralias="win7svrkey"/&gt;

1.9. 撰寫 client 程式

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

wcfclient.java

<code>01.</code><code>public</code> <code>class</code> <code>wcfclient {</code>

<code>02.</code><code>public</code> <code>static</code> <code>void</code> <code>main(string[] args) {</code>

<code>03.</code><code>org.tempuri.productservice client;</code>

<code>04.</code><code>org.tempuri.iproductservice port;</code>

<code>05.</code> 

<code>06.</code><code>product product;</code>

<code>07.</code><code>try</code><code>{</code>

<code>08.</code><code>client=</code><code>new</code> <code>org.tempuri.productservice();</code>

<code>09.</code><code>port=client.getwshttpbindingiproductservice();</code>

<code>10.</code> 

<code>11.</code><code>//設定呼叫wcf的使用者賬号/密碼</code>

<code>12.</code><code>((bindingprovider)port).getrequestcontext().put(bindingprovider.username_property,</code><code>"testman"</code><code>);</code>

<code>13.</code><code>((bindingprovider)port).getrequestcontext().put(bindingprovider.password_property,</code><code>"a0987"</code><code>);</code>

<code>14.</code> 

<code>15.</code><code>string result= port.saysomething(</code><code>"hello~~"</code><code>);</code>

<code>16.</code><code>system.</code><code>out</code><code>.println(result);</code>

<code>17.</code><code>system.</code><code>out</code><code>.println();</code>

<code>18.</code> 

<code>19.</code><code>product=port.getproduct(</code><code>"p-001"</code><code>);</code>

<code>20.</code><code>system.</code><code>out</code><code>.println(</code><code>"産品編号:"</code><code>+product.getno().getvalue());</code>

<code>21.</code><code>system.</code><code>out</code><code>.println(</code><code>"産品名稱:"</code><code>+product.getname().getvalue());</code>

<code>22.</code><code>system.</code><code>out</code><code>.println(</code><code>"單價:"</code><code>+product.getprice());</code>

<code>23.</code><code>system.</code><code>out</code><code>.println(</code><code>"數量:"</code><code>+product.getquantity());</code>

<code>24.</code><code>}</code><code>catch</code><code>(exception er){</code>

<code>25.</code><code>system.</code><code>out</code><code>.println(er.getmessage());</code>

<code>26.</code><code>}</code>

<code>27.</code><code>}</code>

<code>28.</code><code>}</code>

1.10. 測試程式

執行build project﹐如果沒有任何錯誤﹐那麼就直接執行程式。結果﹐失敗~~

<code>01.</code><code>java.util.logging.errormanager: 5</code>

<code>02.</code><code>java.lang.nullpointerexception</code>

<code>03.</code><code>at java.util.propertyresourcebundle.handlegetobject(propertyresourcebundle.java:136)</code>

<code>04.</code><code>at java.util.resourcebundle.getobject(resourcebundle.java:368)</code>

<code>05.</code><code>at java.util.resourcebundle.getstring(resourcebundle.java:334)</code>

<code>06.</code><code>at java.util.logging.formatter.formatmessage(formatter.java:108)</code>

<code>07.</code><code>at java.util.logging.simpleformatter.format(simpleformatter.java:63)</code>

<code>08.</code><code>at java.util.logging.streamhandler.publish(streamhandler.java:177)</code>

<code>09.</code><code>at java.util.logging.consolehandler.publish(consolehandler.java:88)</code>

<code>10.</code><code>at java.util.logging.logger.log(logger.java:478)</code>

<code>11.</code><code>at java.util.logging.logger.dolog(logger.java:500)</code>

<code>12.</code><code>at java.util.logging.logger.log(logger.java:589)</code>

<code>13.</code><code>at com.sun.xml.ws.security.impl.policy.certificateretriever.digestbst(certificateretriever.java:136)</code>

<code>14.</code><code>at com.sun.xml.wss.jaxws.impl.securityclienttube.processrequest(securityclienttube.java:211)</code>

<code>15.</code><code>at com.sun.xml.ws.api.pipe.fiber.__dorun(fiber.java:629)</code>

<code>16.</code><code>at com.sun.xml.ws.api.pipe.fiber._dorun(fiber.java:588)</code>

<code>17.</code><code>at com.sun.xml.ws.api.pipe.fiber.dorun(fiber.java:573)</code>

<code>18.</code><code>com.sun.org.apache.xml.</code><code>internal</code><code>.security.exceptions.base64decodingexception: it should be divisible by four</code>

<code>19.</code><code>at com.sun.xml.ws.api.pipe.fiber.runsync(fiber.java:470)</code>

<code>20.</code><code>at com.sun.xml.ws.client.stub.process(stub.java:319)</code>

<code>21.</code><code>at com.sun.xml.ws.client.sei.seistub.doprocess(seistub.java:157)</code>

<code>22.</code><code>at com.sun.xml.ws.client.sei.syncmethodhandler.invoke(syncmethodhandler.java:109)</code>

<code>23.</code><code>at com.sun.xml.ws.client.sei.syncmethodhandler.invoke(syncmethodhandler.java:89)</code>

<code>24.</code><code>at com.sun.xml.ws.client.sei.seistub.invoke(seistub.java:140)</code>

<code>25.</code><code>at $proxy43.saysomething(unknown source)</code>

<code>26.</code><code>at wshttpclient.wcfclient.main(wcfclient.java:31)</code>

java調用WCF問題

接着在[連結庫]上按滑鼠右鍵選擇[add library…]﹐點選之前所加入的metro 2.2.1按下[加傳入連結接庫]就可以了。

java調用WCF問題
java調用WCF問題
java調用WCF問題

重新再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..........

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:  &lt;bindings&gt;

   2:     &lt;wshttpbinding&gt;

   3:          &lt;binding name="product.wshttpbinding"&gt;

   4:            &lt;security&gt;

   5:              &lt;message clientcredentialtype="username" /&gt;

   6:            &lt;/security&gt;

   7:          &lt;/binding&gt;

   8:    &lt;/wshttpbinding&gt;

   9:  &lt;/bindings&gt;

修改後

   3:        &lt;binding name="product.wshttpbinding"&gt;

   5:              &lt;message clientcredentialtype="username"

   6:                       negotiateservicecredential="false"

   7:                       algorithmsuite="basic128"

   8:                       establishsecuritycontext="false" /&gt;

   9:            &lt;/security&gt;

  10:        &lt;/binding&gt;

  11:    &lt;/wshttpbinding&gt;

  12:  &lt;/bindings&gt;

negotiateservicecredential 設為 false。

另外algorithmsuite原本預設值為basic256必須要修改為basic128﹐理由是什麼…我記得曾在一篇國外讨論區看到說java不支援到256的長度﹐文章已遺落在茫茫網海中﹐這給java高手去回答吧。establishsecuritycontext預設值為true也必須改為false。

注意﹐wcf的web.config一旦有改變﹐原本呼叫此wcf的client程式必須跟着修改設定。

之後重新執行build project﹐那麼就直接執行程式。結果﹐這次總算成功~~

java調用WCF問題

網絡上有許多人詢問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

java調用WCF問題
java調用WCF問題

下一步之後這裡要選擇所使用的容器﹐這裡選擇的是glassfish server 3.1.2。

java調用WCF問題

按下[完成]後﹐可以看到所産生的項目架構。

java調用WCF問題

2.2. 加入 web service client

在項目名稱上滑鼠右鍵選擇new/web service client。

java調用WCF問題

選擇wsdl url并輸入具有認證的wcf url。

wsdl url:http://10.0.100.101:85/wshttpuserauth.webhost/myproducts.svc?wsdl

java調用WCF問題

[完成]之後在項目中可以看到同樣産生了generated sources還有服務參照。請記得generated出來的class檔帶有中文﹐要先将檔案格式轉換成utf-8﹐不然在最後做builder時會失敗。

java調用WCF問題

2.3. 編輯 web 服務屬性

在web服務參照之下的myproducts按下滑鼠右鍵選擇[編輯web服務屬性]。

java調用WCF問題

到此和之前的java application的做法都相同﹐但這次是搭配glassfish做container﹐是以不需要metro﹐故接下來直接點[信任庫]設定keystore就可以。

java調用WCF問題

将位置改選擇放置憑證的keystore檔案﹐并輸入keystore的密碼。同樣的現在是選不到别名的。請按下[ok]回到前一個畫面後也按下[ok]離開web服務屬性的編輯。

java調用WCF問題

重新檢視項目﹐在原始碼套件/meta-inf之下多了myproducts.svc.xml檔案。

java調用WCF問題

開啟myproducts.svc.dml﹐并修改&lt;sc:truststrore&gt;下的type屬性改為jks。

   1:  &lt;wsp1:policy wsu:id="wshttpbinding_iproductservicepolicy"&gt;

   2:    &lt;wsp1:exactlyone&gt;

   3:      &lt;wsp1:all&gt;

   4:        &lt;sc:truststore wspp:visibility="private" storepass="123456" type="jks" location="c:\java\certificate\mytruststore"/&gt;

   5:      &lt;/wsp1:all&gt;

   6:    &lt;/wsp1:exactlyone&gt;

   7:  &lt;/wsp1:policy&gt;

再重新回到之前的web服務屬性的編輯并進入信任庫中﹐這時就已經可以選擇别名了。指定别名後回頭看myproducts.svc.xml的設定就會發現已有修改。如果指令很熟悉的人就不需要這麼麻煩了﹐直接編寫設定檔即可。

2.4. 建立 servlet

在原始碼套件上以滑鼠右鍵﹐選擇new/servlet﹐然後輸入classname和package﹐按下[完成]。

java調用WCF問題

然後在所産生的wsservice.java中輸入以下程式代碼

wsservice.java

<code>01.</code><code>@webservlet(name = </code><code>"wsservice"</code><code>, urlpatterns = {</code><code>"/wsservice"</code><code>})</code>

<code>02.</code><code>public</code> <code>class</code> <code>wsservice extends httpservlet {</code>

<code>03.</code><code>/**</code>

<code>04.</code><code>* processes requests for both http</code>

<code>05.</code><code>* &lt;code&gt;get&lt;/code&gt; and</code>

<code>06.</code><code>* &lt;code&gt;post&lt;/code&gt; methods.</code>

<code>07.</code><code>*</code>

<code>08.</code><code>* @param request servlet request</code>

<code>09.</code><code>* @param response servlet response</code>

<code>10.</code><code>* @throws servletexception if a servlet-specific error occurs</code>

<code>11.</code><code>* @throws ioexception if an i/o error occurs</code>

<code>12.</code><code>*/</code>

<code>13.</code><code>protected</code> <code>void</code> <code>processrequest(httpservletrequest request, httpservletresponse response)</code>

<code>14.</code><code>throws servletexception, ioexception {</code>

<code>15.</code><code>response.setcontenttype(</code><code>"text/html;charset=utf-8"</code><code>);</code>

<code>16.</code><code>printwriter </code><code>out</code> <code>= response.getwriter();</code>

<code>17.</code><code>org.tempuri.productservice client=</code><code>null</code><code>;</code>

<code>18.</code><code>org.tempuri.iproductservice port=</code><code>null</code><code>;</code>

<code>19.</code><code>product product;</code>

<code>20.</code><code>try</code> <code>{</code>

<code>21.</code><code>client=</code><code>new</code> <code>org.tempuri.productservice();</code>

<code>22.</code><code>port=client.getwshttpbindingiproductservice();</code>

<code>23.</code><code>bindingprovider bp=(bindingprovider)port;</code>

<code>24.</code><code>bp.getrequestcontext().put(bindingprovider.username_property, </code><code>"testman"</code><code>);</code>

<code>25.</code><code>bp.getrequestcontext().put(bindingprovider.password_property, </code><code>"a0987"</code><code>);</code>

<code>26.</code> 

<code>27.</code><code>out</code><code>.println(</code><code>"&lt;html&gt;"</code><code>);</code>

<code>28.</code><code>out</code><code>.println(</code><code>"&lt;head&gt;"</code><code>);</code>

<code>29.</code><code>out</code><code>.println(</code><code>"&lt;title&gt;servlet wsservice&lt;/title&gt;"</code><code>);           </code>

<code>30.</code><code>out</code><code>.println(</code><code>"&lt;/head&gt;"</code><code>);</code>

<code>31.</code><code>out</code><code>.println(</code><code>"&lt;body&gt;"</code><code>);</code>

<code>32.</code><code>string result=port.saysomething(</code><code>"hello~~"</code><code>);</code>

<code>33.</code><code>out</code><code>.println(</code><code>"&lt;h1&gt;servlet wsservice at "</code> <code>+ result + </code><code>"&lt;/h1&gt;"</code><code>);</code>

<code>34.</code><code>out</code><code>.println(</code><code>"&lt;br/&gt;"</code><code>);</code>

<code>35.</code> 

<code>36.</code><code>product=port.getproduct(</code><code>"p-001"</code><code>);</code>

<code>37.</code><code>out</code><code>.println(</code><code>"産品編号:"</code><code>+product.getno().getvalue()+</code><code>"&lt;br&gt;"</code><code>);</code>

<code>38.</code><code>out</code><code>.println(</code><code>"産品名稱:"</code><code>+product.getname().getvalue()+</code><code>"&lt;br&gt;"</code><code>);</code>

<code>39.</code><code>out</code><code>.println(</code><code>"單價:"</code><code>+product.getprice()+</code><code>"&lt;br&gt;"</code><code>);</code>

<code>40.</code><code>out</code><code>.println(</code><code>"數量:"</code><code>+product.getquantity()+</code><code>"&lt;br&gt;"</code><code>);</code>

<code>41.</code> 

<code>42.</code><code>out</code><code>.println(</code><code>"&lt;/body&gt;"</code><code>);</code>

<code>43.</code><code>out</code><code>.println(</code><code>"&lt;/html&gt;"</code><code>);</code>

<code>44.</code><code>} </code><code>finally</code> <code>{           </code>

<code>45.</code><code>out</code><code>.close();</code>

<code>46.</code><code>}</code>

<code>47.</code><code>}</code>

<code>48.</code><code>}</code>

将項目builder之後執行應該可以得到如下的結果。

java調用WCF問題

在撰寫過程﹐花了最多時間是在java application 如何調用wcf 的部分﹐目前我所成功的就是 netbeans + metro 及 glassfish﹐至于使用asix 2 還沒成功過﹐不過如果是 basichttpbinding 以 transport 方式加密﹐使用 asix 倒是可以﹐這留待下一篇再繼續了。