שירותי SOAP ב-Java – דוגמה באמצעות Eclipse

מימוש שירותי רשת SOAP ב-Java ניתן לבצע בכמה דרכים. למדנו על JAX-WS SOAP Web Services בשיעור האחרון שלנו, היום נלמד כיצד ליצור שירות רשת SOAP ותוכנית לקוחות שלו באמצעות Eclipse. כאן לא נשתמש ב-JAX-WS, אלא נשתמש ב־Apache Axis שמשולב ב-Eclipse ומספק דרך מהירה וקלה להפוך אפליקציה לשירות רשת Java וליצור גם גרסה ללקוח עם דף JSP לצורך בדיקה.

שירותי רשת SOAP ב-Java

אני משתמש ב־Eclipse Mars Release (4.5.0) לצורך המדר, אך אני חושב שהשלבים הללו יעבדו גם עם גרסאות ישנות יותר של Eclipse. יש לוודא גם שהוספת Apache Tomcat או כל מנהל שרת servlet אחר כשרת ב-Eclipse. בואו נתחיל עם מימוש שירות הרשת שלנו ב-Eclipse כעת.

דוגמה לשירות רשת SOAP

נתחיל עם דוגמה לשירות רשת SOAP ב־Eclipse. לכך ניצור פרויקט רשת דינמי ב־Eclipse שיכיל את הלוגיקה העסקית של היישום שלנו. לחץ על הלחצן הבא למעלה ותקבל עמוד הבא לספק את שם פרויקט הרשת שלך ואת Target Runtime. שים לב שאני משתמש ב־Apache Tomcat 8, אפשר גם להשתמש בכל נושא סטנדרטי אחר. לחץ על הבא ותתבקש לספק "שורש ההקשר" ומיקום ספרייה לתוכן. אפשר להשאיר אותם כברירת מחדל. לחץ על סיום ו־Eclipse ייצור לך את מסגרת הפרויקט. בוא נתחיל עם הלוגיקה העסקית שלנו. לדוגמה שלנו, נרצה לפרסם שירות רשת שישמש להוספה/מחיקה/קבלת אובייקט. הצעד הראשון הוא ליצור מודל בינה.

package com.journaldev.jaxws.beans;

import java.io.Serializable;

public class Person implements Serializable{

	private static final long serialVersionUID = -5577579081118070434L;
	
	private String name;
	private int age;
	private int id;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}
	
	@Override
	public String toString(){
		return id+"::"+name+"::"+age;
	}

}

השם של קובץ זה הוא JAVA_BEAN פשוט, אנו מממשים את הממשק `Serializable` מכיוון שנשלח אותו ברשת. כמו כן, סיפקנו את תרגום השיטה `toString` שישמש בעת הדפסת העצם הזה בצד הלקוח. השלב הבא הוא ליצור קבצי שירות, על מנת שנהיה עם ממשק כמו `PersonService` וממשק המימוש הפשוט שלו `PersonServiceImpl`.

package com.journaldev.jaxws.service;

import com.journaldev.jaxws.beans.Person;

public interface PersonService {

	public boolean addPerson(Person p);
	
	public boolean deletePerson(int id);
	
	public Person getPerson(int id);
	
	public Person[] getAllPersons();
}

למטה יש ממשק שירות המימוש, אנו משתמשים במפה לאחסון אובייקטים מסוג `Person` כמקור נתונים. בתכנות בעולם האמיתי, רוצים לשמור את אלו בטבלאות בבסיס נתונים.

package com.journaldev.jaxws.service;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import com.journaldev.jaxws.beans.Person;

public class PersonServiceImpl implements PersonService {

	private static Map<Integer,Person> persons = new HashMap<Integer,Person>();
	
	@Override
	public boolean addPerson(Person p) {
		if(persons.get(p.getId()) != null) return false;
		persons.put(p.getId(), p);
		return true;
	}

	@Override
	public boolean deletePerson(int id) {
		if(persons.get(id) == null) return false;
		persons.remove(id);
		return true;
	}

	@Override
	public Person getPerson(int id) {
		return persons.get(id);
	}

	@Override
	public Person[] getAllPersons() {
		Set<Integer> ids = persons.keySet();
		Person[] p = new Person[ids.size()];
		int i=0;
		for(Integer id : ids){
			p[i] = persons.get(id);
			i++;
		}
		return p;
	}

}

