Details
-
Improvement
-
Status: Closed
-
Minor
-
Resolution: Fixed
-
2.1.4
-
None
-
None
-
Operating System: other
Platform: All
-
31359
Description
Intro
-----
This code is an initial implementation of Luke Hubbard's concept of loading
web services as objects within flowscript allowing their methods to be called
using native data types as parameters. The WebServiceLoader uses Apache Axis
for the generation of client-side Java bindings and is limited to SOAP-based
web services.
The impetus for writing this code came from the need to direct the application
flow of control based upon the response from a remote SOAP web service. As
Luke pointed out all of the components needed to do this already existed in
either the Cocoon or Axis projects so there wasn't so much for me to do. I
find accessing web services in this way useful and hope that this
functionality can be added to Cocoon in some shape or form.
Flowscript
----------
flowWebServices.js in the attached archive shows how 2 SOAP services are
called from the flow layer using the WebServiceLoader, the example services
are listed at http://www.xmethods.net. The steps required to call a SOAP
service using the loader are:
- import the WebServiceLoader class in the script file
- create an instance of the loader using the cocoon object so that the
component is correctly initialized
- load the web service as a local object by passing the WSDL URI to the
loader, optionally naming the required service and port
- call methods on the web service as a local object
No external Java code is needed.
Java Implementation
-------------------
The class org.apache.cocoon.components.flow.ws.WebServiceLoader.java is
responsible for loading the web service. It coordinates parsing of the WSDL,
endpoint selection, generation of the java client side bindings, and dynamic
compilation of the desired endpoint client proxy. The WebServiceLoader
implements several Avalon interfaces:
- Contextualizable: the Avalon context is needed for accessing the
cocoon 'work-directory' property. Generated java files are created in a
subdirectory of the work directory.
- ThreadSafe: I believe implementing this interface ensures that only one
instance of the class is created per container. The WebServiceLoader holds a
reference to an instance of the CompilingClassLoader. The intention here was
to avoid unnecessary re-compilation of the client bindings. I do confess I
didn't look deeply into the workings of the CompilingClassLoader having lifted
it more or less directly from the FOM javascript interpreter.
- Serviceable: the ServiceManager is used to lookup a SourceResolver for
construction of the classloader.
- LogEnabled: for logging.
The WebServiceLoader creates an EndpointDefinition. The class
org.apache.cocoon.components.flow.ws.EndpointDefinition.java is essentially a
wrapper around an instance of javax.wsdl.Definition. An EndpointDefinition
instance represents a unique service/port combination declared within the WSDL
document. If the service and port name aren't supplied when the web service
is loaded the WSDL is navigated and the 1st service declared in the document
with a port with a SOAP binding is selected as the default service.
Once an EndpointDefinition has been created client bindings are generated from
the WSDL by org.apache.cocoon.components.flow.ws.ClientBindingGenerator.java.
This class extends the WSDL2Java utility from the Apache Axis project, it's
purpose is purely to allow for programmatic invocation of the tool. The
generated classes are created in a subdirectory of the cocoon work directory.
Now that the client bindings have been created an instance of the client
endpoint proxy is dynamically instantiated by reflection and it is this object
that is returned to the calling flowscript. The proxy object is put in a
cache before being returned to the client.
I hope that this code might be a starting point for getting this functionality
into Cocoon, my understanding of the Cocoon architecture might be a little bit
thin to bring this up to scratch for inclusion in the project.
Cheers
Adam
-----
This code is an initial implementation of Luke Hubbard's concept of loading
web services as objects within flowscript allowing their methods to be called
using native data types as parameters. The WebServiceLoader uses Apache Axis
for the generation of client-side Java bindings and is limited to SOAP-based
web services.
The impetus for writing this code came from the need to direct the application
flow of control based upon the response from a remote SOAP web service. As
Luke pointed out all of the components needed to do this already existed in
either the Cocoon or Axis projects so there wasn't so much for me to do. I
find accessing web services in this way useful and hope that this
functionality can be added to Cocoon in some shape or form.
Flowscript
----------
flowWebServices.js in the attached archive shows how 2 SOAP services are
called from the flow layer using the WebServiceLoader, the example services
are listed at http://www.xmethods.net. The steps required to call a SOAP
service using the loader are:
- import the WebServiceLoader class in the script file
- create an instance of the loader using the cocoon object so that the
component is correctly initialized
- load the web service as a local object by passing the WSDL URI to the
loader, optionally naming the required service and port
- call methods on the web service as a local object
No external Java code is needed.
Java Implementation
-------------------
The class org.apache.cocoon.components.flow.ws.WebServiceLoader.java is
responsible for loading the web service. It coordinates parsing of the WSDL,
endpoint selection, generation of the java client side bindings, and dynamic
compilation of the desired endpoint client proxy. The WebServiceLoader
implements several Avalon interfaces:
- Contextualizable: the Avalon context is needed for accessing the
cocoon 'work-directory' property. Generated java files are created in a
subdirectory of the work directory.
- ThreadSafe: I believe implementing this interface ensures that only one
instance of the class is created per container. The WebServiceLoader holds a
reference to an instance of the CompilingClassLoader. The intention here was
to avoid unnecessary re-compilation of the client bindings. I do confess I
didn't look deeply into the workings of the CompilingClassLoader having lifted
it more or less directly from the FOM javascript interpreter.
- Serviceable: the ServiceManager is used to lookup a SourceResolver for
construction of the classloader.
- LogEnabled: for logging.
The WebServiceLoader creates an EndpointDefinition. The class
org.apache.cocoon.components.flow.ws.EndpointDefinition.java is essentially a
wrapper around an instance of javax.wsdl.Definition. An EndpointDefinition
instance represents a unique service/port combination declared within the WSDL
document. If the service and port name aren't supplied when the web service
is loaded the WSDL is navigated and the 1st service declared in the document
with a port with a SOAP binding is selected as the default service.
Once an EndpointDefinition has been created client bindings are generated from
the WSDL by org.apache.cocoon.components.flow.ws.ClientBindingGenerator.java.
This class extends the WSDL2Java utility from the Apache Axis project, it's
purpose is purely to allow for programmatic invocation of the tool. The
generated classes are created in a subdirectory of the cocoon work directory.
Now that the client bindings have been created an instance of the client
endpoint proxy is dynamically instantiated by reflection and it is this object
that is returned to the calling flowscript. The proxy object is put in a
cache before being returned to the client.
I hope that this code might be a starting point for getting this functionality
into Cocoon, my understanding of the Cocoon architecture might be a little bit
thin to bring this up to scratch for inclusion in the project.
Cheers
Adam