Programming WebLogic Web Services
The following sections describe how to use WebLogic Workshop to create WebLogic Web Services:
This document provides examples and scenarios of using different technologies of WebLogic Platform (Workshop IDE) to create WebLogic Web Services. The overview information is divided into the following topics:
Today in Java, there are two main programming models for developing Web Services. Both of these models are supported by BEA. The first is defined in JAX-RPC, which relies on a back-end EJB or a plain Java Object to provide the business logic of the Web Service. To develop Web Services using this model you can use the WebLogic Web Services Ant tasks (such as servicegen
). The second model is based on code annotation as defined in JSR-181, Web Services Metadata for the Java Platform. The model used to develop Web Services in WebLogic Workshop is a precursor to JSR-181. Whether you decide to use the JAX-RPC model or the WebLogic Workshop model, the Web Services you develop will deploy and run on WebLogic Server, however the SOAP implementation and dispatch model will vary depending on which programming model you choose.
Typically, this difference in SOAP implementation is transparent and unimportant. However, due to the differences in the programming models and the slight differences in the characteristics of the two runtimes, you sometimes might want to use WebLogic Workshop to create applications that run on the runtime supported by the JAX-RPC programming model. To do so, you cannot use annotated JWS files, the standard way to create Web Services in WebLogic Workshop. Rather, you use WebLogic Workshop to create the back-end component (stateless session EJB), export the EJB and an Ant build script that builds the component, then add calls to the Web Service Ant tasks to the build script to package everything up into a Web Service than runs on the runtime supported by the JAX-RPC model. The examples in this document show how to go through this process.
Once you have exported the EJB to a JAR file from WebLogic Workshop, follow the standard guidelines outlined in this book to create a Web Service that runs on the runtime supported by the JAX-RPC programming model. In particular, refer to:
When you use WebLogic Workshop to create a stateless session EJB back-end component, you are actually using a plug-in called EJBGen, an EJB 2.0 code generator. When you write the code for your EJB in Workshop, you use special @ejbgen
Javadoc tags to describe what the EJB looks like, and then, when you build your EJB, Workshop calls the EJBGen plug-in to generate the remote and home interface classes and the deployment descriptor files.
EJBGen can also be executed as a command-line utility, which means that the same *.ejb
file you use in Workshop can also be processed outside of Workshop. (The only difference is that you must change the extension from *.ejb
to *.java
.) Use the java command, as shown in the following example:
java weblogic.tools.ejbgen.EJBGen myEJB.java
One way of using the command-line version of EJBGen is to add it to the Ant build script that calls the Ant tasks, such as servicegen
, to build your WebLogic Web Service so that you can re-generate your EJB without having to use Workshop.
Because you can use EJBGen both within Workhop and as a command-line utility, it is assumed in the examples in this document that you might use either flavor, even if the example describes one particular flavor.
For details about using the command-line EJBGen tool, see EJBGen Reference. For details about the EJBGen Workshop plug-in, see the Developing Enterprise JavaBeans topic in the left frame of the WebLogic Workshop Help.
The examples in this document show how to use meta-data tags in Java source code files to create Web Services. These meta-data tags come in two flavors:
@ejbgen
Javadoc tag.source2wsdd
Ant task, specified with the @wlws
Javadoc tag.You use meta-data Javadoc tags in a Java source file to specify in more detail what an EJB, and the Web Service that exposes the EJB, look like. Then you use either EJBGen or the source2wsdd
Ant task (or both) to generate the additional components. In particular, EJBGen generates the EJB deployment descriptors and the EJB Home and Remote interfaces; the source2wsdd
Ant task generates the Web Services deployment descriptor file.
For reference information about the EJBGen tags, see EJBGen Reference. For information about the source2wsdd
tags, see source2wsdd Tag Reference.
This section describes a simple example of creating a WebLogic Web Service using the WebLogic Workshop IDE.
Note: This procedure works only with Service Pack 2 of WebLogic Workshop. For an example that works on the GA version of WebLogic Workshop, see Using WebLogic Workshop To Create a WebLogic Web Service: A More Complex Example.
The example first uses Workshop to create a stateless session EJB called PurchaseOrderBean
, and then uses the servicegen
WebLogic Web Services Ant task to expose the EJB as a Web Service that runs on the runtime supported by the JAX-RPC programming model. In this example, all the business logic is directly in the PurchaseOrderBean
, which exposes two methods as Web Service operations: submitPO
and getStatus
.
Note: The following procedure does not always describe the exact steps you must perform in the IDE to create the various projects and objects. For this kind of detailed information, see the Developing Enterprise JavaBeans topic in the left frame of the WebLogic Workshop Help.
PurchaseOrderBean.ejb
, replacing all the code after the package myPackage
statement with the following code:import javax.ejb.*;
import weblogic.ejb.*;
/**
* @ejbgen:session
* ejb-name = "PurchaseOrder"
*
* @ejbgen:jndi-name
* remote = "ejb.PurchaseOrderRemoteHome"
*
* @ejbgen:file-generation
* remote-class = "true"
* remote-class-name = "PurchaseOrder"
* remote-home = "true"
* remote-home-name = "PurchaseOrderHome"
* local-class = "false"
* local-class-name = "PurchaseOrderLocal"
* local-home = "false"
* local-home-name = "PurchaseOrderLocalHome"
*/
public class PurchaseOrderBean
extends GenericSessionBean
implements SessionBean
{
public void ejbCreate() {
// Your code here
}
/**
* @ejbgen:remote-method
*/
public long submitPO(String PoText)
{
return System.currentTimeMillis();
}
/**
* @ejbgen:remote-method
*/
public int getStatus(long orderNo)
{
return 0;
}
}
myEJBs
project in the application pane and selecting Build myEJBs. PurchaseOrder
EJB outside of Workshop by clicking Tools->Application Properties..., choosing Build in the left pane, and clicking the Export to Ant File button. <taskdef name="servicegen"
classname="weblogic.ant.taskdefs.webservices.servicegen.ServiceGenTask"
classpath="${server.classpath}" />
<target name="servicegen">
<delete file="${output.file}" />
<servicegen
destEar="${output.file}"
>
<service
ejbJar="myEJBs.jar"
serviceName="PurchaseOrderService"
serviceURI="/PurchaseOrderService"
targetNamespace="http://example.com/PurchaseOrderService"
/>
</servicegen>
</target>
setEnv.cmd
command, located in your domain directory. The default location of WebLogic Server domains is BEA_HOME
\user_projects\domains\
domainName, where BEA_HOME
is the top-level installation directory of the BEA products and domainName
is the name of your domain.prompt> ant -f exported_build.xml build servicegen
The servicegen
target of the Ant task updates the application, exposing the PurchaseOrder
EJB as a WebLogic Web Service.
This section describes a more complex example of creating a WebLogic Web Service using the WebLogic Workshop IDE.
In the example, the PurchaseOrderServiceBean
EJB is exposed as a Web Service, but it does not contain any business logic. It has one operation that accepts a purchase order number from an incoming SOAP request and returns a PurchaseOrder
object in the SOAP response. The PurchaseOrderServiceBean
EJB that is exposed as a Web Service delegates all the actual work of looking up a purchase order to a conventional session facade EJB called PurchasingManagerBean
. This EJB implements all the business logic of the application, independent of the Web Service entry point. The EJB uses the Item
and PurchaseOrder
complex data types when processing purchase orders, and creates new PurchaseOrder
objects using the PurchaseOrderFactory
.
The PurchaseOrderServiceBean
EJB, in addition to using EJBGen Javadoc tags as in the preceding example, also uses WebLogic Web Service source2wsdd
tags, identified with the @wlws
prefix. Because of the use of meta-data tags, the example uses individual Ant tasks, such as source2wsdd
and autotype
, to assemble a Web Service, rather than the all-encompassing servicegen
Ant task. For details about the source2wsdd
tags, see source2wsdd Tag Reference.
The example also shows how to use a SOAP message handler. The SOAP message handler looks for a SOAP header called My-Username in the SOAP request from a client, and if it exists, it extracts the value, and logs a message that includes the name to a log file. If the header does not exist, the SOAP message handler logs a message with Unknown
as the username. For additional information about SOAP message handlers, see Creating SOAP Message Handlers to Intercept the SOAP Message.
This main point of this example is to show how to use WebLogic Workshop to create an EJB and SOAP message handler that together will be exposed as a Web Service, and then how to package it all together into a deployable EAR file than runs on the runtime supported by the JAX-RPC programming model. For this reason, it is assumed that you have already:
myComplexApp
, that contains an EJB Project called PurchaseOrderService
. PurchaseOrderService
EJB project, created a folder called po
that contains the EJB that performs all the business logic (PurchasingManagerBean
) as well as the various objects used by the PurchasingManagerBean
EJB, such as Item
, PurchaseOrder
, and PurchaseOrderFactory
. See Source Code for Supporting Java Objects for sample source code for these objects.
Note: The following procedure does not always describe the exact steps you must perform in the IDE to create the various projects and objects. For this kind of detailed information, see the Developing Enterprise JavaBeans topic in the left frame of the WebLogic Workshop Help.
package service;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
import javax.ejb.CreateException;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import po.PurchasingManagerLocal;
import po.PurchasingManagerLocalHome;
/**
* This is the web service facade. It defines the web service
* operations and contains any web service-specific
* application logic (such as logging invokes in a handler).
*
* @ejbgen:session
* ejb-name = "PurchaseOrderServiceEJB"
* @ejbgen:jndi-name
* local = "PurchaseOrderService"
* @ejbgen:ejb-local-ref
* link = "PurchasingManagerEJB"
* @wlws:webservice
* name="PurchaseOrderService"
* targetNamespace="http://openuri.org/easypo_service"
* style="document"
*/
public class PurchaseOrderServiceBean implements SessionBean {
// local interface of the PurchasingManager session facade
PurchasingManagerLocal pm = null;
/**
* This operation return a PurchaseOrder that is retrieved from
* the PurchasingManager
* @ejbgen:local-method
* @wlws:operation handler-chain = "PurchaseOrderServiceHandlerChain"
*/
public po.PurchaseOrder getPurchaseOrder(String poNumber) {
return pm.getPO(poNumber);
}
public void ejbCreate() throws CreateException {
try {
InitialContext ctx = new InitialContext();
PurchasingManagerLocalHome pmhome = (PurchasingManagerLocalHome) ctx.lookup("java:/comp/env/ejb/PurchasingManagerEJB");
pm = pmhome.create();
} catch (NamingException ne) {
throw new CreateException("Could not locate PurchasingManager EJB");
}
}
public void ejbRemove() {
}
public void ejbPassivate() {
}
public void ejbActivate() {
}
public void setSessionContext(SessionContext ctx) {
}
}
package service;
import javax.xml.rpc.handler.Handler;
import javax.xml.rpc.handler.MessageContext;
import javax.xml.rpc.handler.HandlerInfo;
import javax.xml.rpc.handler.soap.SOAPMessageContext;
import javax.xml.namespace.QName;
import javax.xml.soap.*;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
import java.util.Iterator;
import java.util.Date;
/**
* Represents a JAX-RPC handler that intercepts an incoming SOAP message,
* extracts an optional header called"My-Username" and logs a message to a
* log file.
*/
public class PurchaseOrderServiceHandler implements Handler {
private static final String HEADER_NAME = "My-Username";
private PrintWriter out;
public QName[] getHeaders() {
return new QName[]{new QName(HEADER_NAME)};
}
public boolean handleRequest(MessageContext messageContext) {
String userName = null;
try {
userName = getHeaderValue(messageContext, HEADER_NAME);
} catch (SOAPException e) {
throw new RuntimeException("Could not retrieve header value", e);
}
if (userName == null) userName = "UNKNOWN";
out.println(new Date() + ": Received request from username " + userName);
out.flush();
return true;
}
public boolean handleResponse(MessageContext messageContext) {
return true;
}
public boolean handleFault(MessageContext messageContext) {
return true;
}
public void init(HandlerInfo handlerInfo) {
Map config = handlerInfo.getHandlerConfig();
String logFileName = (String) config.get("logFile");
if (logFileName == null) {
throw new RuntimeException("Could not initialize handler; logFile not specified in init-params.");
}
try {
out = new PrintWriter(new FileWriter(logFileName));
} catch (IOException e) {
throw new RuntimeException("Could not initialize handler; could not open log file " + logFileName, e);
}
}
public void destroy() {
out.close();
}
private static String getHeaderValue(MessageContext messageContext,
String headerName) throws SOAPException {
SOAPFactory fact = SOAPFactory.newInstance();
SOAPMessageContext ctx = (SOAPMessageContext) messageContext;
SOAPHeader headers =
ctx.getMessage().getSOAPPart().getEnvelope().getHeader();
Iterator i = headers.getChildElements(fact.createName(HEADER_NAME));
while (i.hasNext()) {
SOAPElement elt = (SOAPElement) i.next();
if (headerName.equals(elt.getElementName().getLocalName())) {
return elt.getValue();
}
}
return null;
}
}
This XML file will contain a description of the SOAP message handler and handler chain used by the Web Service. The file will late be used by the source2wsdd
Ant task when generating the Web Service deployment descriptor.
<root></root>
XML elements with the following XML:<handler-chains>
<handler-chain name="PurchaseOrderServiceHandlerChain">
<handler class-name="service.PurchaseOrderServiceHandler">
<init-params>
<init-param name="logFile" value="./posvc.log" />
</init-params>
</handler>
</handler-chain>
</handler-chains>
setEnv.cmd
command, located in your domain directory. The default location of WebLogic Server domains is BEA_HOME
\user_projects\domains\
domainName, where BEA_HOME
is the top-level installation directory of the BEA products and domainName
is the name of your domainWL_HOME
\workshop\wlwBuild.cmd
, where WL_HOME
refers to the main WebLogic Platform directory, such as c:\beahome\weblogic81
. Use the -project
option to pass it the name of the EJB project, as shown in the following example:prompt> c:\beahome\weblogic81\workshop\wlwBuild.cmd -project PurchaseOrderService
build.xml
file that includes calls to the autotype
and source2wsdd
WebLogic Web Service ant tasks. These Ant tasks take the compiled EJB and SOAP message handler class and create the needed Web Services components, such as the deployment descriptor and data type components. For an example of this file, see Sample build.xml File.
prompt> ant
If you use a build.xml file similar to the sample, the Ant task create a deployable EAR file called PurchaseOrderService.ear
in a directory called output that is parallel to the Workshop project directory.
PurchaseOrderService.ear
file as usual. For details, see Deploying and Testing WebLogic Web Services.<project name="build-scenario1" default="build">
<!-- WebLogic Home. -->
<property name="platformhome" value="/home/toddk/bea/weblogic81"/>
<!-- base url of the server and administrator user, password -->
<property name="server_url" value="http://localhost:7001"/>
<property name="admin_user" value="weblogic"/>
<property name="admin_passwd" value="gumby1234"/>
<!-- location of the browser executable -->
<property name="browser"
value="/usr/local/MozillaFirebird/MozillaFirebird"/>
<!-- the dir into which the output of compilers and tools is directed -->
<property name="output_dir" value="../output"/>
<!-- the name of service (used in constructing war file name, WSDL, etc.) -->
<property name="service_name" value="PurchaseOrderService"/>
<!-- the Java package in which the service class is located -->
<property name="service_package" value="service"/>
<!-- the target namespace of the service -->
<property name="target_namespace"
value="http://openuri.org/easypo_service"/>
<!-- name of the Workshop EJB project containing the web service EJBs -->
<property name="service_ejb_project" value="PurchaseOrderService" />
<!-- the dir that contains the exploded ear file containing the web service -->
<property name="output_ear" value="${output_dir}/${service_name}-ear"/>
<path id="build.classpath">
<pathelement path="${java.class.path}"/>
<pathelement location="${platformhome}/server/lib/webservices.jar"/>
<pathelement location="${output_ear}"/>
<pathelement location="${output_ear}/APP-INF/classes"/>
<pathelement location="${service_ejb_project}.jar"/>
</path>
<target name="build"
depends="clean, setup, webservice.build, webservice.client, build.finish"/>
<target name="clean" description="delete generated stuff">
<delete dir="${output_dir}"/>
</target>
<target name="setup" description="create output directories">
<mkdir dir="${output_ear}/META-INF"/>
<mkdir dir="${output_dir}/${service_name}-war/WEB-INF"/>
</target>
<!-- build the web service from the web service EJB JAR -->
<target name="webservice.build">
<!-- put the EJB JAR in the exploded EAR -->
<copy file="${service_ejb_project}.jar" todir="${output_ear}" />
<!-- generate XML types from the Java value types in the service -->
<autotype javaComponents="${service_package}.${service_name}Local"
typeMappingFile="${output_ear}/APP-INF/classes/types.xml"
destDir="${output_ear}/APP-INF/classes"
packageName="${service_package}"
classpathref="build.classpath"/>
<!-- build the service from the EJB and autotyper types -->
<source2wsdd
javaSource="${service_ejb_project}/${service_package}/${service_name}Bean.ejb"
ddFile="${output_dir}/${service_name}-war/WEB-INF/web-services.xml"
typesInfo="${output_ear}/APP-INF/classes/types.xml"
handlerInfo="${service_ejb_project}/${service_package}/handler-chain.xml"
serviceURI="/${service_name}"
wsdlFile="${output_dir}/${service_name}-war/${service_name}.wsdl"
ejblink="${service_ejb_project}.jar#${service_name}EJB"
classpathref="build.classpath">
</source2wsdd>
<!-- package the web service war -->
<jar destFile="${output_ear}/${service_name}.war" basedir="${output_dir}/${service_name}-war"/>
<!-- package the ear -->
<echo file="${output_ear}/META-INF/application.xml">
<![CDATA[
<!DOCTYPE application PUBLIC '-//Sun Microsystems, Inc.//DTD J2EE Application 1.2//EN'
'http://java.sun.com/j2ee/dtds/application_1_2.dtd'>
<application>
<display-name> ${service_name}-Service </display-name>
<module>
<ejb>
${service_ejb_project}.jar
</ejb>
</module>
<module>
<web>
<web-uri> ${service_name}.war </web-uri>
<context-root> ${service_name} </context-root>
</web>
</module>
</application>
]]>
</echo>
<jar destFile="${output_dir}/${service_name}.ear" baseDir="${output_ear}"/>
</target>
<target name="webservice.client">
<!-- generate JAX-RPC client interfaces, stubs into the client jar -->
<clientgen
description="create a web service client from the ear"
clientJar="${output_dir}/${service_name}-client"
wsdl="${output_dir}/${service_name}-war/${service_name}.wsdl"
typeMappingFile="${output_ear}/APP-INF/classes/types.xml"
packageName="${service_package}.client"
usePortNameAsMethodName="true"
keepgenerated="true"
classpathref="build.classpath">
</clientgen>
<!-- compile the client app into the client jar -->
<javac srcdir="."
includes="client/*.java"
destdir="${output_dir}/${service_name}-client"
classpathref="build.classpath">
</javac>
<!-- package the client jar; executing the jar runs the test app -->
<jar
destFile="${output_dir}/${service_name}-client.jar"
baseDir="${output_dir}/${service_name}-client"/>
</target>
<target name="build.finish" description="clean up temp files">
<delete dir="${output_dir}/${service_name}-client"/>
<delete dir="${output_dir}/${service_name}-war"/>
<delete dir="${output_ear}"/>
</target>
<target name="deploy" description="deploy service">
<wldeploy action="deploy" source="${output_dir}/${service_name}.ear"
adminurl="${server_url}"
user="${admin_user}" password="${admin_passwd}"/>
</target>
<target name="undeploy" description="undeploy service">
<wldeploy action="undeploy" source="${output_dir}/${service_name}.ear"
adminurl="${server_url}"
user="${admin_user}" password="${admin_passwd}"/>
</target>
<target name="browse" description="browse test page for this service">
<exec executable="${browser}">
<arg line="${server_url}/${service_name}/${service_name}"/>
</exec>
</target>
<target name="run" description="run client">
<java classname="client.Main" fork="true">
<classpath>
<pathelement path="${java.class.path}"/>
<pathelement location="${output_dir}/${service_name}-client.jar"/>
</classpath>
<arg line="${server_url}/${service_name}/${service_name}?WSDL"/>
<jvmarg line="-Dweblogic.webservice.verbose=true"/>
</java>
</target>
</project>
This section provides sample code for the following supporting Java objects which are already assumed to exist:
package po;
/**
* Represents a single item on a purchase order.
*/
public class Item {
private String catNumber;
private String description;
private int quantity;
public Item() {
}
public Item(String catNumber, String description, int quantity) {
this.catNumber = catNumber;
this.description = description;
this.quantity = quantity;
}
public String getCatNumber() {
return catNumber;
}
public void setCatNumber(String catNumber) {
this.catNumber = catNumber;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public int getQuantity() {
return quantity;
}
public void setQuantity(int quantity) {
this.quantity = quantity;
}
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("[Item");
sbuf.append("\n\tcatNumber = " + catNumber);
sbuf.append("\n\tdescription = " + description);
sbuf.append("\n\tquantity = " + quantity);
sbuf.append("\n]");
return sbuf.toString();
}
}
package po;
import po.Item;
/**
* Date: Oct 15, 2003
* Time: 3:29:23 PM
*/
public class PurchaseOrder {
private String poNumber;
private Item[] items;
private String custName;
private String custAddress;
public PurchaseOrder() {
}
public PurchaseOrder(String poNumber) {
this.poNumber = poNumber;
}
public String getPoNumber() {
return poNumber;
}
public void setPoNumber(String poNumber) {
this.poNumber = poNumber;
}
public Item[] getItems() {
return items;
}
public void setItems(Item[] items) {
this.items = items;
}
public String getCustName() {
return custName;
}
public void setCustName(String custName) {
this.custName = custName;
}
public String getCustAddress() {
return custAddress;
}
public void setCustAddress(String custAddress) {
this.custAddress = custAddress;
}
public String toString() {
StringBuffer sbuf = new StringBuffer();
sbuf.append("[PurchaseOrder");
sbuf.append("\n\tpoNumber = " + poNumber);
sbuf.append("\n\tcustName = " + custName);
sbuf.append("\n\tcustAddress = " + custAddress);
if (items != null) {
for (int i = 0; i < items.length; ++i) {
sbuf.append("\n");
sbuf.append(items[i].toString());
}
}
sbuf.append("\n]");
return sbuf.toString();
}
}
package po;
import javax.ejb.SessionBean;
import javax.ejb.SessionContext;
/**
* This is a session facade EJB that is the entry point to the business
* logic ofthe application.
*
* @ejbgen:session
* ejb-name = "PurchasingManagerEJB"
* @ejbgen:jndi-name
* local = "PurchasingManager"
*/
public class PurchasingManagerBean implements SessionBean {
/**
* @ejbgen:local-method
*/
public PurchaseOrder getPO(String poNumber) {
return PurchaseOrderFactory.createPO(); // always return same thing
}
/**
* @ejbgen:local-method
*/
public int getStatus(String poNumber) {
return 1;
}
public void ejbRemove() {}
public void ejbCreate() {}
public void ejbPassivate() {}
public void ejbActivate() {}
public void setSessionContext(SessionContext ctx) {}
}
package po;
import java.util.ArrayList;
import java.util.List;
/**
* A Factory to create PurchaseOrders. This just creates the same
* dummy PO each time.
*/
public class PurchaseOrderFactory {
/**
* Constructs a PurchaseOrder object
*/
public static PurchaseOrder createPO() {
PurchaseOrder po = new PurchaseOrder("PO8048392");
// Add customer
po.setCustName("Mary Mary Quite Contrary");
po.setCustAddress("123 Main Street, Hogsmeade");
// Add Line Items
List items = new ArrayList();
items.add(new Item("S-123", "Lacewing Flies", 100));
items.add(new Item("S-456", "Leeches", 3));
items.add(new Item("S-043", "Powdered Bicon Horn", 1));
items.add(new Item("S-153", "Knotgrass", 5));
items.add(new Item("S-904", "Fluxweed", 1));
items.add(new Item("S-034", "Boomslang Skin", 2));
po.setItems((Item[]) items.toArray(new Item[]{}));
return po;
}
}