ไคลเอนต์ JAX-WS: เส้นทางที่ถูกต้องในการเข้าถึง WSDL ในเครื่องคืออะไร?


86

ปัญหาคือฉันต้องสร้างไคลเอนต์บริการเว็บจากไฟล์ที่ได้รับมา ฉันเก็บไฟล์นี้ไว้ในระบบไฟล์ในเครื่องและในขณะที่ฉันเก็บไฟล์ WSDL ไว้ในโฟลเดอร์ระบบไฟล์ที่ถูกต้องทุกอย่างก็เรียบร้อยดี เมื่อฉันปรับใช้กับเซิร์ฟเวอร์หรือลบ WSDL ออกจากโฟลเดอร์ระบบไฟล์พร็อกซีไม่พบ WSDL และทำให้เกิดข้อผิดพลาด ฉันค้นหาเว็บและฉันพบโพสต์ต่อไปนี้ แต่ฉันไม่สามารถใช้งานได้:
JAX-WS กำลังโหลด WSDL จาก jar
http://www.java.net/forum/topic/glassfish/metro -and-jaxb / client-jar-cant-find-local-wsdl-0
http://blog.vinodsingh.com/2008/12/locally-packaged-wsdl.html

ฉันใช้ NetBeans 6.1 (นี่เป็นแอปพลิเคชันดั้งเดิมที่ฉันอัปเดตด้วยไคลเอนต์บริการเว็บใหม่นี้) ด้านล่างนี้คือคลาสพร็อกซี JAX-WS:

    @WebServiceClient(name = "SOAService", targetNamespace = "http://soaservice.eci.ibm.com/", wsdlLocation = "file:/C:/local/path/to/wsdl/SOAService.wsdl")
public class SOAService
    extends Service
{

    private final static URL SOASERVICE_WSDL_LOCATION;
    private final static Logger logger = Logger.getLogger(com.ibm.eci.soaservice.SOAService.class.getName());

    static {
        URL url = null;
        try {
            URL baseUrl;
            baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
            url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl");
        } catch (MalformedURLException e) {
            logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file");
            logger.warning(e.getMessage());
        }
        SOASERVICE_WSDL_LOCATION = url;
    }

    public SOAService(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    public SOAService() {
        super(SOASERVICE_WSDL_LOCATION, new QName("http://soaservice.eci.ibm.com/", "SOAService"));
    }

    /**
     * @return
     *     returns SOAServiceSoap
     */
    @WebEndpoint(name = "SOAServiceSOAP")
    public SOAServiceSoap getSOAServiceSOAP() {
        return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class);
    }

    /**
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns SOAServiceSoap
     */
    @WebEndpoint(name = "SOAServiceSOAP")
    public SOAServiceSoap getSOAServiceSOAP(WebServiceFeature... features) {
        return super.getPort(new QName("http://soaservice.eci.ibm.com/", "SOAServiceSOAP"), SOAServiceSoap.class, features);
    }

}


นี่คือรหัสของฉันในการใช้พร็อกซี:

   WebServiceClient annotation = SOAService.class.getAnnotation(WebServiceClient.class);
   // trying to replicate proxy settings
   URL baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource("");//note : proxy uses "."
   URL url = new URL(baseUrl, "/WEB-INF/wsdl/client/SOAService.wsdl");
   //URL wsdlUrl = this.getClass().getResource("/META-INF/wsdl/SOAService.wsdl"); 
   SOAService serviceObj = new SOAService(url, new QName(annotation.targetNamespace(), annotation.name()));
   proxy = serviceObj.getSOAServiceSOAP();
   /* baseUrl;

   //classes\com\ibm\eci\soaservice
   //URL url = new URL(baseUrl, "../../../../wsdl/SOAService.wsdl");

   proxy = new SOAService().getSOAServiceSOAP();*/
   //updating service endpoint 
   Map<String, Object> ctxt = ((BindingProvider)proxy ).getRequestContext();
   ctxt.put(JAXWSProperties.HTTP_CLIENT_STREAMING_CHUNK_SIZE, 8192);
   ctxt.put(BindingProvider.ENDPOINT_ADDRESS_PROPERTY, WebServiceUrl);

NetBeans ใส่สำเนาของ WSDL ในweb-inf / wsdl / client / SOAServiceดังนั้นฉันไม่ต้องการเพิ่มลงในMETA-INFด้วย คลาสเซอร์วิสอยู่ในWEB-INF / คลาส / com / ibm / eci / soaservice /และตัวแปร baseurl มีพา ธ แบบเต็มของระบบไฟล์ไปยังมัน (c: \ path \ to \ the \ project ... \ soaservice) รหัสด้านบนทำให้เกิดข้อผิดพลาด:

