Oracle® SOA Suite Developer's Guide 10g (10.1.3.1.0) Part Number B28764-01 |
|
|
View PDF |
In an SOA system, you may need to collect data that will be used by an external application. For example, in the Order Booking system, the SOADEMO-CLIENT application collects customer information, which is then passed to the external customer service application, which, in turn, creates a new customer.
Applications and components within an SOA system must expose public service interfaces, typically using WSDL. The web client may therefore either interact with the application components (via RMI) or with the web service interfaces. When interacting through a web service, you can determine the data that a page may need to collect (for example, the attribute names and types) by using the provided WSDL to create a service proxy to the web service.
If methods on the service take simple data types as parameters, then you can create forms that collect data using ADF data binding, similar to the forms created to display data. First, you create a data control based on the provided WSDL. For the procedure, see Section 5.3.3, "How to Create a Data Control From a Web Service". Then, you drop the method itself (as opposed to the return of a method), to create the input form, as described in the following section.
If a method takes a complex object as a parameter, instead of using ADF data binding, you must create the logic to collect the data and pass it to the service. You add this logic to the page's backing bean. For the procedure, see Section 9.6.3, "How to Invoke a Service Using a Backing Bean".
Instead of dragging a return from a method, as you do to display existing data, you drag the method itself to create a parameterized form.
Note: You cannot successfully use a data control to create complex objects. For example, to create aCustomer object for the Order Booking application, you must also create an Address object. To do this, you must manually invoke the service from a backing bean. For more information, see Section 9.6.3, "How to Invoke a Service Using a Backing Bean". |
To create an ADF input form:
Open a JSF page in the visual editor.
From the Data Control Palette, drag the appropriate operation onto the JSF page.
From the context menu, choose Parameters > ADF Parameter Form.
The Edit Form Fields dialog opens, which allows you to customize the labels, bindings, and UI components before creating the form. JDeveloper automatically adds a command button bound to the method, which will invoke the web service and send the data captured in the form.
When you drop an operation as a parameter form, JDeveloper:
Defines variables to hold the data values, a method binding for the operation, and the attribute bindings for the associated attributes in the page definition file.
Inserts code in the JSF page for the form using ADF Faces inputText
components and an ADF Faces command button component. This code is the same as code for any other input form or command button.
When you create a parameter form, JDeveloper creates NamedData
elements for each parameter. Because the user will provide the parameter values, each NamedData
element is bound to the attribute binding for the corresponding attribute. This binding allows the operation to access the attribute's value for the parameter on execution.
Note that like the attributes for a collection, attributes for a method also reference an iterator. However, instead of referencing a method iterator that accesses and iterates over the collection that the associated method returns, attributes for a creation-type method access and iterate over variables. Because this type of method has not returned an object, there is nothing to hold the values entered on the page. Variables act as the data holders.
JDeveloper creates a variable for each parameter the method takes. The variables are declared as children to the variable iterator, and are local, meaning they "live" only as long as the associated binding context. Example 9-9 shows the variable iterator and variables created when you use the addNewCustomer
operation from a data control created from the CustomerSvc
WSDL. Note that for the purposes of demonstration, not all attributes were used to create the form.
Example 9-9 Variables and the Variable Iterator
<executables> <variableIterator id="variables"> <variable Type="java.lang.String" Name="addNewCustomer_customer_fname" IsQueriable="false"/> <variable Type="java.lang.String" Name="addNewCustomer_customer_lname" IsQueriable="false"/> <variable Type="java.lang.String" Name="addNewCustomer_customer_email" IsQueriable="false"/> </variableIterator> </executables>
When the user enters data and submits the form, the variables are populated and the attribute binding can then provide the value for the method's parameters. Note in Example 9-10 that the method parameters are the NamedData
elements, which use an EL expression that evaluates to the attribute binding. The attribute bindings refer to the variables for their value.
Example 9-10 Method Parameters Use NamedData Elements to Provide Values
<bindings> <methodAction id="addNewCustomer" MethodName="addNewCustomer" RequiresUpdateModel="true" Action="999" IsViewObjectMethod="false" DataControl="CustomerSvc" InstanceName="CustomerSvc" ReturnName="CustomerSvc.methodResults.CustomerSvc_addNewCustomer_result"> <NamedData NDName="customer_lname" NDType="java.lang.String" NDValue="${bindings.addNewCustomer_customer_lname}"/> <NamedData NDName="customer_fname" NDType="java.lang.String" NDValue="${bindings.addNewCustomer_customer_fname}"/> <NamedData NDName="customer_email" NDType="java.lang.String" NDValue="${bindings.addNewCustomer_customer_email}"/> </methodAction> <attributeValues id="customer_fname" IterBinding="variables"> <AttrNames> <Item Value="addNewCustomer_customer_fname"/> </AttrNames> </attributeValues> <attributeValues id="customer_lname" IterBinding="variables"> <AttrNames> <Item Value="addNewCustomer_customer_lname"/> </AttrNames> </attributeValues> <attributeValues id="customer_email" IterBinding="variables"> <AttrNames> <Item Value="addNewCustomer_customer_email"/> </AttrNames> </attributeValues> </bindings>
For more information about using ADF Parameter forms, refer to the Oracle Application Development Framework Developer's Guide.
There may be cases when you will need to directly invoke a web service, and you cannot use a data control. You create the logic to do this in the page's backing bean. For example, in the SOADEMO-CLIENT application, all the logic to collect the data and then send it to the service is on the Register.java
backing bean.
In order to easily bind the components on a page, you can use the Automatic Component Binding feature when creating the JSF page. This will automatically bind each component to a property in the backing bean. You'll then add logic to the method bound to the command component, which will send the data to the web service.
To invoke a service without using ADF Data Binding:
Create a JSF page, as described in Section 9.3.1, "How to Create a JSF Web Page". However, for step 7 of that procedure, be sure to select the Automatically Expose UI Components in a New Managed Bean option. Click Help for more information on this step of the wizard.
Use the Component Palette's dropdown menu to choose ADF Faces Core, as shown in Figure 9-19. This will allow you to drag ADF Faces components onto the JSF page.
From the Component Palette, drag an InputText
component onto the page.
Because you chose to use automatic component binding, each component dropped on the page will automatically be bound to a property on the backing bean.
Component properties, such as Label
, AutoSubmit
, and Disabled
, are set for you. You can change these as needed. Refer to the JDeveloper online help for more information about ADF Faces properties.
By default, the component will have an ID based on the component type, such as inputText1
. Change this ID to be the same as the corresponding element name from the associated WSDL.
For example, Example 9-11 shows the elements for a customer in the CustomerService
WSDL:
Example 9-11 Element Definitions in the WSDL
<complexType name="Customer"> <sequence> <element name="password" type="string" nillable="true" /> <element name="lname" type="string" nillable="true" /> <element name="phonenumber" type="string" nillable="true" /> <element name="fname" type="string" nillable="true" /> <element name="addressList" type="ns1:list" nillable="true" /> <element name="creditcardtype" type="string" nillable="true" /> <element name="email" type="string" nillable="true" /> <element name="status" type="string" nillable="true" /> <element name="creditcardnumber" type="string" nillable="true" /> <element name="custid" type="string" nillable="true" /> </sequence> </complexType>
To create an inputText
component for the lname
element, you would change the ID of that component to lname
.
Add code to the corresponding backing bean to collect the data and invoke the web service.
Since there are already getter and setter methods for the input components on the page, you only need to write code that creates a new object using that data. Example 9-12 shows the code to create customer and address objects.
The web service is accessed using the client generated when creating the proxy (see Section 5.3.1, "How to Create a Web Service Proxy"). Note that this method first sets the user as an authorized user, then checks to make sure that the data entered in the password and password check components is the same, and then creates a customer by accessing the web service.
Tip: You will need to import any classes used from the proxy. For example, in Example 9-12, you would need to import theCustomer and Address classes. |
Example 9-12 Code to Create a Customer and Invoke the CustomerService Web Service
public void register_action(ActionEvent ae) { String AUTH_USER = "Authorized_User"; FacesContext ctx = FacesContext.getCurrentInstance(); Customer newCust = new Customer(); if (password.getValue().toString().equals(password _chk.getValue().toString())) { // Call Web service to register new customer try { oracle.soademo.view.services.CustomerServiceClient myPort = new oracle.soademo.view.services.CustomerServiceClient(); System.out.println("calling " + myPort.getEndpoint()); // Adding new customer info Address addr = new Address(); newCust.setFname(fname.getValue().toString()); newCust.setLname(lname.getValue().toString()); newCust.setEmail(email.getValue().toString()); newCust.setPhonenumber(phone.getValue().toString()); newCust.setPassword(password.getValue().toString()); addr.setStreet(street.getValue().toString()); addr.setCity(city.getValue().toString()); addr.setState(state.getValue().toString()); addr.setZip(zip.getValue().toString()); List addrList = new ArrayList(); addrList.add(addr); newCust.setAddressList(addrList); // Call Customer WS to add customer - returns customer id of new customer (newCustId) newCustId = myPort.addNewCustomer(newCust); // Retrieve complete customer object using WS newCust = myPort.findCustomerById(newCustId); // Generate successful registration message.. FacesContext.getCurrentInstance().addMessage(null, new FacesMessage("Registration Successful!")); } catch (Exception ex) { FacesMessage msg = new FacesMessage("Registration Failed!"); msg.setSeverity(msg.SEVERITY_ERROR); FacesContext.getCurrentInstance().addMessage(null, msg); FacesContext.getCurrentInstance().addMessage(null, new FacesMessage(ex.getMessage())); ex.printStackTrace(); }
Drag a command button from the Component Palette.
This button will be used to invoke the method just created.
With the command button selected, in the Property Inspector, use the dropdown menu to select the method just created.
This binds the command button to that method. When the user clicks the button, that method will be invoked, and the entered data will be sent to the web service.
When you drop components onto a JSF page and choose to use automatic component binding, JDeveloper does the following:
If you elect to have JDeveloper create a backing bean, creates a JavaBean using the same name as the JSP, and places it in the view.backing
package.
Creates a managed bean entry in the faces-config.xml
file for the backing bean. By default, the managed bean name is backing_<page_name>
and the bean uses the request scope.
On the newly created or selected bean, adds a property and accessor methods for each component tag you place on the JSP.
Binds the component tag to that property using an EL expression as the value for its binding attribute.
Example 9-13 shows the code created on a backing bean when you add two input components, one for first name and one for last name.
Example 9-13 Register Backing Bean
public class Register { private HtmlHtml html1; private HtmlHead head1; private HtmlBody body1; private HtmlForm form1; private CoreInputText fname; private CoreInputText lname; public void setHtml1(HtmlHtml html1) { this.html1 = html1; } public HtmlHtml getHtml1() { return html1; } public void setHead1(HtmlHead head1) { this.head1 = head1; } public HtmlHead getHead1() { return head1; } public void setBody1(HtmlBody body1) { this.body1 = body1; } public HtmlBody getBody1() { return body1; } public void setForm1(HtmlForm form1) { this.form1 = form1; } public HtmlForm getForm1() { return form1; } public void setFname(CoreInputText inputText1) { this.fname = inputText1; } public CoreInputText getFname() { return fname; } public void setLname(CoreInputText inputText1) { this.lname = inputText1; } public CoreInputText getLname() { return lname; } }
Example 9-14 shows the code on the JSF page binding the components to the Register
backing bean properties.
Example 9-14 JSF Code Using Automatic Component Binding
<afh:body binding="#{backing_Register.body1}" id="body1"> <h:form binding="#{backing_Register.form1}" id="form1"> <af:inputText label="First Name" binding="#{backing_Register.fname}" id="fname"/> <af:inputText label="Label 2" binding="#{backing_Register.lname}" id="lname"/> </h:form> </afh:body>
A command button can use either its Action
or ActionListener
attribute to bind to a method. The actionListener
attribute was used instead of the action
attribute in this case, because the navigation capabilities provided by the action
attribute are not needed, the user remains on the registration page. When you bind the command component to the method on the backing bean, the method is invoked when the user clicks the button.
When creating a method to invoke a web service, you can invoke the web service using the client class automatically generated when creating the proxy. You first call the service, then call the needed operations on the service.
For example, the register_action
method on the Register.java
backing bean first calls the web service using the client class, as shown in Example 9-15.
Example 9-15 Calling the CustomerService Web Service Using the Client Class
oracle.soademo.view.services.CustomerServiceClient myPort = new oracle.soademo.view.services.CustomerServiceClient();
The method then creates the customer by taking the value of each component, converting it to a String
, and setting that as the value for the corresponding property on the Customer
object, as shown in Example 9-16.
Example 9-16 Creating the Customer
Customer newCust = new Customer();. . . newCust.setFname(fname.getValue().toString()); newCust.setLname(lname.getValue().toString()); . . .
Once the customer is created, the method calls the addNewCustomer
operation on the web service, passing in the created customer, as shown in Example 9-17.