A web services client typically begins with WSDL, and from there creates the service endpoint and client service interfaces. Then, in order to deploy the service in a J2EE server, a JAX-RPC mapping file is created. With that input, the service can be added to JNDI for an EJB or a web application.
Once again, Geronimo does not require client "stubs" or implementation code; the classes necessary to hook the interfaces up to the remote service will be generated under the covers.
The configuration process begins in the web.xml for a web application, or ejb-jar.xml for an EJB. In either case, a service-ref element is added for each web service. The XML is the same for both web applications and EJBs, the only difference is that a service reference is added at the top level of a web application, but scoped to a single EJB in an EJB JAR.
A service-ref typically looks like this:
<web-app xmlns:ger="geronimo-book:reviews" ... <service-ref> <service-ref-name>service/ProductReviews</service-ref-name> <service-interface> reviews.client.ProductReviewsService </service-interface> <wsdl-file>WEB-INF/ProductReviews.wsdl</wsdl-file> <jaxrpc-mapping-file> WEB-INF/ProductReviewsMapping.xml </jaxrpc-mapping-file> <service-qname>ger:ProductReviewsService</service-qname> </service-ref>
The elements here are:
The service will be located in JNDI at java:comp/env/ plus the value of the service ref name (here, java:comp/env/service/ProductReviews)
The fully-qualified class name of the client service interface (not the service endpoint interface)
The location of the WSDL file, relative to the root of the WAR or EJB JAR
The location of the JAX-RPC mapping file, relative to the root of the WAR or EJB JAR
If there is more than one service element in the WSDL file, this is used to select the one that this reference points to. It must include a namespace prefix that refers to the correct namespace for the WSDL content, which means an xmlns attribute must be added for that namespace in one of the enclosing elements, associating a prefix with that namespace, and then the prefix must be used in the value here.
The standard service-ref element is sufficient to contact the remote web service under normal circumstances. However, there are some cases where it's necessary to add additional settings to the Geronimo deployment plan for the service reference:
The URL in the WSDL to contact the service is not correct
The service in the WSDL includes multiple ports, and the same service-interface is used for more than one of them in the JAX-RPC mapping file, so it's not clear from the service-ref which port is targeted.
The WSDL is incomplete, and does not have a service element at all (but does have at least one binding).
The service requires authentication, but a user's Geronimo login will not be correct for the service.
The Geronimo deployment plan can select a port and/or binding, provide a URL to the service, and identify additional credentials configured for a Geronimo user that should be used to authenticate to the service. The specific syntax used to complete a service-ref in the Geronimo plan is described in more detail in Section 220.127.116.11, “Web Service References” and Section 18.104.22.168, “Web Service References”.
For example, a block used to override the URI specified in the WSDL for the service-ref above might look like this:
<service-ref> <service-ref-name>service/ProductReviews</service-ref-name> <port> <port-name>ProductReviews</port-name> <protocol>http</protocol> <host>server.domain.com</host> <port>80</port> <uri>/new/path/to/ProductReviews</uri> </port> </service-ref>
The Geronimo web service client infrastructure is capable of authenticating to a remote web service that requires it. However, Geronimo does not save a user's password by default, so it does not have enough information to pass a username and password to the remote service. For this to work, the calling application must use a security realm specially configured to save the user's username and password, and then the Geronimo deployment plan for the service-ref must describe how to find those credentials.
To store the username and password, an additional login module must be added to the Geronimo security realm. (For more details on configuring security realms and login modules, see Chapter 9, Security Configuration [DRAFT (1.0-pre)].) The settings for this login module are:
Table 16.1. Web Service: Login Module to Save Username & Password
|Login Module Class:||org.apache.geronimo.security.jaas.NamedUPCredentialLoginModule|
|Configuration Options:||org.apache.geronimo.jaas.NamedUPCredentialLoginModule.Name (set the value for this option to a name to store the credentials under, and the web service will use the same name to pick the credentials it wants)|
Currently this module cannot be added through the security realm wizard in the Console, though you can create a new security realm, select Other as the type, and then manually enter in the settings for both the main login module and this one. Alternately, you can write a security realm deployment plan like the one in Example 16.5, “Security Realm Plan for a Web Service Client” and deploy that using the normal command-line deployment tool.
Example 16.5. Security Realm Plan for a Web Service Client
This plan uses the standard Geronimo properties login module, and adds the login module described above in addition.
<configuration configId="MyRealm" xmlns="http://geronimo.apache.org/xml/ns/deployment-1.0"> <gbean name="MyRealm" class="org.apache.geronimo.security.realm.GenericSecurityRealm"> <attribute name="realmName">MyRealm</attribute> <reference name="ServerInfo"> <gbean-name>geronimo.server:J2EEApplication=null, J2EEModule=geronimo/j2ee-system/1.0/car, J2EEServer=geronimo,j2eeType=GBean,name=ServerInfo </gbean-name> </reference> <reference name="LoginService"> <gbean-name>geronimo.server:J2EEApplication=null, J2EEModule=geronimo/j2ee-security/1.0/car, J2EEServer=geronimo,j2eeType=JaasLoginService, name=JaasLoginService </gbean-name> </reference> <xml-reference name="LoginModuleConfiguration"> <log:login-config xmlns:log="http://geronimo.apache.org/xml/ns/loginconfig-1.0"> <log:login-module control-flag="REQUIRED" server-side="true" wrap-principals="false"> <log:login-domain-name> MainDomain </log:login-domain-name> <log:login-module-class> org.apache.geronimo.security.realm.providers. PropertiesFileLoginModule </log:login-module-class> <log:option name="usersURI"> var/security/users.properties </log:option> <log:option name="groupsURI"> var/security/groups.properties </log:option> </log:login-module> <log:login-module control-flag="REQUIRED" server-side="true" wrap-principals="false"> <log:login-domain-name> NamedDomain </log:login-domain-name> <log:login-module-class> org.apache.geronimo.security.jaas.NamedUPCredentialLoginModule </log:login-module-class> <log:option name="org.apache.geronimo.jaas.NamedUPCredentialLoginModule.Name" > WebServices </log:option> </log:login-module> </log:login-config> </xml-reference> </gbean> </configuration>
Note that the GBean names and class names in this example were split across multiple lines to fit the page, and will need to be combined again to deploy properly.
The Geronimo deployment plan for the Web or EJB module must include a block that adds the security settings to the service-ref. However, in the current schema, at least the uri is required, so a service-ref block in the Geronimo deployment plan might look like this:
<service-ref> <service-ref-name>service/ProductReviews</service-ref-name> <port> <uri>/path/to/ProductReviews</uri> <credentials-name>WebServices</credentials-name> </port> </service-ref>
Note that the credentials-name specified here must match the login module option org.apache.geronimo.jaas.NamedUPCredentialLoginModule.Name for the new login module in the security realm, and of course that security realm must be configured for the user logging in to the application so their credentials are saved and can be reused when invoking the web service.
In order for a web service client to work, the WAR or EJB JAR must contain:
The client service interface, packaged with the rest of the web or EJB classes
The service endpoint interface, packaged with the rest of the web or EJB classes
The WSDL file, in the WAR or EJB JAR at the location specified in the service-ref
The JAX-RPC mapping file, in the WAR or EJB JAR at the location specified in the service-ref
Beyond that, there are no particular packaging or deployment issues -- the WAR or EJB JAR can be deployed like normal, either standalone or as part of an EAR.
A J2EE component can look up the client service interface in JNDI (at the location configured in the service-ref), use that to get an instance of the service endpoint interface, and use that to communicate with the service. For example, based on the configuration above:
Context ctx = new InitialContext(); Object obj = ctx.lookup("java:comp/env/service/ProductReviews"); ProductReviewsService service = (ProductReviewsService) obj; ProductReviews port = service.getProductReviews(); String reviewers = port.getReviewers(productId); ...