javax.xml.ws.WebServiceException: ไม่สามารถเข้าถึง WSDL ที่: file: /WEB-INF/wsdl/client/SOAService.wsdl ล้มเหลวด้วย: \ WEB-INF \ wsdl \ client \ SOAService.wsdl (ไม่พบเส้นทาง)

ก่อนอื่นฉันจะอัปเดต wsdllocation ของคลาสพร็อกซีหรือไม่ แล้วฉันจะบอกคลาส SOAService ใน WEB-INF / คลาส / com / ibm / eci / soaservice เพื่อค้นหา WSDL ใน \ WEB-INF \ wsdl \ client \ SOAService.wsdl ได้อย่างไร

แก้ไข : ฉันพบลิงค์อื่น - http://jianmingli.com/wp/?cat=41ซึ่งบอกว่าให้ใส่ WSDL ลงใน classpath ฉันละอายที่จะถาม: ฉันจะใส่ลงในคลาสพา ธ ของเว็บแอปพลิเคชันได้อย่างไร


คำถามที่คล้ายกัน: JAX-WS กำลังโหลด WSDL จาก jar
sleske

คำตอบ:


117

ตัวเลือกที่ดีที่สุดคือใช้ jax-ws-catalogue.xml

เมื่อคุณคอมไพล์ไฟล์ WSDL ในเครื่องให้แทนที่ตำแหน่ง WSDL และตั้งค่าเป็นไฟล์

http: //localhost/wsdl/SOAService.wsdl

ไม่ต้องกังวลว่านี่เป็นเพียง URI ไม่ใช่ URL ซึ่งหมายความว่าคุณไม่จำเป็นต้องมี WSDL ในที่อยู่นั้น
คุณสามารถทำได้โดยส่งผ่านอ็อพชัน wsdllocation ไปยัง wsdl ไปยังคอมไพเลอร์ java

เพื่อเปลี่ยนรหัสพร็อกซีจาก

static {
    URL url = null;
    try {
        URL baseUrl;
        baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
        url = new URL(baseUrl, "file:/C:/local/path/to/wsdl/SOAService.wsdl");
    } catch (MalformedURLException e) {
        logger.warning("Failed to create URL for the wsdl Location: 'file:/C:/local/path/to/wsdl/SOAService.wsdl', retrying as a local file");
        logger.warning(e.getMessage());
    }
    SOASERVICE_WSDL_LOCATION = url;
}

ถึง

static {
    URL url = null;
    try {
        URL baseUrl;
        baseUrl = com.ibm.eci.soaservice.SOAService.class.getResource(".");
        url = new URL(baseUrl, "http://localhost/wsdl/SOAService.wsdl");
    } catch (MalformedURLException e) {
        logger.warning("Failed to create URL for the wsdl Location: 'http://localhost/wsdl/SOAService.wsdl', retrying as a local file");
        logger.warning(e.getMessage());
    }
    SOASERVICE_WSDL_LOCATION = url;
}

ไฟล์ประกาศ: // เปลี่ยนเป็น http: // ในตัวสร้าง URL

ตอนนี้มาใน jax-ws-catalogue.xml หากไม่มี jax-ws-catalog.xml jax-ws จะพยายามโหลด WSDL จากตำแหน่ง

http: //localhost/wsdl/SOAService.wsdl
และล้มเหลวเนื่องจากจะไม่มี WSDL ดังกล่าว

แต่ด้วย jax-ws-catalogue.xml คุณสามารถเปลี่ยนเส้นทาง jax-ws ไปยัง WSDL ที่บรรจุในเครื่องเมื่อใดก็ตามที่พยายามเข้าถึง WSDL @

http: //localhost/wsdl/SOAService.wsdl
.

นี่คือ jax-ws-catalogue.xml

<catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system">
        <system systemId="http://localhost/wsdl/SOAService.wsdl"
                uri="wsdl/SOAService.wsdl"/>
    </catalog>

สิ่งที่คุณกำลังทำคือบอก jax-ws ว่าเมื่อใดที่จำเป็นต้องโหลด WSDL จาก

http: //localhost/wsdl/SOAService.wsdl
ควรโหลดจากโลคัลพา ธ wsdl / SOAService.wsdl

