Simon Laws wrote:
On 2/1/07, Jean-Sebastien Delfino <[EMAIL PROTECTED]> wrote:
Simon Laws wrote:
> I notice in implementing the PHP extension (yes - believe it or not
I'm
> nearly ready to make a patch for the next version ;-) that, given the
way
> that I have implemented the PHP extension there is insufficient
> information
> available in the SCA runtime in order to do correct type conversions
when
> passing messages between components. I imagine this has been raised
> before
> but I looked at the archive and couldn't find a relevant thread.
>
> Imagine the scenario:
>
> C++ Component (ComponentA) ---WireA---> PHP Component (ComponentB)
> ---WireB----> C++ Component (ComponentC)
>
> Currently the build process looks at the header files described in the
> component type files and generates wrappers and proxies for the C++
> components. I have currently implemented the PHP Extension to use
generic
> wrappers and proxies, i.e. it doesn't use those generated based on
the
> interface descriptions, so they need to dynamically manage the type
> conversions for data coming in and going out of a PHP component.
>
> WireA.
>
> This is OK because the C++ SCA operation object that appears at
> Component B
> has a set of data/types based on the generated proxy. The PHP
> extension can
> look at this and effect the right type conversions.
>
> WireB
>
> This is problematic. The dynamic PHP proxy has to generate an
operation
> object to pass to the the wrapper of ComponentC. The issue is that
> there is,
> as far as I can tell, no dynamic way of getting at the list of types
that
> are expected for any particular operation. There is of course a static
> C++
> proxy/wrapper combination that has been generated but I can't inspect
> it at
> runtime.
>
> I'm not keen on generating PHP specific interface classes. PHP is a
> dynamic
> environment and it's a whole stack of extra files we could do without
> having
> to manage particularly if we have to adapt the generator for every
> extension
> that's constructed. Can we extend the wrapper/proxy mechanism to
> encapsulate
> a runtime list of required types alongside the static method
descriptions
> that are already generated? We would need this to work for script to
> script
> calls as well as for the script/C++ combination so maybe we need
> something
> that hangs off the interface description part of the model. I'm not
that
> familiar with how that part of the model is used so a little
> investigation
> is required.
>
> Thoughts?
>
> Simon
>
Simon,
It's an interesting issue. To explore it let's walk through the wiring
scenario you describe and assume the following:
- ComponentA (C++) -> WireA -> ComponentB (PHP) -> WireB -> ComponentC
(C++)
- ComponentA (C++) passes a short int to ComponentB (PHP)
- ComponentB executes a PHP script which in turn passes a number to
ComponentC (C++)
- ComponentC expects that number to be given as a long int.
Here's what I think should happen in the runtime:
1. At the source of WireA, a generated C++ CPPServiceProxy adds to our
Operation object a Parameter of a type decided by the C++ client code: a
C++ short int, with type == ParameterType::SHORT.
2. At the end of WireA, a PHPServiceWrapper converts that parameter to
what the PHP script expects, for the sake of simplicity now I am going
to assume that it needs to convert it to a C++ std::string.
3. The PHP script executes, now passes an std::string containing a
number to the PHPServiceProxy at the source of WireB.
4. The PHPServiceProxy does not have much type info about that
std::string parameter and can only add it to the Operation object as a
std::string with type == ParameterType::STRING.
5. The CPPServiceWrapper at the end of WireB (actually the C++
ServiceWrapper generated for ComponentC) receives the std::string and
should convert it to what ComponentC expects: a long int.
The general idea is that a ServiceProxy sends what it is given (or picks
the most natural type out of the ParameterTypes that we have defined and
converts the data to it). A ServiceWrapper converts what it receives to
the type expected by the code it wraps. I think that the
CPPServiceWrapper code and the generated C++ ServiceWrappers are simply
missing the logic to convert data to what the target expects.
At the moment this limitation also prevents a C++ method
getCustomer(long customerID) to be exposed as a REST service for
example, as the generated C++ ServiceWrapper is missing the logic to
convert the customerID received in string form from the REST query
string to the expected C++ long int.
So we just need to add the missing type conversion logic to the C++
ServiceWrappers :)
Thoughts?
--
Jean-Sebastien
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]
Hi Jean-Sebastien
It could well be simpler if, as you suggest, the conversion happens in
the
generated wrapper in this case. So we need such type information, as is
required to effect the type conversion, to be generated into the wrapper.
Yes, and I think that there are three cases to consider here:
- The wrapper is generated and the type info can be burned into it (from
the C++ class/interface of the service for which the wrapper was generated).
- The wrapper is generic and introspects the component implementation
(incl. the associated .componentType metadata and the service interfaces
it declares, if any) to determine the expected data type.
- The wrapper wraps a script written in a language that supports dynamic
typing (e.g. Ruby) and best is to not convert and present the data to
the script as-is (e.g. a string, an integer).
Independent of the type handling, the wrapper is responsible for
presenting the data to the target component implementation in a suitable
form, for example with Ruby, this means turn the input data into a Ruby
VALUE object. Again this is independent of the type, as a Ruby VALUE can
represent a int, a string etc.
However I think we should try and arrange it so that we are not trying
to do
any more conversions than are necessary so I'm not convinced that the
proxy
should be guessing what type to produce only for it to be coerced
again at
the wrapper unless this is a natural effect of the transport/protocol
involved, for example, In your REST example. I.e. we want the proxy to
have
the best information available to it in marshaling the data it has.
I agree. I was not clear before and by conversions in the proxy I meant
"turn the data into a form that can be carried by our Operation +
Operation::Parameter infrastructure". In the case of a C++ proxy, no
conversion should be necessary. In the case of Ruby the string, int etc.
data needs to be extracted of the Ruby VALUE object that wraps it. I'm
not sure about PHP but you probably have a similar mechanism to carry
data and track its usage so you may have to extract the raw data out of
its PHP representation. So in most cases there is no conversion at all
in the proxy or as you said just a natural effect of the
transport/protocol involved (for example with REST we are receiving a
query string and producing XML for the response).
I also need to address complex types in the PHP extension. Where a remote
interface is used you can imagine the WSDL, SMD or whatever being
parsed at
the client component to construct a proxy capable of suitable type
checking
and of providing convenience functions such as
"giveMeAnSDOWithTheRightModelLoaded()". Looking at the code and
samples at
the moment it seems that the C++ infrastructure loads available XSDs and
provides some handy helper methods for creating SDOs based on the types
available. I don't how they are related to particular interfaces
though. I
expect I'm overlooking something.
We also load WSDL files. A Proxy is created from a Reference, and the
Reference can carry an Interface definition, a WSDL portType for
example. Composite::findWSDLDefinition(namespace) will give you a
WSDLDefinition object from which you can lookup a WSDLPortType and
WSDLOperation and the operation input and output types. You should be
able to use these types to validate the data flowing through the proxy.
Regards
Simon
--
Jean-Sebastien
---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]