استفاده از Apache SOAP براى ساخت برنامه‌هاى كاربردى مبتنى بر SOAP

 

قسمت دوم

 

نويسنده: Tarak Modi

مترجم: شهناز پيروزفر

 

چكيده‌

ساخت‌ برنامه‌هاي‌ كاربردي‌ كه‌ از SOAP استفاده‌ مي‌كنند، دشوار نيست‌ و حتي Apache SOAP انجام‌ اين‌ كار را سهولت‌ مي‌بخشد. در اين‌ بخش‌ پياده‌سازي Apache SOAP به‌ همراه‌ چند مثال‌ ساده‌ براي‌ ساخت‌ برنامه‌هاي‌ كاربردي‌ مبتني‌ بر SOAP آمده‌ است.

(Simple Object Access Protocol) SOAP يك‌ پروتكل‌ با سيم‌ است‌ كه‌ از XML براي‌ كدينگ‌ داده‌ها استفاده‌ مي‌كند و فقط‌ مهمترين‌ بخش‌هاي‌ بحراني‌ مورد نياز يك‌ پروتكل‌ با سيم‌ را تعريف‌ مي‌كند و به‌ بيان‌ جزييات Collection، object activation و غيره‌ نمي‌پردازد. SOAP براي‌ توسعه‌دهندگان‌ جاوا حائز اهميت‌ است. زيرا با ايجاد پلات‌فرم‌ مشتمل‌ و قابليت‌ سير بيشتر، بر ارزش‌ جاوا مي‌افزايد. در واقع‌ اگر در نسخه‌ آتي‌ پلات‌فرم (Java 2 Enterprise Edition) J2EE به‌ پروتكلي‌ اجباري‌ تبديل‌ شود كه‌ همه‌ سرورهاي‌ برنامه‌ كاربردي‌ سازگار با J2EE بايد از آن‌ پشتيباني‌ نمايند، تعجبي‌ ندارد.

 

معرفي Apache SOAP

Apache SOAP، مبتني‌ بر SOAP4J شركت‌ IBM‌ است. Apache SOAP همانند همه‌ پروژه‌هاي Apache، Open-Source و تحت‌ ليسانس Apache موجود است‌ و به‌ عقيده‌ من‌ در حال‌ حاضر يكي‌ از بهترين‌ پياده‌سازي‌هاي SOAP محسوب‌ مي‌شود. از آن‌ جايي‌ كه Apache SOAP منطبق‌ بر نسخه‌ 1/1 مشخصه SOAP است، از برخي‌ گونه‌هاي‌ موجود در SOAP1.1  پشتيباني‌ نمي‌كند.

 

download و نصب Apache SOAP

همان‌گونه‌ كه‌ در فوق‌ اشاره‌ شد، شما مي‌توانيد Apache SOAP را مجاني download نماييد. من‌ فايل Soap-bin-2.0.Zip را كه‌ حاوي‌ آخرين‌ نسخه‌ يعني Aparche SOAP 2.0  بود، براي‌ محيط‌ ويندوز ان‌تي‌ كامپيوتر كيفي‌ام download نمودم. نصب Apache SOAP شامل‌ سه‌ مرحله ساده‌ است:

 

1. unzip فايل download شده:

 اين‌ كار درساخت‌ ساب‌دايركتوري Soap-2_0 نتيجه‌ مي‌دهد. من‌ محتويات‌ فايل‌ را در دايركتوري‌ ريشه‌ درايو E خود unzip نمودم. لذا اينك‌ دايركتوري E:\Soap-2_0 من‌ حاوي Apache SOAP است.

 

2. تنظيم‌ محيط‌ وب:

شما به‌ وب‌ سروري‌ نياز داريد كه‌ از Servaletها و JSPها (Java Server Pages) پشتيباني‌ نمايد. در اين‌ حالت‌ در يكي‌ از طبقه‌بندي‌هاي‌ زير قرار خواهيد گرفت.

 

 

شكل 1 : ابزار Apache SOAP administration

 

 

 

 

طبقه‌ 1: پيشتر وب‌ سروري‌ داشتيد كه‌ از servletها و JSPها پشتيباني‌ مي‌كند و شما با پيكربندي‌ آن‌ احساس‌ راحتي‌ مي‌كنيد. در اين‌ حالت‌ وب‌ سرور خود را به‌ گونه‌اي‌ تنظيم‌ نماييد كه‌ مرورگر بتواند به

http://pcworldi.ipowermysql.com:8080/apache-soap/

اشاره‌ نمايد.

مرورگر به‌ فايل index.html در دايركتوري soap-2_0\webapps\soap\  دسترسي‌ پيدا خواهد كرد.

طبقه‌ 2: شما وب‌ سروري‌ نداريد كه‌ از servletها و JSPها پشتيباني‌ كند. در اين‌ حالت‌ پيشنهاد مي‌كنم‌ آخرين‌ نسخه  Tomcat (در حال‌ حاضر نسخه‌ 1/3) را download نماييد. Tomcat مثالي‌ از يك‌ نرم‌افزار عالي‌ است. به‌ محض‌ اينكه‌ فايل Zip مناسب‌ را downloan نموديد (Jakarta-tomcat-3.1.1. zip) با آن‌ را unzip نماييد. ساب‌ دايركتوري Jakarta-tomcat ساخته‌ خواهد شد. من‌ محتويات‌ آن‌ را در دايركتوري‌ ريشه‌ درايو E قرار دادم. Context جديدي‌ را به‌ فايل‌ پيكربندي Jakarta-tomcat\conf\server.xml به‌ صورت‌ زير بيفزاييد:

 