ตอนนี้คุณควรวาง wsdl / SOAService.wsdl และ jax-ws-catalog.xml ไว้ที่ไหน นั่นคือคำถามล้านดอลลาร์ไม่ใช่เหรอ?
ควรอยู่ในไดเรกทอรี META-INF ของโถแอปพลิเคชันของคุณ

อะไรทำนองนี้

ABCD.jar  
| __ META-INF    
    | __ jax-ws-catalogue.xml  
    | __ wsdl  
        | __ SOAService.wsdl  

ด้วยวิธีนี้คุณไม่จำเป็นต้องแทนที่ URL ในไคลเอนต์ของคุณที่เข้าถึงพร็อกซี WSDL ถูกดึงขึ้นมาจากภายใน JAR ของคุณและคุณหลีกเลี่ยงที่จะต้องมีพา ธ ระบบไฟล์แบบฮาร์ดโค้ดในโค้ดของคุณ

ข้อมูลเพิ่มเติมเกี่ยวกับ jax-ws-catalog.xml http://jax-ws.java.net/nonav/2.1.2m1/docs/catalog-support.html

หวังว่าจะช่วยได้


ตกลงฉันไม่สามารถแก้ปัญหาด้วยวิธีนี้ภายในเว็บแอปพลิเคชัน: ฉันพยายามใส่ wsdl ไว้ใน web-inf โดยไม่ประสบความสำเร็จอาจเป็นเพราะฉันขาดความรู้ อย่างไรก็ตามมันใช้งานได้กับขวดดังนั้นฉันจะสร้างห้องสมุดกระดาษห่อหุ้มเหมือนที่ควรทำตั้งแต่เริ่มต้น ขอบคุณสำหรับการสนับสนุน

ฉันสามารถใช้คำตอบนี้ได้สำเร็จและฉันเชื่อว่านี่เป็นทางออกที่ดีกว่าทางเลือกอื่น ๆ ทั้งหมดที่มีในบทความและบทช่วยสอนอื่น ๆ ดังนั้นสำหรับฉันนี่คือแนวทางปฏิบัติที่ดีที่สุด ฉันแค่สงสัยว่าทำไมโซลูชันนี้จึงไม่ได้รับการบันทึกไว้ในบทความทางการและแบบฝึกหัดอื่น ๆ ที่ครอบคลุมหัวข้อ JAX-WS
Rahul Khimasia

19

อีกวิธีหนึ่งที่เราประสบความสำเร็จคือการสร้างรหัสพร็อกซีไคลเอ็นต์ WS โดยใช้ wsimport (จาก Ant เป็นงาน Ant) และระบุแอตทริบิวต์ wsdlLocation

<wsimport debug="true" keep="true" verbose="false" target="2.1" sourcedestdir="${generated.client}" wsdl="${src}${wsdl.file}" wsdlLocation="${wsdl.file}">
</wsimport>

เนื่องจากเรารันสิ่งนี้สำหรับโปรเจ็กต์ที่มี WSDL หลายตัวสคริปต์จะแก้ไขค่า $ (wsdl.file} แบบไดนามิกซึ่งตั้งค่าเป็น /META-INF/wsdl/YourWebServiceName.wsdl ที่สัมพันธ์กับตำแหน่ง JavaSource (หรือ / src, ขึ้นอยู่กับวิธีการตั้งค่าโครงการของคุณ) ในระหว่างขั้นตอนการสร้างไฟล์ WSDL และ XSDs จะถูกคัดลอกไปยังตำแหน่งนี้และบรรจุในไฟล์ JAR (คล้ายกับโซลูชันที่อธิบายโดย Bhasakar ด้านบน)

MyApp.jar
|__META-INF
   |__wsdl
      |__YourWebServiceName.wsdl
      |__YourWebServiceName_schema1.xsd
      |__YourWebServiceName_schmea2.xsd

หมายเหตุ: ตรวจสอบให้แน่ใจว่าไฟล์ WSDL ใช้การอ้างอิงสัมพัทธ์กับ XSD ที่นำเข้าและไม่ใช่ http URLs:

  <types>
    <xsd:schema>
      <xsd:import namespace="http://valueobject.common.services.xyz.com/" schemaLocation="YourWebService_schema1.xsd"/>
    </xsd:schema>
    <xsd:schema>
      <xsd:import namespace="http://exceptions.util.xyz.com/" schemaLocation="YourWebService_schema2.xsd"/>
    </xsd:schema>
  </types>

ในโค้ดที่สร้างขึ้นเราพบสิ่งนี้:

/**
 * This class was generated by the JAX-WS RI.
 * JAX-WS RI 2.2-b05-
 * Generated source version: 2.1
 * 
 */