זהו הכל ללוגיקת העסקים שלנו, מכיוון שנשתמש בזה בשירות אינטרנט, אין טעם ליצור כאן דפים אינטרנטיים. שימו לב שאין לנו הפנייה לאיזו מחלקת שירותי אינטרנט בקוד שנמצא למעלה.

SOAP Webservices in Java using Eclipse

ברגע שהלוגיקה העסקית שלנו מוכנה, השלב הבא הוא להשתמש ב־Eclipse כדי ליצור יישום של שירות אינטרנט ממנו. יש ליצור פרויקט חדש ולבחור באשף שירות אינטרנט. לחץ על הלחצן Next ותקבל דף בו יש לספק פרטי שירות האינטרנט והלקוח שלו. זהו הדף החשוב ביותר ביצירת שירות אינטרנט. ודא שבחרת "סוג שירות אינטרנט" כ"שירות Java Bean אינטרנט מלמטה" משום שאנו מיישמים עם גישת מלמטה. ישנם שני דרכים ליצירת שירות אינטרנט:

  1. יצירת חוזה אחרון או גישת מלמטה: בגישה זו אנו יוצרים תחילה את היישום ואז יוצרים את קובץ ה־WSDL ממנו. היישום שלנו נכנס לקטגוריה זו.
  2. יצירת חוזה ראשוני או גישת מלמעלה: בגישה זו, אנו יוצרים תחילה את חוזה שירות האינטרנט, כלומר קובץ ה־WSDL, ואז יוצרים את היישום שלו.

במימוש השירות, ספק את המחלקת המימוש PersonServiceImpl בנתיב מובהק. וודא שתזיז את המחוון בסוג השירות וסוג הלקוח לצד שמאל כך שיוכל לייצר תוכנית לקוח וגם ממשק משתמש לבדיקת השירות שלנו. בדוק את ההגדרות במימוש השירות הרשתי, עליך לספק פרטים נכונים עבור זמן הריצה של השרת, זמן ריצת השירות הרשתי ופרויקט השירות. כללית זהים מתמלאים באופן אוטומטי ואין צורך לבצע שינויים כאן. להגדרות הלקוח, תוכל לספק את שם פרויקט הלקוח כפי שתרצה. עזבתי אותו כברירת מחדל כ-SOAPExampleClient. אם תלחץ על הקישור לזמן ריצת השירות הרשתי, תקבל אפשרויות שונות כפי שמוצג בתמונה למטה. בכל זאת, עזבתי אותו כמו הרגיל. לחץ על הכפתור הבא ואז תוכל לבחור את השיטות שברצונך לחשוף כשירות רשת. תוכל גם לבחור את סגנון השירות הרשתי, בין אם מסמך או מילולי. באפשרותך לשנות את שם מסמך ה- WSDL, אך נכון להשאיר אותו עם שם מחלקת המימוש כדי למנוע בלבול לאחר מכן. לחץ על הכפתור הבא ותקבל עמוד התחלת שרת, לחץ על כפתור "התחל שרת" ואז תתאפשר הכפתור הבא. לחץ על הכפתור הבא ותקבל עמוד להשקת "ממצאי שירותי הרשת". לחץ על הכפתור "הפעל" וזה יפתח חלון חדש בדפדפן שבו תוכל לבדוק את השירות הרשתי שלך לפני שתמשיך עם חלק היישום של הלקוח. זה נראה כמו התמונה למטה עבור הפרויקט שלנו. ניתן לבצע בדיקת אימיתות כאן, אך עבור היישום הפשוט שלנו אני מוכן להמשיך ליצירת יישום הלקוח. לחץ על הכפתור הבא בחלון הקופץ של שירותי הרשת של Eclipse ותקבל עמוד לתיקיית המקור עבור יישום הלקוח. לחץ על הכפתור הבא ותקבל אפשרויות שונות לבחירה כמוסד בדיקה. אני הולך קדימה עם JAX-RPC JSPs כך שהיישום של הלקוח יצור דף JSP שנוכל להשתמש בו. שים לב לשיטות getEndpoint() ו-setEndpoint(String) שנוספו שבאפשרותנו להשתמש בהן כדי לקבל את כתובת ה-URL של נקודת הקצה של השירות הרשתי ולהגדירה לכתובת אחרת במידה ונעבור את השרת שלנו לכתובת URL אחרת. לחץ על הכפתור "סיים" ו- Eclipse תיצור את פרויקט הלקוח במרחב העבודה שלך, וגם תפתח את דף הבדיקה של הלקוח כמוצג למטה. תוכל להעתיק את כתובת ה-URL ולפתוח אותה בדפדפן כלשהו שתבחר. בוא נבדוק כמה מהשירותים שחשפנו ונראה את הפלט.

