Hands-on: Synchronize your database from a webservice with JAX-WS and ADF Business Components

0

This step-by-step starter hands-on provides an example how to make a JAX-WS webservice proxy in JDeveloper, and save retrieved data from this webservice in a batch-job to your own database with ADF Business Components.
Duration: 60 minutes.

For this hands-on example, imagine that your company wants to expand internationally and that reliable, up to date country information is absolutely critical. Recently there were some changes in the number of countries and there might be in the future. Since 1990, 33 new countries have been created. A few months ago the world welcomed a new country (South-Sudan) and yet we dont know what will happen in Libya (maybe it will be separated in West and East-Libya?). Your company wants to weekly synchronise its internal countries database table with up-to-date country information from a recognised country-monitoring institution that delivers up-to-date country information by a webservice.

Part 1: Create the country webservice client with JAX-WS

We are going to create a webservice client proxy for a country webservice available on:

http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso?WSDL

Look at the wsdl file of this webservice and lets have a look at it so that we know to which webservice we talk to. Open de wsdl in de browser, for example in Firefox. At the bottom are all the wsdl operations that we are interested in:

These operations (for example CountryIntPhoneCode) have input (tns:CountryIntPhoneCodeSoapRequest) and output (tns:CountryIntPhoneCodeSoapResponse) messages. These messages are also in the wsdl defined:

The part parameters is also referring to their own definition:

In this example a standard ISO country-code is submitted to the webservice as request parameter for the operation CountryIntPhoneCode. The webservice will give back the international telephone-code back. We are going to use also 3 other operations from this webservice.

1-Create in JDeveloper a new application and call it for example CountriesBatchJob and the project WsClientproxy.

2-Create a webservice proxy (JAX-WS Style) for the wsdl describing the country webservice:

http://webservices.oorsprong.org/websamples.countryinfo/CountryInfoService.wso?WSDL

Give appropiate package names. Click next –> next –> finish.

Regenerate the webservice proxy in case you see any red crosses in the generated overview.

The client class CountryInfoServiceSoapClient will open. Save all.

3-Add in the main() method:

System.out.println("Code ZW; "+countryInfoServiceSoapType.countryName("ZW"));

Test the webservice. (select CountryInfoServiceSoapClient –> right mouse –> run)

OK, the client is ready en works!

Part 2: ADF Business Components

The current database table COUNTRIES from Oracles HR schema is used in this example. Use might need to unlock it first. We will synchronise the rows of the COUNTRIES table with up-to-date countries data from the webservice.
Fortunately the table has already a column COUNTRY_ID that is the PK and is the ISO-landcode.

1- Add columns to the table COUNTRIES with the following script:

alter table
COUNTRIES
add
(
CAPITAL varchar2(500) ,
INTPHONECODE varchar2(500) ,
COUNTRYFLAG varchar2(500) ,
LANGUAGENAME varchar2(500)
)

2-Add a new generic project in this application and call it for example ADFBC.

3-Start the wizard ADF Business Components from tables to create the objects for the Country table. Create a database connection in the wizard with the HR schema. Test it.

Walk through the wizard steps. We only need 1 EntityObject (call it: Country), 1 ViewObject (call it: CountryVw) and an ApplicationModule (call it: AppModule).

Click finish. In the end it should look like this:

4-Change the ViewObject usage name from CountryVw1 to CountryVw. (Click the pencil on the right bottom)

5- Test the ApplicationModule AppModule (select AppModule in tree –> right mouse –> run):

6-Create a Java class. Call it BatchJobSynchronizeCountries. Deselect constructors and select main method;

7-Type bc4jclient followed by CTRL + ENTER .
Test-code is generated by JDeveloper:

8-The generated names (amDef and config) need to be changed to your names. Look in bc4j.xcfg for the correct names: This file is in the common folder of your ADFBC project. On my computer:

C:\JDeveloper\mywork\CountriesBatchJob\ADFBC\src\nl\amis\services\common


The selected part are the (amDef and config names) we need. Use these names in BatchJobSynchronizeCountries.java

Replace ViewObject to CountryVw:
Add:
System.out.println("Number of countries in CountryVw: "+vo.getEstimatedRowCount());

9-Run the client:

Fortunately the call to the countries table succeeded.

10-Query and print all countries and country-codes. Add:

//Query and print all countries
vo.executeQuery();
int rownr = 0;
while (vo.hasNext()) {
rownr++;
Row curCountry = vo.next();
String isoCountryCode = (String)curCountry.getAttribute("CountryId");
System.out.println("(" + rownr + ") isoCountryCode: " + isoCountryCode);
}

Oracle comes standard with 25 country records in the sample Countries table in the HR schema.

Part 3: Synchronize your countries table with data from the webservice

