Visar inlägg med etikett web services. Visa alla inlägg
Visar inlägg med etikett web services. Visa alla inlägg

onsdag, april 16, 2008

Re-Configuring a Web Service DataControl to Point to Another WSDL

A common issue that occurs in all projects is the question about moving an Application between different environments; for example, moving from the Development environment to the Test environment or from the Test environment to the Production environment. Normally this is not a big issue, you just make some modifications to your build scripts (Ant, Maven or whatever you use), or you already have different targets within them for the different environments, and within these targets you point the Application to use the appropriate resources (like Databases) for the different environments. Quite convenient. For most type of resources this approach works fine, however when you use an ADF Application that accesses Web Services via a DataControl exactly how-to do this is not that obvious.

Suppose that you have a Web Services available in a Test and in a Production environment. The URLs are:

Test:
http://MyTestHost/myContext/TheWebServiceSoapHttpPort?WSDL

Production:
http://MyProductionHost/myContext/TheWebServiceSoapHttpPort?WSDL

Now, you create a new JDeveloper project with a Web Service DataControl that point to the Test URL. You then start to browse the project and you find a file called DataControls.dcx. Within this file you find a pointer to a WSDL file (under DataControlConfigs -> AdapterDataControl -> Source -> definition). Great! This must the pointer to my Web Service you think, which is reasonable to believe cause there is no single other reference to a WSDL available within your whole project. So, you modify your Ant build script, creates deployment targets for the different environments that points to the respective WSDL and you deploy to the Test environment. This works fine (well, since the Web Service was generated towards this WSDL, it should). Next, you deploy to the Production environment but now when you run the Application, you still see data from the Test environment. What the ¤%&" going on???

The answer here is that there is a little more to the story then what appears at first sight. If you have a look in the WAR file for the Application, you will notice a file called connections.xml, sounds promising, right, as the connections seems to be the problem here? If you open it, you will find some interesting information, but first...

If you go back and have another look at your DataControls.dcx under the definitions section, you see an element like:

<service name="TheWebService" namespace="http://testwsdcx/" connection="ClientService">

as the connection here is the same as the name in the definition it is easy to believe that this point to the definition, however, that is not the case. The connection attribute points instead to the corresponding Reference element in the connections.xml file. Further, in the Reference section, you will find the real pointers that the DataControl uses to communicate with the Web Service. I said pointers, cause there are two for each Reference element; one to the WSDL (under wsconnection) and one to the Port (under service -> port -> soap).

I might at this point just add for reference that JDeveloper adds this file automatically to the WAR file during deployment; however, I think you have figured that one out already...

So, based on the above discussion we can now solve the problem in two ways:

Option A: Let your build script modify the connections.xml file and point to the correct.

Option B: Copy the whole Reference section for your Web Service connection and give it another name, for example ClientServiceTest and ClientServiceProd. You can then choose which connection to use by pointing the connection attribute in the DataControls.dcx to the correct definition, like:

<service name="TheWebService" namespace="http://testwsdcx/" connection="ClientServiceTest">

Or:

<service name="TheWebService" namespace="http://testwsdcx/" connection="ClientServiceProd">

and of course, you need to handle this in your build script. So far I haven't found any major differences between the approaches, however I think that Option B looks a bit cleaner, but the choice is really yours.

Oh, I almost forgot... The connections.xml file is found on the Application level, not on the Project level, for JDeveloper. It is not visible in the IDE, but you can find it in the .adf/META-INF folder for your Application.

I'm assuming that the Web Services in the example above are identical; just that they are deployed to different machines...

onsdag, december 05, 2007

Maintaining Changes to Web Services

A common problem that occurs when working with Web Services is how-to maintaining changes to the WSDL and/or XSD files that are used by the service. The problem occurs when performing changes to the WSDL (or XSD) files like removing operations, renaming operations, changing the structure of data types etc. As you might always not be aware of which clients that are using your service(s); you want to make sure that they continues to work even after you have implemented changes to your service. As support for dealing with this has not been built into the Web services architecture / standards; it is basically left to each implementer to deal with this problem.

One way of dealing with this problem is to use Namespaces versions. The approach for this is as:

1. Use different XML namespaces for different version of the service.
2. Send a specific namespace value along with every SOAP message and result.
3. Based on this value a Web Service implementation (mediator) can then correctly determine what to do with the incoming message.

You first need to ensure that the namespace for the XML elements resulting from that document is unique, for example, suppose that you now are using a namespace like:

targetNamespace="http://example.com/mySchema.xsd".

You then need to change this to:

targetNamespace="http://example.com/2007/11/01/mySchema.xsd"

Where the numbers are year, month and date (it is not likely that you will alter the namespace more than once a day...).

Now each request will be accompanied with a reference to a namespace thus leaving it up to the Web Services to deal with what to do with requests that come in for any particular namespace.

Next thing is to decide what to do with the various types of incoming requests. One approach here is to generate an error if a request for an older namespace is received, and leave it up to the client to deal with this. Another approach is to use a mediator, as mentioned earlier in the post. The task of the mediator is to determine what to do with Web service requests that come in for any particular namespace. This is done by examination of the date stamp on the namespace (introduced in the previous steps) and then route requests from the older namespace to the older version of the Web service, while routing requests from the newer namespace to the new version of the Web service. This means that the URL to the Web Service (the mediator) will always be the same to your external clients, while you are routing it to different services internally.

As a mediator you could either use a Web Service that you write the code yourself for to handle this, or you could look into using our ESB (Enterprise Service Bus) product for this task.