בדיקת שירות רשת SOAP של Eclipse

  • addPerson

  • getPerson

  • getAllPersons שים לב שפרטי האדם לא מודפסים בחלק התוצאות, זה מכיוון שקוד מקורי מיוצר אוטומטית ואנו צריכים לבצע ריפקטורינג קטן כדי לקבל את הפלט הרצוי. פתח את Result.jsp בפרויקט הלקוח ותראה שהוא משתמש במבנה switch case כדי ליצור את פלט התוצאה. בשביל שיטת getAllPersons(), זה היה case 42 במקרה שלי. שים לב שזה יכול להיות שונה לחלוטין במקרה שלך. פשוט שיניתי את הקוד עבור case 42 כפי שמוצג למטה.

    case 42:
            gotMethod = true;
            com.journaldev.jaxws.beans.Person[] getAllPersons42mtemp = samplePersonServiceImplProxyid.getAllPersons();
    if(getAllPersons42mtemp == null){
    %>
    <%=getAllPersons42mtemp %>
    <%
    }else{
            String tempreturnp43 = null;
            if(getAllPersons42mtemp != null){
            java.util.List<com.journaldev.jaxws.beans.Person> listreturnp43= java.util.Arrays.asList(getAllPersons42mtemp);
            //tempreturnp43 = listreturnp43.toString();
            for(com.journaldev.jaxws.beans.Person p : listreturnp43){
            	int id = p.getId();
            	int age = p.getAge();
            	String name=p.getName();
            	%>
            	<%=id%>::<%=name %>::<%=age %>
            	<%
            	}
            }
            }      
    break;
    

    לאחר מכן אנו מקבלים את הפלט הבא, שים לב ש-Eclipse עושה פיתוח חם כאן, לכן לא הייתה לי צורך להפעיל מחדש את היישום שלי.

נראה שהשירות האינטרנטי ואפליקציות הלקוח עובדות בסדר, ודא שתבלה זמן להסתכל על ה-"stubs" בצד הלקוח שנוצרו על ידי Eclipse כדי להבין יותר.

שירות רשת SOAP WSDL ותצורות

לבסוף תגלה שקובץ ה-WSDL נוצר בפרויקט שירות הרשת כך שיש:

<?xml version="1.0" encoding="UTF-8"?>
<wsdl:definitions targetNamespace="https://service.jaxws.journaldev.com" xmlns:apachesoap="https://xml.apache.org/xml-soap" xmlns:impl="https://service.jaxws.journaldev.com" xmlns:intf="https://service.jaxws.journaldev.com" xmlns:tns1="https://beans.jaxws.journaldev.com" xmlns:wsdl="https://schemas.xmlsoap.org/wsdl/" xmlns:wsdlsoap="https://schemas.xmlsoap.org/wsdl/soap/" xmlns:xsd="https://www.w3.org/2001/XMLSchema">
<!--WSDL created by Apache Axis version: 1.4
Built on Apr 22, 2006 (06:55:48 PDT)-->
 <wsdl:types>
  <schema elementFormDefault="qualified" targetNamespace="https://service.jaxws.journaldev.com" xmlns="https://www.w3.org/2001/XMLSchema">
   <import namespace="https://beans.jaxws.journaldev.com"/>
   <element name="addPerson">
    <complexType>
     <sequence>
      <element name="p" type="tns1:Person"/>
     </sequence>
    </complexType>
   </element>
   <element name="addPersonResponse">
    <complexType>
     <sequence>
      <element name="addPersonReturn" type="xsd:boolean"/>
     </sequence>
    </complexType>
   </element>
   <element name="deletePerson">
    <complexType>
     <sequence>
      <element name="id" type="xsd:int"/>
     </sequence>
    </complexType>
   </element>
   <element name="deletePersonResponse">
    <complexType>
     <sequence>
      <element name="deletePersonReturn" type="xsd:boolean"/>
     </sequence>
    </complexType>
   </element>
   <element name="getPerson">
    <complexType>
     <sequence>
      <element name="id" type="xsd:int"/>
     </sequence>
    </complexType>
   </element>
   <element name="getPersonResponse">
    <complexType>
     <sequence>
      <element name="getPersonReturn" type="tns1:Person"/>
     </sequence>
    </complexType>
   </element>
   <element name="getAllPersons">
    <complexType/>
   </element>
   <element name="getAllPersonsResponse">
    <complexType>
     <sequence>
      <element maxOccurs="unbounded" name="getAllPersonsReturn" type="tns1:Person"/>
     </sequence>
    </complexType>
   </element>
  </schema>
  <schema elementFormDefault="qualified" targetNamespace="https://beans.jaxws.journaldev.com" xmlns="https://www.w3.org/2001/XMLSchema">
   <complexType name="Person">
    <sequence>
     <element name="age" type="xsd:int"/>
     <element name="id" type="xsd:int"/>
     <element name="name" nillable="true" type="xsd:string"/>
    </sequence>
   </complexType>
  </schema>
 </wsdl:types>

   <wsdl:message name="addPersonResponse">

      <wsdl:part element="impl:addPersonResponse" name="parameters">

      </wsdl:part>

   </wsdl:message>

   <wsdl:message name="getAllPersonsResponse">

      <wsdl:part element="impl:getAllPersonsResponse" name="parameters">

      </wsdl:part>

   </wsdl:message>

   <wsdl:message name="deletePersonResponse">

      <wsdl:part element="impl:deletePersonResponse" name="parameters">

      </wsdl:part>

   </wsdl:message>

   <wsdl:message name="addPersonRequest">

      <wsdl:part element="impl:addPerson" name="parameters">

      </wsdl:part>

   </wsdl:message>

   <wsdl:message name="getPersonResponse">

      <wsdl:part element="impl:getPersonResponse" name="parameters">

      </wsdl:part>

   </wsdl:message>

   <wsdl:message name="getPersonRequest">

      <wsdl:part element="impl:getPerson" name="parameters">

      </wsdl:part>

   </wsdl:message>

   <wsdl:message name="deletePersonRequest">

      <wsdl:part element="impl:deletePerson" name="parameters">

      </wsdl:part>

   </wsdl:message>

   <wsdl:message name="getAllPersonsRequest">

      <wsdl:part element="impl:getAllPersons" name="parameters">

      </wsdl:part>

   </wsdl:message>

   <wsdl:portType name="PersonServiceImpl">

      <wsdl:operation name="addPerson">

         <wsdl:input message="impl:addPersonRequest" name="addPersonRequest">

       </wsdl:input>

         <wsdl:output message="impl:addPersonResponse" name="addPersonResponse">

       </wsdl:output>

      </wsdl:operation>

      <wsdl:operation name="deletePerson">

         <wsdl:input message="impl:deletePersonRequest" name="deletePersonRequest">

       </wsdl:input>

         <wsdl:output message="impl:deletePersonResponse" name="deletePersonResponse">

       </wsdl:output>

      </wsdl:operation>

      <wsdl:operation name="getPerson">

         <wsdl:input message="impl:getPersonRequest" name="getPersonRequest">

       </wsdl:input>

         <wsdl:output message="impl:getPersonResponse" name="getPersonResponse">

       </wsdl:output>

      </wsdl:operation>

      <wsdl:operation name="getAllPersons">

         <wsdl:input message="impl:getAllPersonsRequest" name="getAllPersonsRequest">

       </wsdl:input>

         <wsdl:output message="impl:getAllPersonsResponse" name="getAllPersonsResponse">

       </wsdl:output>

      </wsdl:operation>

   </wsdl:portType>

   <wsdl:binding name="PersonServiceImplSoapBinding" type="impl:PersonServiceImpl">

      <wsdlsoap:binding style="document" transport="https://schemas.xmlsoap.org/soap/http"/>

      <wsdl:operation name="addPerson">

         <wsdlsoap:operation soapAction=""/>

         <wsdl:input name="addPersonRequest">

            <wsdlsoap:body use="literal"/>

         </wsdl:input>

         <wsdl:output name="addPersonResponse">

            <wsdlsoap:body use="literal"/>

         </wsdl:output>

      </wsdl:operation>

      <wsdl:operation name="deletePerson">

         <wsdlsoap:operation soapAction=""/>

         <wsdl:input name="deletePersonRequest">

            <wsdlsoap:body use="literal"/>

         </wsdl:input>

         <wsdl:output name="deletePersonResponse">

            <wsdlsoap:body use="literal"/>

         </wsdl:output>

      </wsdl:operation>

      <wsdl:operation name="getPerson">

         <wsdlsoap:operation soapAction=""/>

         <wsdl:input name="getPersonRequest">

            <wsdlsoap:body use="literal"/>

         </wsdl:input>

         <wsdl:output name="getPersonResponse">

            <wsdlsoap:body use="literal"/>

         </wsdl:output>

      </wsdl:operation>

      <wsdl:operation name="getAllPersons">

         <wsdlsoap:operation soapAction=""/>

         <wsdl:input name="getAllPersonsRequest">

            <wsdlsoap:body use="literal"/>

         </wsdl:input>

         <wsdl:output name="getAllPersonsResponse">

            <wsdlsoap:body use="literal"/>

         </wsdl:output>

      </wsdl:operation>

   </wsdl:binding>

   <wsdl:service name="PersonServiceImplService">

      <wsdl:port binding="impl:PersonServiceImplSoapBinding" name="PersonServiceImpl">

         <wsdlsoap:address location="https://localhost:8080/SOAPExample/services/PersonServiceImpl"/>

      </wsdl:port>

   </wsdl:service>