<Contextpath"/apach-soap"docBase="E:/Soap-2-0/webapps/soap"

debug = “1” reloadable= “true”>

</Context>

شما بايد E: را در ويژگي docBase از عنصر Context با مكان‌ دايركتوري Soap-2_0 جايگزين‌ نماييد. براي‌ كار با Tomcat فايل startup.bat (startup.sh در يونيكس) را اجرا و براي‌ متوقف‌ نمودن‌ آن‌ فايل shutdown.sh) shutdown.bat در يونيكس) را اجرا نماييد.

اما فعلا صبر كنيد.

 

3. تنظيم classpath وب‌ سرور:

Apache SOAP به Apache Xerces نسخه‌ 2/1/1 يا بالاتر كه‌ از (Document Object Model) DOM پشتيباني‌ مي‌كند نياز دارد. من‌ از نسخه‌ Xerces 1.2 كه‌ به‌ صورت Xerces-J-bin.1.2.0.zip موجود است‌ استفاده‌ مي‌كنم. با unzip اين‌ فايل‌ دايركتوري Xerces-1_2_0 ساخته‌ مي‌شود. همانند قبل‌ فايل‌ را در دايركتوري‌ ريشه‌ درايو E كامپيوترم unzip نمودم. شما بايد وب‌ سرور را براي‌ استفاده‌ از Xerces.Jar (كه‌ در ساب‌ دايركتوري Xerces-1_2_0 است) برخلاف‌ هر library/Jar اصلي‌ كه‌ با سرور همراه‌ است‌ - براي‌ همه Parsingهاي XML پيكربندي‌ نماييد. براي‌ نمونه Tomcat با (Jakarta – tomcat\lib\xml.jar) XML Parser كه‌ رابط‌هاي DOM level 1 را دارد همراه‌ است.

حتي‌ اگر Xerces.Jar را در CLASSPATH خود قرار دهيد، هر كد جاوا كه‌ در Tomcat اجرا مي‌شود، رابط‌هاي‌ اشتباه‌ را پيدا مي‌كند، زيرا فايل shell script/batch كه‌ براي‌ اجراي Tomcat به‌ كار مي‌بريد CLASSPATH را در انتها قرار مي‌دهد لذا بايدtomcat.bat  (tomcat.sh براي‌ يونيكس) را در tomcat\bin\directory ويرايش‌ نموده‌ و xerces.Jar را در جلوي CLASSPATHي‌ كه‌ اسكريپت‌ مي‌سازد، قرار دهيد.

 من‌ به‌ ترتيب‌ زير در فايل Jakarta-tomcat\bin\tomcat.bat عمل‌ نمودم:

 

set CLASSPATH=E:\Xerces-1_2_0\Xerces.jar;%CLASSPATH%; %cp%

 

اگر در طبقه‌ 2 گام‌ 2 قرار مي‌گيريد بايد سرور را براي‌ استفاده‌ از xerces.Jar نيز پيكربندي‌ نماييد.

صرفنظر از طبقه‌اي‌ كه‌ در آن‌ قرار داريد، بايد CLASSPATH را به‌ گونه‌اي‌ كه‌ بتوانيد از soap.Jar از دايركتوري Soap-2_0\lib\ استفاده‌ كنيد، تنظيم‌ نماييد.

 

نصب‌ خود را آزمايش‌ كنيد

اينك، وب‌ سرور خود را start نموده‌ و مرورگر را در مسير

http://pcworldi.ipowermysql.com: 8080/apache-soap/admin

 قرار دهيد. در اين‌ حالت‌ صفحه administration را مانند شكل‌ زير مشاهده‌ خواهيد نمود:

 

مثال HelloWorld

اينك‌ كه Apache SOAP را تنظيم‌ نموديد، اجازه‌ دهيد آن‌ را با يك‌ برنامه‌ كاربردي‌ ساده HelloWorld مورد استفاده‌ قرار دهيم. برنامه‌هاي‌ كاربردي‌ در SOAP، سرويس‌ ناميده‌ مي‌شوند. سرويسها در دو مرحله‌ ساخته‌ مي‌شوند كه‌ شايد توسط‌ شخص‌/سازماني‌ اجرا شوند. مرحله‌ اول‌ تعريف‌ و پياده‌سازي‌ سرويس‌ در زبان‌ انتخابي‌ است: در اين‌ حالت‌ جاوا مرحله‌ دوم، ساخت‌ كلاينت‌هايي‌ است‌ كه‌ سرويس‌ را درخواست‌ مي‌كنند. اجازه‌ دهيد در ابتدا به‌ سرويس HelloWorld نظر بيندازيم.

 

The HelloWorld Service

سرويس HelloWorld پياده‌سازي Apache SOAP مثالي‌ است‌ كه‌ در بخش‌ 1 مورد بررسي‌ قرار دادم. اين‌ سرويس‌ نام‌ شخص‌/كاربر را گرفته‌ و پيام Hello را بازمي‌گرداند. كد زير پياده‌سازي‌ كامل‌ سرويس HelloWorld را نشان‌ مي‌دهد:

package hello;
public class HelloServer
{
   public String sayHelloTo(String name)
   {
      System.out.println("sayHelloTo(String name)");
      return "Hello " + name + ", How are you doing?";      
   }    
}