@WebServiceClient(name = "YourService", targetNamespace = "http://test.webservice.services.xyz.com/", wsdlLocation = "/META-INF/wsdl/YourService.wsdl")
public class YourService_Service
    extends Service
{

    private final static URL YOURWEBSERVICE_WSDL_LOCATION;
    private final static WebServiceException YOURWEBSERVICE_EXCEPTION;
    private final static QName YOURWEBSERVICE_QNAME = new QName("http://test.webservice.services.xyz.com/", "YourService");

    static {
        YOURWEBSERVICE_WSDL_LOCATION = com.xyz.services.webservice.test.YourService_Service.class.getResource("/META-INF/wsdl/YourService.wsdl");
        WebServiceException e = null;
        if (YOURWEBSERVICE_WSDL_LOCATION == null) {
            e = new WebServiceException("Cannot find '/META-INF/wsdl/YourService.wsdl' wsdl. Place the resource correctly in the classpath.");
        }
        YOURWEBSERVICE_EXCEPTION = e;
    }

    public YourService_Service() {
        super(__getWsdlLocation(), YOURWEBSERVICE_QNAME);
    }

    public YourService_Service(URL wsdlLocation, QName serviceName) {
        super(wsdlLocation, serviceName);
    }

    /**
     * 
     * @return
     *     returns YourService
     */
    @WebEndpoint(name = "YourServicePort")
    public YourService getYourServicePort() {
        return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class);
    }

    /**
     * 
     * @param features
     *     A list of {@link javax.xml.ws.WebServiceFeature} to configure on the proxy.  Supported features not in the <code>features</code> parameter will have their default values.
     * @return
     *     returns YourService
     */
    @WebEndpoint(name = "YourServicePort")
    public YourService getYourServicePort(WebServiceFeature... features) {
        return super.getPort(new QName("http://test.webservice.services.xyz.com/", "YourServicePort"), YourService.class, features);
    }

    private static URL __getWsdlLocation() {
        if (YOURWEBSERVICE_EXCEPTION!= null) {
            throw YOURWEBSERVICE_EXCEPTION;
        }
        return YOURWEBSERVICE_WSDL_LOCATION;
    }

}

บางทีนี่อาจช่วยได้เช่นกัน เป็นเพียงแนวทางอื่นที่ไม่ใช้แนวทาง "แค็ตตาล็อก"


ฉันชอบแนวทางนี้ ... แต่ทำไมต้องเป็นไดเรกทอรี META-INF
IcedDante

1
โปรดทราบว่าต้องใช้ JAX-WS RI 2.2 ไม่ใช่ 2.1 ที่มาพร้อมกับ JDK 6 โดยค่าเริ่มต้น
ᄂ ᄀ

4

ขอบคุณมากสำหรับคำตอบของ Bhaskar Karambelkar ซึ่งอธิบายรายละเอียดและแก้ไขปัญหาของฉัน แต่ฉันก็อยากจะพูดคำตอบใหม่ในสามขั้นตอนง่ายๆสำหรับคนที่รีบแก้ไข

  1. ทำให้การอ้างอิงตำแหน่งท้องถิ่น wsdl ของคุณเป็น wsdlLocation= "http://localhost/wsdl/yourwsdlname.wsdl"
  2. สร้างโฟลเดอร์ META-INF ที่อยู่ใต้ src วางไฟล์ wsdl ของคุณในโฟลเดอร์ภายใต้ META-INF พูดว่า META-INF / wsdl
  3. สร้างไฟล์ xml jax-ws-catalogue.xml ภายใต้ META-INF ดังต่อไปนี้

    <catalog xmlns="urn:oasis:names:tc:entity:xmlns:xml:catalog" prefer="system"> <system systemId="http://localhost/wsdl/yourwsdlname.wsdl" uri="wsdl/yourwsdlname.wsdl" /> </catalog>

ตอนนี้บรรจุขวดของคุณ ไม่มีการอ้างอิงไปยังไดเร็กทอรีภายในอีกต่อไป แต่ทั้งหมดนี้รวมอยู่ในแพ็กเกจและอ้างอิงภายใน


4