Lets return to the client (proxy) project in JDeveloper.
1-We see 2 lines of code that instantiate a proxy object. With this object we can later call the operations from the webservice. Copy these lines.

2-Return to our ADFBC project to BatchJobSynchronizeCountries.java.
Paste the 2 copied lines under the code we just edited. We can see red compiler lines under our copied code:

This is because JDeveloper cannot find the previous generated client proxy classes. The ADFBC project doesnt know from the existence of our client project. Go to ADFBC project –> right mouse –> properties –> dependencies.
3-Add a dependency:

Save all. Now JDeveloper is able to find the classes and with ALT+ENTER you have can select the import we need and except for one the red lines disappear. It cannot find the variable countryInfoService. We need to declare it.

4- Add :

private static CountryInfoService countryInfoService;

Now the class will compile.

5-Add the following code under the proxy instantiation:

System.out.println("--- (" + rownr + ")\t: " + isoCountryCode+" country : "+countryInfoServiceSoapType.countryName(isoCountryCode));
System.out.println("--- (" + rownr + ")\t: " + isoCountryCode+" capitalCity : "+countryInfoServiceSoapType.capitalCity(isoCountryCode));
System.out.println("--- (" + rownr + ")\t: " + isoCountryCode+" countryFlag : "+countryInfoServiceSoapType.countryFlag(isoCountryCode));
System.out.println("--- (" + rownr + ")\t: " + isoCountryCode+" IntPhoneCode: "+countryInfoServiceSoapType.countryIntPhoneCode(isoCountryCode));

Run this class.

Now, all country-codes are retrieved from the our database. For each country a request is send to the webservice for the country name, the capital city, the country flag and the international phone the data from this webservice is printed out:

6- Updating our COUNTRIES table with the data from the webservice

We need to update/synchronize all data from the webservice with our COUNTRIES table. Look at the specific ViewObjectRow attribute names in CountriesVw:

We need to use these names to map the values from the webservice to the Country ViewObject attributes.
Replace the whole main() method with:

public static void main(String[] args) {
String amDef = "nl.amis.adfbc.services.AppModule";
String config = "AppModuleLocal";
ApplicationModule am =
Configuration.createRootApplicationModule(amDef, config);
ViewObject countryVw = am.findViewObject("CountryVw");

// Work with your appmodule and view object here
System.out.println(“Number of countries in CountryVw: “+countryVw.getEstimatedRowCount());

//Get generated webservice proxy classes form the client project
countryInfoService = new CountryInfoService();
CountryInfoServiceSoapType countryInfoServiceSoapType = countryInfoService.getCountryInfoServiceSoap();

//1- First update existing country rows in the COUNTRIES table
countryVw.executeQuery();
int rownr=0;
while (countryVw.hasNext()) {
rownr++;
Row curCountry = countryVw.next();
String isoCountryCode = (String)curCountry.getAttribute(“CountryId”);
System.out.println(“— UPDATING (” + rownr + “)\t isoCountryCode: ” + isoCountryCode+” country: “+countryInfoServiceSoapType.countryName(isoCountryCode));

//Put attributes on your ViewObject row
curCountry.setAttribute(“Capital”, countryInfoServiceSoapType.capitalCity(isoCountryCode));
curCountry.setAttribute(“Intphonecode”, countryInfoServiceSoapType.countryIntPhoneCode(isoCountryCode));
curCountry.setAttribute(“Countryflag”, countryInfoServiceSoapType.countryFlag(isoCountryCode));
curCountry.setAttribute(“Languagename”, countryInfoServiceSoapType.languageName(isoCountryCode));
curCountry.setAttribute(“CountryName”, countryInfoServiceSoapType.countryName(isoCountryCode));
}
//commit transaction
am.getTransaction().commit();
System.out.println(“Country-batch SUCCEFULLY updated the COUNTRIES table”);
Configuration.releaseRootApplicationModule(am, true);
}

7-This will update all current country records we have in the COUNTRIES table with data from the webservice. Run the class.

This will update all our country records with the country information from the webservice and commit them. Our update-batch job has been successful!

Part 4 – Adding new countries from the webservice

Now, only existing records of the COUNTRIES table are updated. New countries are not yet added and we will do that now.

1-Add just above //commit transaction:

<code>//-----------------------START------------------------------
//Query all countries and put them in a HashMap.
//Purpose: separate an UPDATE and an INSERT in COUNTRIES table
countryVw.executeQuery();
HashMap landCodesMap = new HashMap();
while (countryVw.hasNext()) {
Row curCountry = countryVw.next();
landCodesMap.put(curCountry.getAttribute("CountryId"),curCountry.getAttribute("CountryName"));
}</code>