Apache SOAP ساخت‌ سرويسها را فوق‌العاده‌ آسان‌ مي‌سازد. سرويس‌ شامل‌ منطق‌ تجاري‌ است‌ و درواقع‌ كدي‌ است‌ كه‌ شما صرفنظر از نحوه‌ در دسترس‌ قرار دادن‌ سرويس‌ مي‌نويسيد. به‌ عبارتي، سرويس‌ با هر كد مربوط‌ به SOAP خراب‌ نمي‌شود زيرا زيرساختار Apache SOAP كه‌ شامل rpcrouter servlet و soap.gar است. همه‌ اعمال‌ پيچيده‌ را برايتان‌ انجام‌ مي‌دهد. اجازه‌ دهيد كمي‌ درباره‌ اين‌ اعمال‌ پيچيده‌ صحبت‌ كنيم.

چگونه Apache SOAP درخواست‌هاي (Remote Procedure Call) RPC را بر روي HTTP انجام‌ مي‌دهد؟ درك‌ اين‌ مطلب، ساخت‌ كلاينت‌ها را آسان‌ مي‌سازد (بله، كلاينت‌ها). بسته org.apache.Soap.rpc از انجام RPC بر روي SOAP در Apache SOAP پشتيباني‌ مي‌كند. مفهوم‌ پشتيباني RPC، آبجكت ID است. همه‌ سرويس‌هاي Apache SOAP بايد يك ID (شماره‌ شناسايي) منحصر بفرد داشته‌ باشند. منحصر بفرد بودن‌ نسبي‌ است؛ در Apache SOAP منحصر بفرد بودن‌ اين‌ آبجكت IDها به‌ سرور Apache SOAP مربوط‌ مي‌شود. به‌ عبارتي‌ در سرويسي‌ كه‌ بر روي‌ سرورهاي‌ مختلف Apache SOAP نصب‌ مي‌شوند، ممكن‌ است‌ آبجكت ID يكساني‌ داشته‌ باشند. كلاينتي‌ كه‌ از سرويس‌ استفاده‌ مي‌كند، آبجكت org.apache.Sorp.rpc.call را با آبجكت ID دلخواه، نام‌ متد درخواستي‌ و پارامترهايي‌ براي‌ متد (در صورت‌ وجود) تنظيم‌ مي‌نمايد. به‌ محض‌ اينكه‌ آبجكت call تنظيم‌ شد كلاينت‌ متد invoke() را كه‌ دو پارامتر دارد فرا مي‌خواند. اولين‌ پارامتر يك URL‌ به rpcrouter servlet، در اين‌ مورد URL:

 

http://pcworldi.ipowermysql.com:8080/apache-soap/servlet/rpcrouter

مي‌باشد.

پارامتر دوم SOAP Action header است.

متد invoke() آبجكت Call را به‌ درخواست XML SOAP (كه‌ مشابه‌ مثال‌هاي‌ بخش‌ 1 است) تبديل‌ نموده‌ و درخواست‌ را به rpcrouter servlet كه‌ توسط URL تعيين‌ شده‌ ارسال‌ مي‌دارد. وقتي Servlet پاسخ‌ را بازگرداند، متد Invoke() آبجكت org.apache.soap.rpc.Response كه‌ حاوي‌ پاسخ‌ سرويس‌ (در صورت‌ وجود) يا خطاست‌ (اگر خطايي‌ به‌ وجود آيد) بازمي‌گرداند HTTP مشخص‌ مي‌كند كه‌ هر درخواست‌ بايد پاسخ‌ داشته‌ باشد حتي‌ اگر سرويس‌ پاسخي‌ ندهد. rpcrouter servlet پاسخ‌ را بازمي‌گرداند. لذا همواره‌ متد Invoke() آبجكت Response را بازخواهد گرداند. در سمت‌ سرويس، سرور rpcrouter servlet -- Apache SOAP -- درخواست Posted SOAP را از كلاينت‌ دريافت‌ و آبجكت Call را بازسازي‌ مي‌كند. servlet آبجكت ID را از آبجكت Call بازسازي‌ شده‌ استفاده‌ مي‌كند و آبجكت‌ را در مدير سرويس‌ قرار مي‌دهد. سپس Servlet نام‌ متد را تاييد نموده‌ و آن‌ را در آبجكت‌ استقرا يافته‌ صدا مي‌كند. Servlet مقدار بازگشتي‌ اين‌ فراخواني‌ را سري‌ نموده‌ و آن‌ را در پاسخ HTTP ارسال‌ مي‌دارد.

بحث‌ فوق‌ اين‌ پوشش‌ را پديد مي‌آورد: چگونه Apache SOAP مي‌فهمد كه‌ نوع‌ داده‌اي‌ را سري‌ نمايد و چگونه‌ سري‌سازي‌ را انجام‌ مي‌دهد؟ Apache SOAP از طريق‌ رجيستري type-mapping  (org.apache.soap.encoding.SOAPMappingRegistry) و رابط‌هاي‌ سري سازي (org.apache.soap.util.xml.serializer) و deserialize (org.apache.soap.util.xml.Deserialization) انجام‌ مي‌دهد. Apache SOAP تعدادي unmarshaller, marshaller پيش‌ساخته‌ براي‌ پياده‌سازي‌ اين‌ رابط‌ها فراهم‌ مي‌آورد. براي‌ نمونه، شما مي‌توانيد از كلاس org.apache.soap.encoding.soapenc.BeanSerializer براي JavaBeanهاي marshall و unmarshall استفاده‌ نماييد.

Serializerها و deserializerها براي‌ انواع‌ اصلي‌ جاوا (مثلا int، lonf، double) در رجيستري type-mapping ثبت‌ شده‌اند. با وجود اين‌ اگر سرويس‌ متد به‌ يك‌ پارامتر JavaBean نياز داشته‌ باشد، بايد به BeanSerializer را با رجيستري type-mapping به‌ طور دستي‌ ثبت‌ كند هيچگاه‌ سرويس‌ كار اضافه‌ انجام‌ نمي‌دهد.

 

