|
In This Issue
Welcome to the Enterprise Java Technologies Tech Tips for July 29, 2006. Here you'll get tips on using enterprise Java
technologies and APIs, such as those in Java Platform,
Enterprise Edition (Java EE).
This issue covers:
These tips were developed using an open source reference
implementation of Java EE 5 called GlassFish. You can
download GlassFish from the GlassFish Community Downloads page.
You can download the sample archive for the tip Writing
a Handler in JAX-WS.
You can download the sample archive for the tip Using Multiple Databases in a Java Persistence Application.
Any use of this code and/or information below is subject to the
license terms.
See the Subscribe/Unsubscribe note at the end of this newsletter
to subscribe to Tech Tips that focus on technologies and products
in other Java platforms.
Document Handling Using JAX-WS Dispatch and Provider APIs
The JAX-WS 2.0 specification provides two new APIs which make it possible for web services to operate at the XML message level. In working with web services and XML, programmers use an implementation of the Service Endpoint Interface (SEI) to provide a Java level abstraction that hides the details of converting between Java methods and corresponding XML. However, sometimes developers want to work directly with XML rather than with Java abstractions. The new APIs provide an alternative to the SEI approach. This Tech Tip shows the APIs in use in a sample application.
The two new APIs are javax.xml.ws.Provider and
java.xml.ws.Dispatch. Both are generic APIs. Provider is
a server-side API, while Dispatch is a client-side API. These
APIs require services to work with messages or message payloads.
Because of that, the APIs need to know specifics about the
message's structure or the payload structure. The generic nature
of the APIs allows them to be used with a variety of message
object types. The JAX-WS specification requires that
implementations support at least javax.xml.transform.Source,
javax.xml.soap.SOAPMessage, javax.activation.DataSource, and
JAXB objects. The sample application in this tip uses the
javax.xml.transform.Source and javax.xml.soap.SOAPMessage
objects in distinct implementations.
The sample application: using Dispatch and Provider
The sample application for this tip is an update to the sample
application from the May 27, 2007 tip Document Handling in Web
Services Applications.
The sample application in the previous tip emulated credit card
authorization and used an SEI to implement the web service. The
sample application for this tip provides the same service using
the Provider interface to implement the web service. The client
uses a SOAPMessage object to send messages, and a Source object
to send message payloads. The sample uses synchronous callback
service invocation mode. In synchronous callback mode, the
service's invoke method blocks further processing until the
remote operations complete and return a result. The sample
application also uses JAXB on both the client and server.
Setting up your environment
The sample for this tip uses an open source reference
implementation of Java EE 5 called GlassFish. If you haven't
already done so, download GlassFish from the GlassFish Community Downloads page. To build and
run the sample, you also need JDK 5.0, which you can download
from the J2SE 5.0 downloads page, and Apache
Ant 1.6.5, which is in the GlassFish bundle (in Windows, it's in
the lib\ant subdirectory).
Installing the sample application
Download the sample package
and unzip its contents. The root directory for the sample is
techtip. Change the current directory to the techtip directory.
Edit the script env.sh (for UNIX) or env.bat (for Windows) to
reflect your build environment. For example, change the value of
the JAVA_HOME environment variable in the script to the location
of JDK 5.0 on your system. Then execute the script to set up
your environment.
Building the web service
The sample builds a web service using a WSDL file. The steps
involved in doing this are:
- Create a WSDL file based on XML schemas.
- Write an endpoint implementation class.
- Generate portable artifacts for web service execution.
- Compile the web service, package it as a WAR file, and deploy
it.
Let's look at these steps and files a little closer.
Create a WSDL file based on XML schemas
A WSDL file is packaged with the sample. You can find the WSDL
file, CreditCardService.wsdl, in the conf directory. The WSDL
file exposes web service operations that authorize credit card
payments. For more information about WSDL, see
Web Services Description Language (WSDL) 1.1.
Schema files
The two schema files that the WSDL file imports are
CreditCardAuthorization.xsd and CreditCardServiceException.xsd.
CreditCardAuthorization.xsd defines the structure of the XML
document that a client can send to the web service. The elements
in the schema that are pertinent to credit card authorization
requests are:
<element name="AuthorizationRequest">
<complexType>
<sequence>
<element name="CreditCard"
type="tns:CreditCard" nillable="true"/>
<element name="CardUser"
type="tns:CardUser" nillable="true"/>
</sequence>
</complexType>
</element>
Similar elements are defined and used for returning
authorization status.
CreditCardServiceException.xsd defines the structure of an XML
document for returning an error message, that is, when the web
service cannot process the request.
Generate portable artifacts
You can find the ant tasks for the sample application in the
build.xml file in the techtip directory. The sample uses the
wsimport tool and the packaged WSDL to generate portable
artifacts such as the service class, JAXB classes, and exception
classes. In the sample this is done in an ant task that has the
target name generate-server. The ant task uses an external
binding file, config-server.xml, which you can find in the conf
directory.
After the artifacts are generated, they're compiled using the
javac compiler. In the sample, this is done using an ant task
with the target name compile-server.
Write an endpoint implementation class
The sample provides two endpoint implementation classes,
CreditCardServiceUsingPAYLOAD.java and
CreditCardServiceUsingMESSAGE.java. Only one can be deployed as a
service at a time. Which one gets deployed depends on an
environment variable for the service mode. The environment
variable is set by the script you executed earlier. For example,
the env.sh script includes the following setting of the
environment variable for the service mode:
# possible values for service_mode are MESSAGE or PAYLOAD
service_mode=PAYLOAD; export service_mode.
The CreditCardServiceUsingPAYLOAD.java class implements the
Provider interface for payload-oriented synchronous callback
mode. The CreditCardServiceUsingMESSAGE.java class implements
the Provider interface for message-oriented synchronous callback
mode.
Here is the implementation using Payload:
@WebServiceProvider
@ServiceMode(value=Service.Mode.PAYLOAD)
public class CreditCardServiceUsingPAYLOAD
implements Provider<Source> {
public Source invoke(Source request) {
Source response = null;
try {
response = authorizePayment(request);
...
The JAX-WS specification requires the Provider implementation
class contain a @WebServiceProvider annotation. The @ServiceMode
annotation defines which callback mode to use. The code for
CreditCardServiceUsingPAYLOAD.java specifies
Service.Mode.PAYLOAD mode. This indicates that the Provider
implementation works with message payloads only.
The class implements the Provider<Source> interface. The type
Source can be used only with payload mode. For synchronous
callback, the implementation defines an invoke method. This
method is called by the client. For an interface of type
Provider<Source>, the input and output parameters of the invoke
method are Source objects. In the sample, the authorizePayment
method is a utility method used to authorize charges on the
credit card.
public Source authorizePayment(Source request)
throws Exception {
Source response = null;
try {
String requestXML = getXMLFromSource(request);
String responseXML = authorizePaymentSTAX(requestXML);
response = getSourceFromXML(responseXML);
...
The authorizePayment method converts the input Source to
a string. It uses StAX reader and writer APIs to read the input
XML and construct output XML, respectively. Recall that the
sample in the May 2006 tech tip used the method
authorizePaymentSTAX to process the XML document using StAX.
Now, let's look at the service implementation using Message:
@WebServiceProvider
@ServiceMode(value=Service.Mode.MESSAGE)
public class CreditCardServiceUsingMESSAGE
implements Provider<SOAPMessage> {
public SOAPMessage invoke(SOAPMessage request) {
SOAPMessage response = null;
try {
Source req = util.getSourceFromSOAPMessage(request);
Source res = authorizePayment(req);
...
As before, the class has WebServiceProvider and ServiceMode
annotations. The notable difference is that the mode is now
Service.Mode.MESSAGE. The Provider interface supports methods of
type SOAPMessage. The invoke method is a synchronous callback
method with input and output parameters for soap messages. The
utility method getSourceFromSOAPMessage returns a Source object.
The utility method authorizePayment is similar to the one
defined in the payload example.
The Provider implementation can also use JAXB with the payload
or message service modes. The details of using JAXB are beyond
the scope of this tip. The sample however does include code that
demonstrates the use of JAXB (more about that a little later in
the tip).
Compile the web service, package it as a WAR file, and deploy it
You need to compile the service implementation class as well as
the portable artifacts for the web service that were generated
earlier. Run wsimport on CreditCardService.wsdl to compile the
service implementation class and the generated artifacts. Next,
you need to package the classes into a WAR file along with the
deployment descriptor for web services, sun-jaxws.xml. The
packaging is performed in the sample through an ant task that
has the target build. The server performs the build based on
the service mode you set in the environment. To test the
different modes, change the service_mode environment variable in
the env.sh or env.bat script, and rebuild the server and client.
Building the client
Here are the steps to build the web service client:
- Write the client class.
- Generate portable artifacts for web service execution.
- Compile the client.
Write the client class
A standalone web service client, CreditCardServiceTest.java, is
packaged with the sample. You can find it in the src\client
directory. The client calls the credit card web service to
authorize payment on a credit card provided by the customer. The
client sends credit card and card holder information contained
in an XML document to the server.
Here's the method in CreditCardServiceTest that invokes the web
service:
public void testAuthorizePayment(String url,
String authrequestXMLfile, Service.Mode mode)
throws Exception {
try {
Service service = Service.create(
wsdlLoc, serviceName);
service.addPort(portName,
SOAPBinding.SOAP11HTTP_BINDING, url);
if(mode.equals(Service.Mode.PAYLOAD)) {
Dispatch<Source> disp = service.createDispatch(
portName, Source.class, mode);
...
Source response = disp.invoke(xmlSource);
...
The testAuthorizePayment method creates a service using the url
for the WSDL located on the server and the service QName
identifying the wsdl:service element. Next, the method adds a port
to be used with Dispatch. The added port does not contain
information about wsdl:port -- it can be used only with Dispatch
instances. The portName is QName for the new web service port,
and the url is the endpoint address for the web service. The
port uses SOAP1.1/HTTP binding.
If the service mode selected is PAYLOAD, the Dispatch instance
is created using an object of type Source. The Dispatch instance
invokes the service method using the Source object as input and
gets output of type Source.
If the service mode selected is MESSAGE, the Dispatch instance
is created using an object of type SOAPMessage:
if(mode.equals(Service.Mode.MESSAGE)) {
Dispatch<SOAPMessage> disp =
service.createDispatch(
portName, SOAPMessage.class, mode);
...
SOAPMessage response = disp.invoke(message);
...
For MESSAGE mode, the Dispatch instance calls the invoke method
using a SOAPMessage object, and gets a SOAPMessage object in
response.
As mentioned earlier, this tip does not describe how to use JAXB
with the payload or message service modes. However the following
lines of code in class CreditCardServiceTest.java use JAXB to
test the payment authorization:
JAXBContext jc =
JAXBContext.newInstance("creditcard");
Unmarshaller u = jc.createUnmarshaller();
AuthorizationRequest request =
(AuthorizationRequest)u.unmarshal(
new FileInputStream(authrequestXMLfile));
...
Dispatch<Object> disp =
service.createDispatch(portName, jc, mode);
AuthorizationStatus response =
(AuthorizationStatus)disp.invoke(request);
...
Generate portable artifacts and compile the client
Generate client-side portable artifacts using the WSDL on the
server. This is done in the sample using an ant task with the
target build. Actually, when you call ant target build, it
compiles both the server and client classes, including the
generated artifacts. It also builds a deployable WAR file. Note
that the client uses a JAXB binding configuration file,
config-client.xml. The client is built based on the service mode
you set in the environment. To test the different modes, change
the service_mode environment variable in the env.sh or env.bat
script, and rebuild the server and client.
Running the sample
After running the script to set up your environment, you can run
the sample as follows:
- Start GlassFish by entering the following command:
%J2EE_HOME%/bin/asadmin start-domain domain1
Ensure that the J2EE_HOME environment variable is set to the
GlassFish install directory.
- Build the web service, the client, and needed artifacts,
and package it into a WAR file by entering the following
command:
ant build
- Deploy the application by entering the following command:
ant deploy
- Run the application by entering the following command:
ant run
The command runs the application using all three XML handling
technologies. You should see output that looks something like
this:
run:
[java] Calling Credit Card Service with MESSAGE...
[java] Response from Credit Card Service:
[java] <?xml version="1.0" encoding="UTF-8"?>
<soapenv:Envelope xmlns:soapenv=
"http://schemas.xmlsoap.org/soap/envelope/">
<soapenv:Body><ns0:AuthorizationStatus xmlns:ns0=
"urn:CardService"><ns0:authorizationToken>49235677
</ns0:authorizationToken><ns0:authorized>true
</ns0:authorized><ns0:errorCode>0</ns0:errorCode>
</ns0:AuthorizationStatus></soapenv:Body>
</soapenv:Envelope>
- Undeploy the application by entering the following command:
ant undeploy
- Delete all classes and the WAR file generated for the
application by entering the following command:
ant clean
About the Author
Deep Singh is a staff member of the Java Performance Engineering
group at Sun Microsystems.
Using Multiple Databases in a Java Persistence Application
The June 24, 2006 Tech Tip Inheritance and the Java Persistence
API was the first Enterprise Java Technologies Tech Tip about the
new Java Persistence API. This tip is second in the series. Here
you'll examine a sample application that uses the Java
Persistence API to persist entities in multiple databases.
The sample application
A sample application
accompanies this tip. The application displays information about about orders. A user can
view basic information about an order:
The user can also display more details about an order:
To display this information, the application loads and displays
entities (that is, persistent objects) that map to two separate
databases. One entity maps to data in a database that contains
basic information about an order such as the order
identification and description. Another entity maps to
a database that contains further details about the order such
as the configuration description.
The application has four types of components:
- An HTML page:
index.html
- A JavaServer Page (JSP):
viewOrder.jsp
- A stateless session bean (
SessionStateless.java) and its local
interface (SessionStatelessLocal.java)
- Entities (
Order.java and Configuration.java)
The JSP uses the stateless session bean to access the entities.
For example, when a user selects an order in the drop down menu
of the index.html page and then clicks the View Order button,
it loads the viewOrder.jsp page. The JSP page then uses the
stateless session bean to access the Order entity. Here's
a snippet of code from viewOrder.jsp that accesses the Order
entity:
<%
Order order = null;
String orderID=request.getParameter("orderID");
String action = request.getParameter("action");
order = stateless.loadOrder(orderID);
%>
Notice that the viewOrder.jsp page calls the loadOrder method in
the stateless session bean. Here's what the loadOrder method
looks like:
@TransactionAttribute(TransactionAttributeType.REQUIRED)
public Order loadOrder(String id){
return em1.find(Order.class, id);
}
em1 in the loadOrder method is an entity manager. The session
bean uses entity managers to load entities from a database. In
the loadOrder method, em1 is used to load the Order entity from
a database. If the user requests additional details about an
order, the JSP page calls another method, loadConfiguration, in
the session bean. That method uses another entity manager to
load a Configuration entity from a second database. The
Configuration entity that is loaded is based on the configID
that's stored in the Order entity. The Order and Configuration
entities have the following attributes:
public class Order implements Serializable {
private String id;
private String description;
private String configID;
public class Configuration implements Serializable {
private String id;
private String description;
The configID attribute of the Order entity is the primary key of
the Configuration entity in a separate database.
Using Multiple Databases
If you look at the build.xml file in the setup directory of the
sample, you'll see an ant task with the target setup. That
task creates two Derby databases: sun-appserv-samples-db1 and
sun-appserv-samples-db2 (the database names are specified in
the build.properties file in the sample directory). The setup
task also creates two sets of transactional JDBC data sources
(jdbc/SamplesDB and jdbc/SamplesDB2) and JDBC connection pools
on the application server. One connection pool points to the
sun-appserv-samples-db1 database and the other points to the
sun-appserv-samples-db2 database.
The JDBC resources and JDBC connection pools need to map to
persistence units. The Java Persistence API allows you to define
multiple persistence units, each of which can map to a separate
database. If you look at the persistence.xml file in the
src\conf directory you'll see persistence units specified for
sample-db1 and sample-db2:
<persistence version="1.0">
<persistence-unit name="sample-db1">
<provider>
oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
</provider>
<jta-data-source>jdbc/SamplesDB</jta-data-source>
<non-jta-data-source>jdbc/SamplesDB__nontx
</non-jta-data-source>
</persistence-unit>
<persistence-unit name="sample-db2">
<provider>
oracle.toplink.essentials.ejb.cmp3.EntityManagerFactoryProvider
</provider>
<jta-data-source>jdbc/SamplesDB2</jta-data-source>
<non-jta-data-source>jdbc/SamplesDB2__nontx
</non-jta-data-source>
</persistence-unit>
</persistence>
The sample-db1 persistence unit maps to the jta-data-source with
a JNDI name of SamplesDB. The sample-db2 persistence unit maps
to the jta-data-source with a JNDI name of SamplesDB2.
The sample-db1 and sample-db2 persistence units are used to
create and inject the appropriate entity managers into the
stateless session bean. Here is the code in the session bean
that injects the entity managers with a transactional
persistence context on which the session bean has a dependency:
@PersistenceContext(unitName="sample-db1",
type=PersistenceContextType.TRANSACTION)
private EntityManager em1;
@PersistenceContext(unitName="sample-db2",
type=PersistenceContextType.TRANSACTION)
private EntityManager em2;
em1 is the entity manager for the sample-db1 persistence context.
This means that when the viewOrder.jsp JSP calls the method
loadOrder, the method uses the entity manager for the
sample-db1 persistence context to load the Order entity from
the sun-appserv-samples-db1 database.
em2 is the entity manager for the sample-db2 persistence context.
This means that when the viewOrder.jsp JSP calls the method
loadConfiguration, the method uses the entity manager for the
sample-db2 persistence context to load the Configuration
entity from the sun-appserv-samples-db2 database.
Summary
As you can see, the Java Persistence API makes it easy to store
persistent entities in multiple databases.
For more information about the Java Persistence API, see the
article The Java Persistence API - A Simpler Programming Model
for Entity Persistence.
Running the Sample Code
To install and run the sample code that accompanies this tip:
- If you haven't already done so, download GlassFish from the
GlassFish Community Downloads Page, and install
it.
- Set the following environment variables:
GLASSFISH_HOME. This should point to where you installed
GlassFish.
ANT_HOME. This should point to where ant is installed. Ant
is included in the GlassFish bundle that you downloaded.
(In Windows, it's in the lib\ant subdirectory.)
JAVA_HOME. This should point to the location of JDK 5.0 on
your system.
Add $JAVA_HOME/bin, $ANT_HOME/bin, and $GLASSFISH_HOME/bin to
your PATH environment variable.
- Download the sample package and extract its contents. You should now see the newly extracted
directory as
<sample_install_dir>/ttjuly2006multdb , where
<sample_install_dir> is the directory in which you installed
the sample package. The sample directory below
ttjuly2006multdb contains the source files and other support
files for the sample.
- Change to the sample directory and edit the
build.properties
file as appropriate. For example, if the admin host is remote,
change the value of admin.host from the default (localhost)
to the appropriate remote host. Also, make sure that the
javaee.server.passwordfile location is correct.
- Start GlassFish:
$GLASSFISH_HOME/bin/asadmin start-domain domain1
- Start the database server. From the sample directory enter
the following command:
ant setup
In response you should see output similar to this:
...
start-db:
[exec] Database started in Network Server mode on
host ... and port ...
[exec] Starting database in the background. Log
redirected to ...
[exec] Command start-database executed successfully.
- Build and deploy the sample application. From the sample
directory enter the following command:
ant all
In response you should see output similar to this:
compile:
[javac] Compiling ... source files to ...
...\sample\build\classes
package-ejb:
...
[jar] Building jar: ...\sample\build\ejb\sample.jar
package-war:
...
[zip] Building zip: ...\sample\build\sample.ear
tools:
deploy:
[exec] Command deploy executed successfully.
- Start the application. Open your browser to
http://<host>:8080/sample/index.html, replacing <host> with
your host name (for instance,
localhost). You should see the
home page of the application.
About the Author
Rahul Biswas is a member of the Java Performance Engineering
group at Sun. He is currently involved in the development of
a generic performance benchmark for Java Persistence and the
performance improvement of the persistence implementation in
GlassFish.
|