//2- Insert new country rows in the COUNTRIES table
//Get all countrycodes from the webservice
ArrayOftCountryCodeAndName arrayOftCountryCodeAndName = countryInfoServiceSoapType.listOfCountryNamesByCode();
List tCountryCodeAndNameList = arrayOftCountryCodeAndName.getTCountryCodeAndName();
for(TCountryCodeAndName tCountryCodeAndName:tCountryCodeAndNameList){

String isoCountryCode = tCountryCodeAndName.getSISOCode();
//Add it country to table COUNTRIES when it does not yet exists
if (!landCodesMap.containsKey(tCountryCodeAndName.getSISOCode())) {
System.out.println(” INSERTING IN COUNTRIES TABLE isoCountryCode: "+tCountryCodeAndName.getSISOCode()+" country: "+tCountryCodeAndName.getSName());
// Create new row and add it to the rowset
Row newCounrtyRow = countryVw.createRow();
countryVw.insertRow(newCounrtyRow);

newCounrtyRow.setAttribute("CountryId", tCountryCodeAndName.getSISOCode());
newCounrtyRow.setAttribute("Capital", countryInfoServiceSoapType.capitalCity(isoCountryCode));
newCounrtyRow.setAttribute("Intphonecode", countryInfoServiceSoapType.countryIntPhoneCode(isoCountryCode));
newCounrtyRow.setAttribute("Countryflag", countryInfoServiceSoapType.countryFlag(isoCountryCode));
newCounrtyRow.setAttribute("Languagename", countryInfoServiceSoapType.languageName(isoCountryCode));
newCounrtyRow.setAttribute("CountryName", countryInfoServiceSoapType.countryName(isoCountryCode));
}
}
//

First, we need to identify countries that do not exist yet in our COUNTRIES table. We need a collection (for example a HashMap) with all our current country-records. Later, if the webservice returns a countrycode our hashMap doesnt contain, we know that we have to insert that country in our table.

countryVw.executeQuery();
HashMap landCodesMap = new HashMap();
while (countryVw.hasNext()) {
Row curCountry = countryVw.next();
landCodesMap.put(curCountry.getAttribute("CountryId"),curCountry.getAttribute("CountryName"));

Secondly, we need to get all available country codes from the webservice:

ArrayOftCountryCodeAndName arrayOftCountryCodeAndName = countryInfoServiceSoapType.listOfCountryNamesByCode();
List tCountryCodeAndNameList = arrayOftCountryCodeAndName.getTCountryCodeAndName();

Then, we are going to loop through all the country codes and check whether we need to insert it or not:

for(TCountryCodeAndName tCountryCodeAndName:tCountryCodeAndNameList){
String isoCountryCode = tCountryCodeAndName.getSISOCode();
//Add it country to table COUNTRIES when it does not yet exists
if (!landCodesMap.containsKey(tCountryCodeAndName.getSISOCode())) {
//..
}
}

Then we create a new row and add it to the rowset:

Row newCounrtyRow = countryVw.createRow();
countryVw.insertRow(newCounrtyRow);

And we can set all attribute values:

newCounrtyRow.setAttribute("CountryId", tCountryCodeAndName.getSISOCode());
newCounrtyRow.setAttribute("Capital", countryInfoServiceSoapType.capitalCity(isoCountryCode));
newCounrtyRow.setAttribute("Intphonecode", countryInfoServiceSoapType.countryIntPhoneCode(isoCountryCode));
newCounrtyRow.setAttribute("Countryflag", countryInfoServiceSoapType.countryFlag(isoCountryCode));
newCounrtyRow.setAttribute("Languagename", countryInfoServiceSoapType.languageName(isoCountryCode));
newCounrtyRow.setAttribute("CountryName", countryInfoServiceSoapType.countryName(isoCountryCode));

At the last moment we need to commit the transaction:

am.getTransaction().commit();

2-Run the class. Now, First existing countries are updated and then new countries from the webservice are inserted:

And when we look at our COUNTRIES table we see all the new countries from the webservice;

Our synchronize-batch task which took approximately 10 minutes to run – has succeeded!

Download this complete sample project from here: CountriesBatchJob.zip
Download this hands-on in word: Hands-on JAX-WS.doc

Share.

About Author

Frank Houweling is an Oracle ADF and Java specialist with AMIS (The Netherlands). He focuses mainly on Oracle Fusion ADF, Java Enterprise development and performance management. During the past years he has been requested several times as troubleshooter of ADF projects with bad performance. As such he has been performing performance analysis, bottleneck detection and developing mitigating solutions based on these analysis. He is also the creator of the AMIS ADF Performance Monitor, an advanced monitor that can identify, report and help solve performance bottlenecks in ADF applications.

Leave a Reply