راه‌اندازي‌ سرويس HelloWorld

به‌ دو روش‌ مي‌توان‌ سرويسي‌ را در Apache SOAP راه‌اندازي‌ نمود: استفاده‌ از ابزار administration يا خط‌ فرمان.

 

به‌ كارگيري‌ ابزار administration

بدين‌ منظور از طريق‌ مرورگر به http://pcworldi.ipowermysql.com:8080/apache-Soup/admin برويد. در اين‌ حالت‌ پنجره‌اي‌ مانند شكل‌ 1 به‌ نمايش‌ درمي‌آيد. اينك‌ بر روي‌ دكمه Deploy كليك‌ نماييد. فرمي‌ به‌ همراه‌ تعدادي‌ فيلد ظاهر مي‌گردد. در اين‌ بخش‌ تعدادي‌ از اين‌ فيلدها معرفي‌ مي‌شود.

فيلد ID براي‌ ست‌ كردن‌ آبجكت ID به‌ كار مي‌رود همانگونه‌ كه‌ در فوق‌ اشاره‌ شد. زيرساختار SOAP از آبجكت ID براي‌ برقراري‌ ارتباط‌ بين‌ درخواست RPC و سرويس SOAP استفاده‌ مي‌كند. من‌ عموما از فرمت urn:<UniqueServiceID> استفاده‌ مي‌كنم. uniqueServiceID، آبجكت ID منحصر بفرد براي‌ سرويس‌ من‌ است. براي‌ مثال‌ مذكور ID را به urn:Hello ست‌ نماييد.

فيلد Scope براي‌ تعريف‌ طول‌ عمر نمونه‌اي‌ از سرويس‌ به‌ كار مي‌رود و شايد يكي‌ از چهار مقدار زير را داشته‌ باشد:

Pageo: نمونه‌ سرويس‌ تا زماني‌ كه‌ پاسخ‌ برگشت‌ يا درخواست‌ به Page ديگري forward وجود دارد. اگر از مكانيزم‌ استاندارد راه‌اندازي‌ استفاده‌ كنيد request-forwarding روي‌ نخواهد داد.

Request: نمونه‌ سرويس‌ در زمان‌ درخواست‌ صرفنظر از forward وجود دارد.

Session: نمونه‌ سرويس‌ براي‌ كل Session وجود دارد.

Application: نمونه‌ سرويس‌ يكسان‌ براي‌ همه‌ درخواستي‌ها مورد استفاده‌ قرار مي‌گيرد.

توجه‌ نماييد كه‌ مقدار Scope مي‌تواند اثرات‌ امنيتي‌ مهمي‌ داشته‌ باشد. مقادير Page و درخواست‌ جداسازي‌ فراخواني‌هاي‌ موفق‌ را تاييد مي‌كنند. مقدار برنامه‌ كاربردي‌ بدين‌معناست‌ كه‌ همه‌ كاربران‌ سرور SOAP در نمونه‌ سرويس‌ با هم‌ اشتراك‌ دارند. تگ <JSP:useBean> در JSP نيز از اين‌ مقادير استفاده‌ مي‌كند. در واقع، rpcrouter Servlet يك‌ بار JSP Page بوده‌ است. Scope را براي‌ مثال‌ مذكور application ست‌ نماييد.

فيلد Methods را به‌ فهرست white-space-delimit اسامي‌ متد ست‌ كنيد. سرويس‌ مثال‌ مذكور فقط‌ از متد SayHelloTo پشتيباني‌ مي‌كند.

فيلد Provider Type را به Java ست‌ كنيد. به‌ عبارتي‌ سرويس‌ در جاوا پياده‌سازي‌ شده‌ است. فيلد Provider Class را به hello.HelloServer ست‌ كنيد. از آن‌ جايي‌ كه‌ اين‌ متد ايستا نيست، فيلد static را در حالت no قرار دهيد. اينك‌ به‌ پايين‌ پنجره‌ مرورگر رفته‌ و بر روي‌ دكمه Deploy در زيرفوم‌ كليك‌ نماييد و براي‌ حصول‌ اطمينان‌ از صحت deploy در سمت‌ چپ‌ بر روي‌ دكمه List كليك‌ نماييد. در اين‌ حالت‌ سرويس urn:HelloSercice بايد نشان‌ داده‌ شود.

 

از طريق‌ خط‌ فرمان‌

شما مي‌توانيد از كلاس‌ جاواي‌ مبتني‌ بر خط‌ فرمان org.apache.soap.server.serviceManagerClient همراه‌ با Apache SOAP استفاده‌ نماييد. اين‌ كلاس‌ دو پارامتر اجباري‌ مي‌گيرد: يك URL به rpcrouter و يك action. action مي‌تواند به‌ يكي‌ از حالات: list, undeploy, deploy يا query باشد. پارامتر اضافي‌ نيز با توجه‌ به action انتخابي‌ مورد نياز است. براي‌ نمونه‌ اگر action به‌ صورت deploy باشد، شما بايد نام‌ فايل‌ توصيف‌كننده‌ راه‌اندازي XML را كه‌ حاوي‌ همه‌ اطلاعات‌ موردنياز براي‌ راه‌اندازي‌ سرويس‌ از طريق‌ خط‌ فرمان‌ است، فراهم‌ آوريد. براي‌ نمونه XML راه‌اندازي‌ براي‌ سرويس HelloWorld مي‌تواند به‌ صورت‌ زير مشخص‌ گردد:

 

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment" id="urn:Hello">
  <isd:provider type="java" scope="Application" methods="sayHelloTo">
    <isd:java class="hello.HelloServer" static="false"/>
  </isd:provider>  