tisdag, november 27, 2007

Building a Web Service from a XSD using JDeveloper 11

A few days ago I wrote a post about building a Web Service from a XSD using JDeveloper 10.1.3.3. As a result of this I got a mail from Gerard Davison who informed me that this will be even easier in JDeveloper 11 using the WSDL editor and the top down generator:
  1. Create a new WSDL
  2. Put the XSD somewhere nearby in the project
  3. Tile the editors
  4. Pick up the country info element and drop in the empty "PortType" column. The tool will prompt you for the name of the portType, and generate a single operation that takes this types as the input and output message.
  5. Pick up and drop the portType on the binding, then the new binding on the services column. You should now have a valid WSDL
  6. Now use the generate Java web service from WSDL wizard.
This will now generate all the right code and binding classes in one step without you needing to do any JAXB work.

Thanks a lot for the information Gerard!

It also turns out that we are having a demo for this on OTN, check the one under 'WSDL Editor New Features'.

torsdag, november 22, 2007

Building a Web Service from a XSD using JDeveloper

Yesterday I was asked how to create a Web Service from a XSD file using JDeveloper by a colleague, so I thought I'd might share the answer I gave to a wider audience. Hopefully someone else out there might also have some use for it.

The purpose of this example is to show how you can build a Java Web Service starting with only a single XSD file using JDeveloper.

1. Create an empty project in JDeveloper (I used version 10.1.3.3).

2. Add the XSD to your project, in this example I use an XSD that looks like:

<schema xmlns="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.oracle.com/pcbpel/CountryInfo"
elementFormDefault="qualified">
<element name="CountryInfo">
<complexType>
<sequence>
<element name="Country" maxOccurs="unbounded">
<complexType>
<sequence>
<element name="Name" type="string"/>
<element name="Capital" type="string"/>
<element name="Area" type="decimal"/>
</sequence>
</complexType>
</element>
</sequence>
</complexType>
</element>
</schema>

3. Select the XSD in the Applications Navigator

4. Select Tools -> JAXB Compilation from the Menu

5. Now you have JAXB generated Java classes based on your XSD in your project. This is described more in:

http://www.oracle.com/webapps/online-help/jdeveloper/10.1.3?navId=4&navSetId=_&vtTopicFile=working_with_xml/xml_pjaxb.html

6. Create a new Java class, this is the class that will be the Web Service

7. In this class create a method (this will be exposed as a Web Service method) it has a javax.xml.soap.SOAPElement as input parameter and void as return value, like:

public void testMethod(javax.xml.soap.SOAPElement myDoc)

8. In the method body, add code to unmarshal the document and in this example just write it to System.out:

try {
JAXBContext jc = JAXBContext.newInstance("project3");
Unmarshaller u = jc.createUnmarshaller();

CountryInfo countryInfo = (CountryInfo)u.unmarshal(myDoc);
List lst = countryInfo.getCountry();

for(Iterator iter = lst.iterator(); iter.hasNext();) {
CountryInfo.CountryType country = (CountryInfo.CountryType)iter.next();
System.out.println( country.getName() + ", " + country.getCapital());
}
}
catch (JAXBException je) { je.printStackTrace(); }


9. Start the Java Web Service wizard from the New Gallery, select the newly generated class and just use the default values in the Wizard, except for SOAP Format, here use Document/Literal.

10. Before deploying the Web Service, make sure that all needed files are included in the deployment profile.

11. Once done, either deploy the Web Service to an Application Server or standalone OC4J instance and test it using XML like:

<?xml version="1.0" encoding="UTF-8" ?>
<CountryInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.oracle.com/pcbpel/CountryInfo src/xsd/CountryInfo.xsd"
xmlns="http://www.oracle.com/pcbpel/CountryInfo">
<Country>
<Name>Sweden</Name>
<Capital>Stockholm</Capital>
<Area>1.0</Area>
</Country>
</CountryInfo>

this should give the following printed to System.out:

07/11/22 12:29:31 Sweden, Stockholm

fredag, april 27, 2007

Problems Calling .Net Web Services from UTL_DBWS

I've been having some issues with a problem from a customer, the scenario is quite simple; they simply want to call a .Net Web Service from the database using utl_dbws. This should be fairly straightforward, however, this did not work as expected, instead I've been keep getting this error message:

ORA-29532: Java call terminated by uncaught Java exception:
javax.xml.rpc.soap.SOAPFaultException: Server did not recognize the value of HTTP Header SOAPAction: .
ORA-06512: at "SYS.UTL_DBWS", line 387
ORA-06512: at "SYS.UTL_DBWS", line 384


and I could simply not figure out why. So, when I read the utl_dbws api for the 111:th time (give or take a few times...) I found that:

"'SOAPACTION_USE': This boolean property indicates whether or not SOAPAction is to be used. The default value of this property is 'FALSE'."

which certainly corresponds to the error message I've been getting, as it indicates that no SOAPAction is being sent.

So, I simply added these lines:

sys.utl_dbws.set_property(call_, 'SOAPACTION_USE', 'TRUE');
sys.utl_dbws.set_property(call_, 'SOAPACTION_URI', 'http://tempuri.org/Hello');


and the request went fine. A few lines that made the difference but took me too long time to find out...

The utl_dbws package is described in:
http://download-uk.oracle.com/docs/cd/B19306_01/appdev.102/b14258/u_dbws.htm#sthref13863

The The SOAPAction HTTP Header Field is described in:
http://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528