</wsdl:definitions>

אם תפתח את זה במצב עיצוב באקליפס, זה יראה כמו בתמונה למטה. ניתן גם לגשת לקובץ WSDL של שירות האינטרנט דרך הדפדפן על ידי הוספת ?wsdl לנקודת סיום של שירות האינטרנט. תיארו כי web.xml הוא מותאם כך שישמש את Apache Axis כבקר קדמי עבור שירות האינטרנט.

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance" xmlns="https://xmlns.jcp.org/xml/ns/javaee" xsi:schemaLocation="https://xmlns.jcp.org/xml/ns/javaee https://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" id="WebApp_ID" version="3.1">
  <display-name>SOAPExample</display-name>
  <welcome-file-list>
    <welcome-file>index.html</welcome-file>
    <welcome-file>index.htm</welcome-file>
    <welcome-file>index.jsp</welcome-file>
    <welcome-file>default.html</welcome-file>
    <welcome-file>default.htm</welcome-file>
    <welcome-file>default.jsp</welcome-file>
  </welcome-file-list>
  <servlet>
    <display-name>Apache-Axis Servlet</display-name>
    <servlet-name>AxisServlet</servlet-name>
    <servlet-class>org.apache.axis.transport.http.AxisServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>AxisServlet</servlet-name>
    <url-pattern>/servlet/AxisServlet</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>AxisServlet</servlet-name>
    <url-pattern>*.jws</url-pattern>
  </servlet-mapping>
  <servlet-mapping>
    <servlet-name>AxisServlet</servlet-name>
    <url-pattern>/services/*</url-pattern>
  </servlet-mapping>
  <servlet>
    <display-name>Axis Admin Servlet</display-name>
    <servlet-name>AdminServlet</servlet-name>
    <servlet-class>org.apache.axis.transport.http.AdminServlet</servlet-class>
    <load-on-startup>100</load-on-startup>
  </servlet>
  <servlet-mapping>
    <servlet-name>AdminServlet</servlet-name>
    <url-pattern>/servlet/AdminServlet</url-pattern>
  </servlet-mapping>
</web-app>

בתמונה למטה מוצג שירות האינטרנט ופרויקט לקוח עם כל המזנון האוטומטי ועמודי JSP לבדיקת שירות האינטרנט. זהו כל הכלים של שירותי SOAP ב-Java בדוגמה באקליפס, כפי שתוכלו לראות שכל החלקים המורכבים עושים על ידי אקליפס באופן אוטומטי וכל המוקד שלנו היה לכתוב לוגיקת עסקים עבור שירות האינטרנט שלנו.

Source:
https://www.digitalocean.com/community/tutorials/soap-webservices-in-java-example-eclipse