</isd:service>

 

XML فوق‌ اطلاعات‌ مشابه‌ با به‌كارگيري‌ ابزار administration را فراهم‌ مي‌آورد. به‌ عنوان‌ تمرين، نگاشت‌ اطلاعاتي‌ كه‌ شما به‌ طور دستي‌ در اطلاعات‌ لحاظ‌ شده‌ در XML وارد نموديد امتحان‌ كنيد. براي‌ راه‌اندازي‌ سرويس HelloWorld از خط‌ فرمان‌ اعمال‌ زير را انجام‌ دهيد:

java org.apach.soap.server.ServiceManagerClient http://pcworldi.ipowermysql.com:8080/apach-soap/servlet/repcrouter deploy Deployment Descriptor.xml

DeploymentDescriptor.xml نام‌ فايلي‌ است‌ كه‌ حاوي XML راه‌اندازي‌ است. براي‌ حصول‌ اطمينان‌ از راه‌اندازي‌ موفق‌آميز سرويس‌ به‌ صورت‌ زير عمل‌ كنيد:

java org.apach.soap.server.ServiceManagerClient http://pcworldi.ipowermysql.com:8080/apache-soap/servlet/repcrouter query urn:Hello

 

 

كلاينت HelloWorld

كدينگ‌ برنامه‌ كلاينت‌ دشوارتر از كدينگ‌ سرويس HelloWorld است. جاي‌ تعجب‌ ندارد؛ شما مي‌دانيد كه‌ كلاينت‌ مسئول‌ تنظيم‌ آبجكت Call است.

فهرست‌ 1 برنامه‌ كلاينت‌ را نشان‌ مي‌دهد. اجازه‌ دهيد گام‌ به‌ گام‌ آن‌ را بررسي‌ نماييم.

 اين‌ برنامه‌ يك‌ پارامتر اجباري‌ دارد: نام‌ فردي‌ كه‌ سلام‌ مي‌دهد.

 

 

Listing 1: Client.Java

 

package hello;

import java.net.URL;
import java.util.Vector;            
import org.apache.soap.SOAPException;
import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
                            
public class Client
{
   public static void main(String[] args) throws Exception
   {      
      if(args.length == 0)
      {
         System.err.println("Usage: java hello.Client [SOAP-router-URL] ");
         System.exit (1);
      }

      try
      {
         URL url = null;
         String name = null;              
         if(args.length == 2)
         {
            url = new URL(args[0]);
            name = args[1];
         }
         else
         {
            url = new URL("http://pcworldi.ipowermysql.com:8080/apache-soap/servlet/rpcrouter");        
            name = args[0];
         }

         // Build the call.
         Call call = new Call();
         call.setTargetObjectURI("urn:Hello");
         call.setMethodName("sayHelloTo");
         call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);        
         Vector params = new Vector();        
         params.addElement(new Parameter("name", String.class, name, null));
         call.setParams(params);

         // Invoke the call.
         Response resp = null;        
         try
         {
            resp = call.invoke(url, "");
         }
         catch( SOAPException e )
         {
            System.err.println("Caught SOAPException (" + e.getFaultCode() + "): " + e.getMessage());
            System.exit(-1);
         }
  
         // Check the response.
         if( !resp.generatedFault() )
         {
            Parameter ret = resp.getReturnValue();
            Object value = ret.getValue();            
            System.out.println(value);
         }
         else
         {
            Fault fault = resp.getFault();            
            System.err.println("Generated fault: ");
            System.out.println ("  Fault Code   = " + fault.getFaultCode());  
            System.out.println ("  Fault String = " + fault.getFaultString());
         }
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }  
}

 

كار كلاينت‌ باتنظيم‌ آبجكت Call آغاز مي‌گردد. اين‌ آبجكت‌ اطلاعات‌ زير را نياز دارد:

- آبجكت ID سرويس‌ درخواستي‌ كه‌ از طريق‌ متد setTarget Object URI() بر روي‌ آبجكت Call ست‌ مي‌شود. آبجكت ID شما urn:Hello است.

- نام‌ متدي‌ كه‌ سرويس‌ را صدا مي‌زند كه‌ از طريق‌ متد setMethodName() بر روي‌ آبجكت Call، ست‌ مي‌شود. نام‌ متدتان SayHelloto است.

- سبك‌ اينكدينك‌ به‌ كار رفته‌ براي‌ اين‌ كد پارامترها كه‌ از طريق‌ متد SetEncodingstyleURI() بر روي‌ آبجكت Call تست‌ مي‌شود. شايد سبك‌ اينكدينگ SOAP استاندارد به‌ صورت‌ زير برايتان‌ جالب‌ باشد:

 

http://schemas.xmlsoap.org/soap/encoding/

 

- پارامترهاي‌ براي‌ فراخواني‌ متد از طريق setParams() بر روي‌ آبجكت Call ست‌ مي‌شود.

- متد SetParams()، Vector جاوا را به‌ عنوان‌ پارامتر مي‌پذيرد. vector حاوي‌ همه‌ پارامترهاست‌ انديس‌ صفر اولين‌ پارامتر در متد (شروع‌ از چپ)، انديس‌ 1 دومين‌ پارامتر و به‌ همين‌ ترتيب‌ هر عنصر در vector، نمونه‌اي‌ از org.apache.soap.rpc.parameter است.