สำหรับผู้ที่ยังคงมาหาวิธีแก้ปัญหาที่นี่วิธีที่ง่ายที่สุดคือการใช้<wsdlLocation>โดยไม่ต้องเปลี่ยนรหัสใด ๆ ขั้นตอนการทำงานมีดังต่อไปนี้:

  1. ใส่ wsdl ของคุณในไดเรกทอรีทรัพยากรเช่น: src/main/resource
  2. ในไฟล์ pom ให้เพิ่มทั้ง wsdlDirectory และ wsdlLocation (อย่าพลาด / ที่จุดเริ่มต้นของ wsdlLocation) เช่นด้านล่าง ในขณะที่ใช้ wsdlDirectory เพื่อสร้างโค้ดและใช้ wsdlLocation ที่รันไทม์เพื่อสร้างพร็อกซีแบบไดนามิก

    <wsdlDirectory>src/main/resources/mydir</wsdlDirectory>
    <wsdlLocation>/mydir/my.wsdl</wsdlLocation>
    
  3. จากนั้นในรหัส java ของคุณ (โดยไม่มีตัวสร้างอาร์กิวเมนต์):

    MyPort myPort = new MyPortService().getMyPort();
    
  4. เพื่อความสมบูรณ์ฉันกำลังให้ส่วนการสร้างโค้ดแบบเต็มที่นี่พร้อมด้วย API ที่คล่องแคล่วในโค้ดที่สร้างขึ้น

    <plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>jaxws-maven-plugin</artifactId>
    <version>2.5</version>
    
    <dependencies>
        <dependency>
            <groupId>org.jvnet.jaxb2_commons</groupId>
            <artifactId>jaxb2-fluent-api</artifactId>
            <version>3.0</version>
        </dependency>
        <dependency>
            <groupId>com.sun.xml.ws</groupId>
            <artifactId>jaxws-tools</artifactId>
            <version>2.3.0</version>
        </dependency>
    </dependencies>
    
    <executions>
        <execution>
            <id>wsdl-to-java-generator</id>
            <goals>
                <goal>wsimport</goal>
            </goals>
            <configuration>
                <xjcArgs>
                    <xjcArg>-Xfluent-api</xjcArg>
                </xjcArgs>
                <keep>true</keep>
                <wsdlDirectory>src/main/resources/package</wsdlDirectory>
                <wsdlLocation>/package/my.wsdl</wsdlLocation>
                <sourceDestDir>${project.build.directory}/generated-sources/annotations/jaxb</sourceDestDir>
                <packageName>full.package.here</packageName>
            </configuration>
        </execution>
    </executions>
    


2

สำหรับผู้ที่ใช้ Spring คุณสามารถอ้างอิงทรัพยากร classpath ใดก็ได้โดยใช้ classpath-protocol ดังนั้นในกรณีของ wsdlLocation สิ่งนี้จะกลายเป็น:

<wsdlLocation>classpath:META-INF/webservice.wsdl</wsdlLocation>

โปรดทราบว่าไม่ใช่พฤติกรรม Java มาตรฐาน ดูเพิ่มเติม: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/resources.html


0

มีแน่นอนปัญหาเดียวกันที่อธิบายไว้ในเอกสารฉบับนี้ ไม่ว่าฉันจะทำอะไรตามตัวอย่างข้างต้นเพื่อเปลี่ยนตำแหน่งของไฟล์ WSDL ของฉัน (ในกรณีของเราจากเว็บเซิร์ฟเวอร์) มันยังคงอ้างถึงตำแหน่งเดิมที่ฝังอยู่ในโครงสร้างต้นทางของกระบวนการเซิร์ฟเวอร์

หลังจากหลายชั่วโมงในการพยายามแก้จุดบกพร่องนี้ฉันสังเกตเห็นว่า Exception มักจะถูกโยนออกจากบรรทัดเดียวกันเสมอ (ในกรณีของฉัน 41) ในที่สุดเช้าวันนี้ฉันตัดสินใจเพียงแค่ส่งซอร์สโค้ดไคลเอนต์ไปยังคู่ค้าของเราเพื่ออย่างน้อยพวกเขาก็สามารถเข้าใจว่าโค้ดมีลักษณะอย่างไร แต่อาจจะสร้างขึ้นเอง ด้วยความตกใจและสยองขวัญของฉันฉันพบไฟล์คลาสจำนวนมากผสมกับไฟล์. java ของฉันภายในแผนผังซอร์สไคลเอ็นต์ พิลึก !! ฉันสงสัยว่าสิ่งเหล่านี้เป็นผลพลอยได้จากเครื่องมือสร้างไคลเอ็นต์ JAX-WS

เมื่อฉันใส่ไฟล์. class โง่ ๆ เหล่านั้นและทำการล้างข้อมูลและสร้างโค้ดไคลเอนต์ใหม่ทุกอย่างก็ทำงานได้อย่างสมบูรณ์ !! ซ้ำซาก !!

YMMV แอนดรูว์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.