سازنده parameter، نام‌ پارامتر، نوع‌ جاوا و مقدار و يك‌ سبك‌ اختياري‌ را مي‌گيرد.

 اگر سبك‌ اينكدينگ null باشد. سبك‌ اينكدينگ‌ آبجكت Call مورد استفاده‌ قرار خواهد گرفت. عليرغم‌ اينكه‌ هر پارامتر به‌ نام‌ وابسته‌ است، اين‌ نام‌ مي‌تواند تهي‌ باشد و سرور Apache SOAP در هنگام‌ صدا زدن‌ متد، آن‌ را مورد استفاده‌ قرار ندهد.

كد زير نحوه‌ ساخت‌ آبجكت Call را توسط‌ كلاينت‌ نشان‌ مي‌دهد:

 

// Build the call.
Call call = new Call();
call.setTargetObjectURI("urn:Hello");
call.setMethodName("sayHelloTo");
call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);        
Vector params = new Vector();        
params.addElement(new Parameter("name", String.class, name, null));
call.setParams(params);

 

اينك‌ زمان‌ آن‌ فرارسيده‌ تا متد بر روي‌ سرويس HelloWorld از راه‌ دور صدا زده‌ شود. بدين‌منظور كلاينت‌ متد invoke() را بر روي‌ آبجكت Call فرامي‌خواند. اين‌ متد آبجكت org.apache.soap.rpc.Response را به‌ صورت‌ زير بازمي‌گرداند:

// Invoke the call.
Response resp = null;        
try
{
   resp = call.invoke(url, "");
}
catch( SOAPException e )
{
   System.err.println("Caught SOAPException (" + e.getFaultCode() + "): " + e.getMessage());
   System.exit(-1);
}

 

سپس کلاينت شيي Response را كنترل مي‌کند. اگر فعال سازی متد با خطا مواجه شود، متد generatedFault() مقدار true باز می‌گرداند و خطاي واقعی نمايش داده مي‌شود.

 

Fault fault = resp.getFault();            
System.err.println("Generated fault: ");
System.out.println ("  Fault Code   = " + fault.getFaultCode());  
System.out.println ("  Fault String = " + fault.getFaultString());

 

اگر متد كار خود را با موفقيت‌ انجام‌ داد، پيام Hello به‌ نمايش‌ درمي‌آيد:

 

// Check the response.
if( !resp.generatedFault() )
{
   Parameter ret = resp.getReturnValue();
   Object value = ret.getValue();            
   System.out.println(value);
}

 

مثال HelloWorld با JavaBeans

همان‌گونه‌ كه‌ پيشتر اشاره‌ نمودم Apache SOAP تعدادي deserializer  و serializer پيش‌ساخته‌ فراهم‌ مي‌آورد كه Vectors، Enumeration، array، Java Beans را مورد استفاده‌ قرار مي‌دهند و مقاديري‌ را بازمي‌گردانند. در اين‌ بخش‌ سرويس HelloWorld را تغيير مي‌دهيم‌ و از نام JavaBean براي‌ عبور دادن‌ در نام‌ فردي‌ كه‌ پيام Hello را دريافت‌ مي‌دارد، استفاده‌ مي‌كنيم.

 

سرويس HelloWorld

پياده‌سازي‌ كامل‌ سرويس‌ جديد HelloWorld در ذيل‌ آمده‌ است:

 

package hello;
public class HelloServer
{
   public String sayHelloTo(String name)
   {
      System.out.println("sayHelloTo(String name)");
      return "Hello " + name + ", How are you doing?";      
    }

   public String sayHelloTo(Name theName)
   {
      System.out.println("sayHelloTo(Name theName)");
      return "Hello " + theName.getName() + ", How are you doing?";      
   }
}

اين‌ سرويس‌ هنوز ساده‌ است. در واقع‌ تنها تفاوت‌ بين‌ اين‌ نسخه‌ سرويس‌ و نسخه‌ پيشين، حضور متد sayHelloTo است. اين‌ متد رجوع‌ به Name JavaBean است‌ كه‌ تعريف‌ آن‌ به‌ صورت‌ زير مي‌باشد:

 

package hello;

public class Name
{
   private String name;
   public String getName()
   {
      return name;
   }
   public void setName(String name)
   {
      this.name = name;
   }
}

 

فهرست‌ 2 سرويس‌ كامل HelloWorld را نشان‌ مي‌دهد:

 

package hello;

public class HelloServer
{
   public String sayHelloTo(String name)
   {
      System.out.println("sayHelloTo(String name)");
      return "Hello " + name + ", How are you doing?";      
   }

   public String sayHelloTo(Name theName)
   {
      System.out.println("sayHelloTo(Name theName)");
      return "Hello " + theName.getName() + ", How are you doing?";      
   }
}

 

راه‌اندازي‌ سرويس‌

به‌ دليل‌ اينكه‌ براي‌ راه‌اندازي‌ سرويسي‌ كه‌ از JavaBean استفاده‌ مي‌كند به‌ اطلاعات‌ اضافي‌ براي‌ سرور Apache SOAP نياز داريد، راه‌اندازي‌ سرويس‌ كمي‌ پيچيده‌تر خواهد بود:

 

استفاده‌ از ابزار administration

Text Box: نشريه علم‌الكترونيك و كامپيوتر ديدگاه وسيعتري از دانش الكترونيك و كامپيوتر را به شما معرفي مي‌نمايد
بدين‌منظور دستور عمل‌هاي‌ مشابه‌ قبلي‌ را انجام‌ دهيد؛ اما دكمه Deploy را كليك‌ نكنيد. اينك‌ فيلد Number of Mappings را به‌ 1 ست‌ نماييد. جدولي‌ در زير فيلد Mappings وجود دارد و شما اولين‌ رديف‌ جدول‌ را مورد استفاده‌ قرار خواهيد داد. Encoding Style را به‌ صورت SOAP نگهداريد. فيلد NameSpace UR1 را به object ID ست‌ كنيد در اين‌ حالت urn.Hello اينك‌ فيلدهاي Local part و Java Type را به‌ نام hello.Name ست‌ كنيد. سرانجام‌ فيلدهاي Java to XML serializer و XML to Java Deserializer را به org.apache.soap.encoding.soapenc.Bean.Serializer ست‌ كنيد كه‌ كلاسي‌ است‌ كه‌ رابط‌هاي serializer و Deserializer را پياده‌سازي‌ مي‌كند. اگر JavaBeans بيشتري‌ داشتيد (نظير (Address Bean اطلاعات‌ اين beanها را در اين‌ جدول‌ وارد نماييد.

 

از طريق‌ خط‌ فرمان‌

شما از طريق‌ خط‌ فرمان‌ بايد فايل XML deplyment descriptor را به‌ روز برسانيد. XML به‌ روزرساني‌ شده‌ به‌ صورت‌ زير نشان‌ داده‌ مي‌شود.

 

<isd:service xmlns:isd="http://xml.apache.org/xml-soap/deployment" id="urn:Hello">
  <isd:provider type="java" scope="Application" methods="sayHelloTo">
    <isd:java class="hello.HelloServer" static="false"/>
  </isd:provider>  
  <isd:mappings>
    <isd:map encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
             xmlns:x="urn:Hello" qname="x:hello.Name"
             javaType="hello.Name"
             java2XMLClassName="org.apache.soap.encoding.soapenc.BeanSerializer"
             xml2JavaClassName="org.apache.soap.encoding.soapenc.BeanSerializer"/>
  </isd:mappings>    
</isd:service>

 

XML همانند قبل‌ حاوي‌ همان‌ اطلاعاتي‌ است‌ كه‌ از طريق‌ ابزار administration مبتني‌ بر وب‌ به‌ دست‌ مي‌آمد.

 

كلاينت HelloWorld

برنامه‌ كلاينت‌ جالب‌تر است. من‌ بر روي‌ تفاوت‌ بين‌ دو نسخه‌ برنامه‌ كلاينت‌ تمركز خواهم‌ نمود. از آن‌ جايي‌ كه‌ يكي‌ از پارامترها يا در اين‌ حالت‌ تنها پارامتر JavaBean است، شما بايد به‌ طور دستي‌ رجيستري type-mapping را تنظيم‌ نماييد. با ساخت‌ نمونه‌ جديد كلاس org.apache.soap.encoding.SOAPMappingRegistry و فراخواني‌ متد mapTypes() اين‌ كار را تكميل‌ نماييد.

 اين‌ متد براي‌ اعلان‌ رجيستري‌ نوع‌ ناشناخته‌ مانند JavaBean سفارشي‌ به‌ كار مي‌رود mapTypes() سبك‌ اينكدينگ‌ به‌ كار رفته‌ نام‌ مجاز، نام‌ كلاس‌ و Serializer, deserializer را مورد استفاده‌ قرار مي‌دهد. نام‌ مجاز شامل‌ نام‌ عنصر يا همان‌ فضاي‌ نامي‌ كه‌ به‌ آن‌ تعلق‌ دارد مي‌باشد. در مثال‌ مذكور نام‌ مجاز با تركيبي‌ از (urn:Hello) URI و نام‌ محلي (hello.Name) تشكيل‌ مي‌شود و كد آن‌ در زير آمده‌ است:

 

// Create the type mapping registry
SOAPMappingRegistry smr = new SOAPMappingRegistry();
BeanSerializer beanSer = new BeanSerializer();                    
// Map the types.
smr.mapTypes(Constants.NS_URI_SOAP_ENC,
new QName("urn:Hello", "hello.Name"),hello.Name.class, beanSer, beanSer);

 

سپس بايد به شيي Call گفته شود از اين رجيستری بجای رجيستري ÷یش فرض استفاده کند. بدين منظور متد setSOAPMappingRegistry() را در شيي Call همانند مثال زير فراخواني کنيد:

 

call.setSOAPMappingRegistry(smr);

 

به‌ محض‌ اينكه‌ رجيستري type.mapping را تنظيم‌ نموديد، بايد پارامترها را براي‌ آبجكت Call تنظيم‌ نماييد. همانند قبل‌ اين‌ كار را انجام‌ دهيد؛ تنها تفاوت‌ اين‌ است‌ كه‌ به‌ جاي‌ پارامتر string پارامتر JavaBean را به‌ صورت‌ زير خواهيد داشت:

 

// Set the Parameters
Vector params = new Vector();        
Name theName = new Name();
theName.setName(name);
arams.addElement(new Parameter("name", hello.Name.class, theName, null));
call.setParams(params);

 

 

فهرست‌ 3 برنامه‌ كامل‌ كلاينت‌ را نشان‌ مي‌دهد:

package hello;

import java.net.URL;
import java.util.Vector;            
import org.apache.soap.SOAPException;
import org.apache.soap.Constants;
import org.apache.soap.Fault;
import org.apache.soap.rpc.Call;
import org.apache.soap.rpc.Parameter;
import org.apache.soap.rpc.Response;
import org.apache.soap.encoding.SOAPMappingRegistry;
import org.apache.soap.encoding.soapenc.BeanSerializer;
import org.apache.soap.util.xml.QName;
                            
public class Client2
{
   public static void main(String[] args) throws Exception
   {      
      if(args.length == 0)
      {
         System.err.println("Usage: java hello.Client [SOAP-router-URL] ");
         System.exit (1);
      }

      try
      {
         URL url = null;
         String name = null;              
         if(args.length == 2)
         {
            url = new URL(args[0]);
            name = args[1];
         }
         else
         {
            url = new URL("http://pcworldi.ipowermysql.com:8080/apache-soap/servlet/rpcrouter");        
            name = args[0];
         }

         // Build the call.
         Call call = new Call();
         call.setTargetObjectURI("urn:Hello");
         call.setMethodName("sayHelloTo");
         call.setEncodingStyleURI(Constants.NS_URI_SOAP_ENC);        

         // Create the type mapping registry
         SOAPMappingRegistry smr = new SOAPMappingRegistry();
         BeanSerializer beanSer = new BeanSerializer();                    
         // Map the types.
         smr.mapTypes(Constants.NS_URI_SOAP_ENC,
             new QName("urn:Hello", "hello.Name"),hello.Name.class, beanSer, beanSer);                    
         // and tell the Call object to use it..
         call.setSOAPMappingRegistry(smr);

         // Set the Parameters
         Vector params = new Vector();        
         Name theName = new Name();
         theName.setName(name);
         params.addElement(new Parameter("name", hello.Name.class, theName, null));
         call.setParams(params);

         // Invoke the call.
         Response resp = null;        
         try
         {
            resp = call.invoke(url, "");
         }
         catch( SOAPException e )
         {
            System.err.println("Caught SOAPException (" + e.getFaultCode() + "): " + e.getMessage());
            System.exit(-1);
         }
  
         // Check the response.
         if( !resp.generatedFault() )
         {
            Parameter ret = resp.getReturnValue();
            Object value = ret.getValue();            
            System.out.println(value);
         }
         else
         {
            Fault fault = resp.getFault();            
            System.err.println("Generated fault: ");
            System.out.println ("  Fault Code   = " + fault.getFaultCode());  
            System.out.println ("  Fault String = " + fault.getFaultString());
         }
      }
      catch(Exception e)
      {
         e.printStackTrace();
      }
   }  
}

كامپايل‌ و اجراي‌ برنامه‌ها

اينك‌ كه‌ مثال‌ كامل‌ را توسعه‌ داديد، زمان‌ آن‌ فرارسيده‌ تا عملكردش‌ را مشاهده‌ كنيد. با وجود اين‌ بايد در ابتدا سرويس‌ و كد كلاينت‌ جاوا را كامپايل‌ نماييد.

يك‌ دايركتوري hello بسازيد وClient1.Java ، Client1 Java و HelloServer.Java را در آن‌ كپي‌ كنيد. من‌ اين‌ دايركتوري‌ را به‌ صورت‌ زير ساختم (E:\soap-2_0\samples). شما بايد براي‌ كامپايل‌ برنامه‌ها به parent دايركتوري soap.Jar, hello و Xerces.Jar نياز داريد. من‌ ز فايل‌ زير براي‌ كامپايل‌ برنامه‌ها استفاده‌ مي‌كنم:

set CLASSPATH=E:\Soap-2_0\samples\; E:\SOAP-2_0\lib\soap.jar; E:\xerces-1_2_0\xerces.jar

javac -d .. HelloServer.java Client.java Client2java

 

تذكر: اطمينان‌ حاصل‌ نماييد كه‌ اين‌ فايل‌ از دايركتوري hello اجرا نشود.

شما براي‌ استفاده‌ از سرويس‌ بايد classpath وب‌ سرور را نيز به‌ گونه‌اي‌ تغيير دهيد كه‌ بتواند  كلاس hello.HelloServer را بيابيد. پس‌ از تغيير لازم‌ وب‌ سرور را restart كنيد. اينك‌ آماده‌ اجراي‌ برنامه‌هاي‌ كلاينت‌ خواهيد بود. فايلي‌ كه‌ براي‌ اجراي helloclient استفاده‌ نمودم‌ به‌ صورت‌ زير است:

 

set CLASSPATH=E:\soap-2_0\samples\;E:\Soap-2_0\lib\soap.jar; E:\Xerces-1_2_0\xerces.jar

 

سرانجام، فايلي‌ كه‌ براي‌ اجراي hello.client2 استفاده‌ مي‌نمايم، در زير آمده‌ است:

 

set CLASSPATH=E:\Soap-2_0\samples\;soap-2_0\lib\Soap.jar; E:\xerces-1_2_0\xerces.gar

java hello.Client2 Tarak

 

منابع

·        The SOAP 1.1 specification at W3C:
http://www.w3.org/TR/SOAP/

·        Download Apache SOAP:
http://xml.apache.org/dist/soap/

·        More information about IBM's SOAP project:
http://www.alphaworks.ibm.com/tech/soap4j

·        A list of Apache SOAP's available features:
http://xml.apache.org/soap/features.html

·        The Apache license:
http://www.apache.org/LICENSE.txt

·        Download Tomcat 3.1:
http://jakarta.apache.org/builds/jakarta-tomcat/release/v3.1.1/bin/

·        Download Apache Xerces version 1.2:
http://xml.apache.org/dist/xerces-j/

·        "MS SOAP SDK vs IBM SOAP4J: Comparison & Review," James Snell (O'Reilly):
http://windows.oreilly.com/news/soapreview_0600.html

·        Sign up for the JavaWorld This Week free weekly email newsletter and keep up with what's new at JavaWorld:
http://www.idg.net/jw-subscribe

·        To read more articles on Java and XML, browse JavaWorld's Topical Index:
http://www.javaworld.com/javaworld/topicalindex/jw-ti-javaxml.html