Orocos Real-Time Toolkit  2.5.0
CorbaOperationCallerFactory.cpp
00001 /***************************************************************************
00002   tag: The SourceWorks  Tue Sep 7 00:55:18 CEST 2010  CorbaOperationCallerFactory.cpp
00003 
00004                         CorbaOperationCallerFactory.cpp -  description
00005                            -------------------
00006     begin                : Tue September 07 2010
00007     copyright            : (C) 2010 The SourceWorks
00008     email                : peter@thesourceworks.com
00009 
00010  ***************************************************************************
00011  *   This library is free software; you can redistribute it and/or         *
00012  *   modify it under the terms of the GNU General Public                   *
00013  *   License as published by the Free Software Foundation;                 *
00014  *   version 2 of the License.                                             *
00015  *                                                                         *
00016  *   As a special exception, you may use this file as part of a free       *
00017  *   software library without restriction.  Specifically, if other files   *
00018  *   instantiate templates or use macros or inline functions from this     *
00019  *   file, or you compile this file and link it with other files to        *
00020  *   produce an executable, this file does not by itself cause the         *
00021  *   resulting executable to be covered by the GNU General Public          *
00022  *   License.  This exception does not however invalidate any other        *
00023  *   reasons why the executable file might be covered by the GNU General   *
00024  *   Public License.                                                       *
00025  *                                                                         *
00026  *   This library is distributed in the hope that it will be useful,       *
00027  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
00028  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU     *
00029  *   Lesser General Public License for more details.                       *
00030  *                                                                         *
00031  *   You should have received a copy of the GNU General Public             *
00032  *   License along with this library; if not, write to the Free Software   *
00033  *   Foundation, Inc., 59 Temple Place,                                    *
00034  *   Suite 330, Boston, MA  02111-1307  USA                                *
00035  *                                                                         *
00036  ***************************************************************************/
00037 
00038 
00039 #include "CorbaOperationCallerFactory.hpp"
00040 #include "AnyDataSource.hpp"
00041 #include "CorbaLib.hpp"
00042 
00043 #include "../../types/Types.hpp"
00044 #include "../../internal/DataSources.hpp"
00045 #include "../../internal/DataSourceCommand.hpp"
00046 #include "../../SendStatus.hpp"
00047 #include "../../Handle.hpp"
00048 
00049 using namespace std;
00050 using namespace RTT;
00051 using namespace RTT::detail;
00052 
00053 CorbaOperationCallerFactory::CorbaOperationCallerFactory( const std::string& method_name, corba::CService_ptr fact, PortableServer::POA_ptr the_poa )
00054     : RTT::OperationInterfacePart(),
00055       mfact(corba::CService::_duplicate(fact) ),
00056       mpoa(PortableServer::POA::_duplicate(the_poa)),
00057       method(method_name)
00058 {}
00059 
00060 CorbaOperationCallerFactory::~CorbaOperationCallerFactory() {}
00061 
00062 unsigned int CorbaOperationCallerFactory::arity()  const {
00063     return mfact->getArity( method.c_str() );
00064 }
00065 
00066 unsigned int CorbaOperationCallerFactory::collectArity()  const {
00067     return mfact->getCollectArity( method.c_str() );
00068 }
00069 
00070 const TypeInfo* CorbaOperationCallerFactory::getArgumentType(unsigned int i) const {
00071     try {
00072         CORBA::String_var tname = mfact->getArgumentType( method.c_str(), i);
00073         if ( Types()->type( tname.in() ) != 0 )
00074             return Types()->type( tname.in() );
00075         // locally unknown type:
00076         if (i == 0)
00077             log(Warning) << "CorbaOperationCallerFactory: remote operation's "<< method <<" return type " << tname.in() << " is unknown in this process." << endlog();
00078         else
00079             log(Warning) << "CorbaOperationCallerFactory: remote operation's "<< method <<" argument "<< i <<" of type " << tname.in() << " is unknown in this process." << endlog();
00080     } catch ( CNoSuchNameException& ) {
00081         assert(false);
00082     }
00083     catch ( CWrongArgumentException& wae){
00084         log(Error) << "CorbaOperationCallerFactory::getArgumentType: Wrong arg nbr: " << wae.which_arg <<" max is " << wae.max_arg <<endlog();
00085     }
00086     return 0;
00087 }
00088 
00089 const TypeInfo* CorbaOperationCallerFactory::getCollectType(unsigned int i) const {
00090     try {
00091         CORBA::String_var tname = mfact->getCollectType( method.c_str(), i);
00092         return Types()->type( tname.in() );
00093     } catch (...){
00094         return 0;
00095     }
00096 }
00097 
00098 
00099 std::string CorbaOperationCallerFactory::resultType() const {
00100     try {
00101         CORBA::String_var result = mfact->getResultType( method.c_str() );
00102         return std::string( result.in() );
00103     } catch ( corba::CNoSuchNameException& nsn ) {
00104         throw name_not_found_exception( nsn.name.in() );
00105     }
00106     return std::string();
00107 }
00108 
00109 std::string CorbaOperationCallerFactory::getName() const {
00110     return method;
00111 }
00112 
00113 std::string CorbaOperationCallerFactory::description() const {
00114     try {
00115         CORBA::String_var result = mfact->getDescription( method.c_str() );
00116         return std::string( result.in() );
00117     } catch ( corba::CNoSuchNameException& nsn ) {
00118         throw name_not_found_exception( nsn.name.in() );
00119     }
00120     return std::string();
00121 }
00122 
00123 std::vector< ArgumentDescription > CorbaOperationCallerFactory::getArgumentList() const {
00124     CDescriptions ret;
00125     try {
00126         corba::CDescriptions_var result = mfact->getArguments( method.c_str() );
00127         ret.reserve( result->length() );
00128         for (size_t i=0; i!= result->length(); ++i)
00129             ret.push_back( ArgumentDescription(std::string( result[i].name.in() ),
00130                                                           std::string( result[i].description.in() ),
00131                                                           std::string( result[i].type.in() ) ));
00132     } catch ( corba::CNoSuchNameException& nsn ) {
00133         throw  name_not_found_exception( nsn.name.in() );
00134     }
00135     return ret;
00136 }
00137 
00146 class CorbaOperationCallerCall: public ActionInterface
00147 {
00148     CService_var mfact;
00149     std::string mop;
00150     std::vector<base::DataSourceBase::shared_ptr> margs;
00151     ExecutionEngine* mcaller;
00152     base::DataSourceBase::shared_ptr mresult;
00153     corba::CAnyArguments_var nargs;
00154     // The type transporter for the return value
00155     CorbaTypeTransporter* mctt;
00156     bool mdocall;
00157 public:
00158     CorbaOperationCallerCall(CService_ptr fact,
00159                     std::string op,
00160                     std::vector<base::DataSourceBase::shared_ptr> const& args,
00161                     ExecutionEngine* caller,
00162                     CorbaTypeTransporter* ctt,
00163                     base::DataSourceBase::shared_ptr result, bool docall)
00164     : mfact(CService::_duplicate(fact)), mop(op), margs(args), mcaller(caller), mresult(result), mctt(ctt), mdocall(docall)
00165     {
00166     }
00167 
00168     void readArguments() {
00169         // We need to delay reading the arguments upto this point such that the args contain
00170         // the latest values.
00171         nargs = new corba::CAnyArguments();
00172         nargs->length( margs.size() );
00173         for (size_t i=0; i < margs.size(); ++i ) {
00174             const types::TypeInfo* ti = margs[i]->getTypeInfo();
00175             CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00176             assert( ctt );
00177             ctt->updateAny(margs[i], nargs[i]);
00178         }
00179     }
00180 
00181     bool execute() {
00182         try {
00183             if (mdocall) {
00184                 CORBA::Any_var any = mfact->callOperation( mop.c_str(), nargs.inout() );
00185                 for (size_t i=0; i < margs.size(); ++i ) {
00186                     const types::TypeInfo* ti = margs[i]->getTypeInfo();
00187                     CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00188                     assert( ctt );
00189                     ctt->updateFromAny( &nargs[i], margs[i] );
00190                 }
00191                 // convert returned any to local type:
00192                 if (mctt)
00193                     return mctt->updateFromAny(&any.in(), mresult);
00194             } else {
00195                 CSendHandle_var sh = mfact->sendOperation( mop.c_str(), nargs.in() );
00196                 AssignableDataSource<CSendHandle_var>::shared_ptr ads = AssignableDataSource<CSendHandle_var>::narrow( mresult.get() );
00197                 if (ads) {
00198                     ads->set( sh ); // _var creates a copy of the obj reference.
00199                 }
00200             }
00201             return true;
00202         } catch ( corba::CNoSuchNameException& ) {
00203             return false;
00204         } catch ( corba::CWrongNumbArgException& ) {
00205             return false;
00206         } catch ( corba::CWrongTypeArgException& ) {
00207             return false;
00208         }
00209 
00210     }
00211 
00212     ActionInterface* clone() const { return new CorbaOperationCallerCall(CService::_duplicate( mfact.in() ), mop, margs, mcaller, mctt, mresult, mdocall); }
00213 
00214     virtual ActionInterface* copy( std::map<const DataSourceBase*, DataSourceBase*>& alreadyCloned ) const {
00215         vector<DataSourceBase::shared_ptr> argcopy( margs.size() );
00216         unsigned int v=0;
00217         for (vector<DataSourceBase::shared_ptr>::iterator it = argcopy.begin(); it != argcopy.end(); ++it, ++v)
00218             argcopy[v] = (*it)->copy(alreadyCloned);
00219         return new CorbaOperationCallerCall(CService::_duplicate( mfact.in() ), mop, argcopy, mcaller, mctt, mresult->copy(alreadyCloned), mdocall);
00220     }
00221 };
00222 
00223 base::DataSourceBase::shared_ptr CorbaOperationCallerFactory::produce(const std::vector<base::DataSourceBase::shared_ptr>& args, ExecutionEngine* caller) const {
00224     corba::CAnyArguments_var nargs = new corba::CAnyArguments();
00225     nargs->length( args.size() );
00226 
00227     // this part is only done to feed to checkOperation() with some defaults.
00228     // We don't want to evaluate() the *real* data sources yet !
00229     for (size_t i=0; i < args.size(); ++i ) {
00230         const types::TypeInfo* ti = args[i]->getTypeInfo();
00231         CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00232         if (!ctt)
00233         throw wrong_types_of_args_exception(i+1,"type known to CORBA transport", ti->getTypeName());
00234         DataSourceBase::shared_ptr tryout = ti->buildValue();
00235         ctt->updateAny(tryout, nargs[i]);
00236     }
00237     // check argument types and produce:
00238     try {
00239         // will throw if wrong args.
00240         mfact->checkOperation(method.c_str(), nargs.in() );
00241         // convert returned any to local type:
00242         const types::TypeInfo* ti = this->getArgumentType(0);
00243         if ( ti ) {
00244             if ( ti != Types()->type("void") ) {
00245                 // create a method call object and a return value and let the former store results in the latter.
00246                 CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00247                 DataSourceBase::shared_ptr result = ti->buildValue();
00248                 // evaluate()/get() will cause the method to be called and remote return value will end up in result.
00249                 return ti->buildActionAlias(new CorbaOperationCallerCall(mfact.in(),method,args,caller, ctt, result, true), result );
00250             } else {
00251                 return new DataSourceCommand( new CorbaOperationCallerCall(mfact.in(),method,args,caller, 0, DataSourceBase::shared_ptr() , true) );
00252             }
00253         } else {
00254             // it's returning a type we don't know ! Return a DataSource<Any>
00255             DataSource<CORBA::Any_var>::shared_ptr result = new AnyDataSource( new CORBA::Any() );
00256             // todo Provide a ctt  implementation for 'CORBA::Any_var' such that the result is updated !
00257             // The result is only for dummy reasons used now, since no ctt is set, no updating will be done.
00258             return new ActionAliasDataSource<CORBA::Any_var>(new CorbaOperationCallerCall(mfact.in(),method,args,caller, 0, result, true), result.get() );
00259         }
00260     } catch ( corba::CNoSuchNameException& nsn ) {
00261         throw  name_not_found_exception( nsn.name.in() );
00262     } catch ( corba::CWrongNumbArgException& wa ) {
00263         throw  wrong_number_of_args_exception( wa.wanted, wa.received );
00264     } catch ( corba::CWrongTypeArgException& wta ) {
00265         throw  wrong_types_of_args_exception( wta.whicharg, wta.expected.in(), wta.received.in() );
00266     }
00267     return 0; // not reached.
00268 }
00269 
00270 base::DataSourceBase::shared_ptr CorbaOperationCallerFactory::produceSend(const std::vector<base::DataSourceBase::shared_ptr>& args, ExecutionEngine* caller) const {
00271     corba::CAnyArguments_var nargs = new corba::CAnyArguments();
00272     nargs->length( args.size() );
00273     for (size_t i=0; i < args.size(); ++i ) {
00274         const types::TypeInfo* ti = args[i]->getTypeInfo();
00275         CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00276         if (!ctt)
00277         throw wrong_types_of_args_exception(i+1,"type known to CORBA transport", ti->getTypeName());
00278         DataSourceBase::shared_ptr tryout = ti->buildValue();
00279         ctt->updateAny(tryout, nargs[i]);
00280     }
00281     try {
00282         // will throw if wrong args.
00283         mfact->checkOperation(method.c_str(), nargs.inout() );
00284         // Will return a CSendHandle_var:
00285         DataSource<CSendHandle_var>::shared_ptr result = new ValueDataSource<CSendHandle_var>();
00286         return new ActionAliasDataSource<CSendHandle_var>(new CorbaOperationCallerCall(mfact.in(),method,args,caller, 0, result, false), result.get() );
00287     } catch ( corba::CNoSuchNameException& nsn ) {
00288         throw  name_not_found_exception( nsn.name.in() );
00289     } catch ( corba::CWrongNumbArgException& wa ) {
00290         throw  wrong_number_of_args_exception( wa.wanted, wa.received );
00291     } catch ( corba::CWrongTypeArgException& wta ) {
00292         throw  wrong_types_of_args_exception( wta.whicharg, wta.expected.in(), wta.received.in() );
00293     }
00294     return 0; // not reached.
00295 }
00296 
00297 base::DataSourceBase::shared_ptr CorbaOperationCallerFactory::produceHandle() const {
00298     // collect expects a handle of this type. Also send returns a CSendHandle_var, which can be copied into this handle object.
00299     return new ValueDataSource<CSendHandle_var>();
00300 }
00301 
00310 class CorbaOperationCallerCollect: public DataSource<SendStatus>
00311 {
00312     CSendHandle_var msh;
00313     std::vector<base::DataSourceBase::shared_ptr> margs;
00314     DataSource<bool>::shared_ptr misblocking;
00315     mutable SendStatus mss;
00316 public:
00317     CorbaOperationCallerCollect(CSendHandle_ptr sh,
00318                        std::vector<base::DataSourceBase::shared_ptr> const& args,
00319                        DataSource<bool>::shared_ptr isblocking)
00320     : msh( CSendHandle::_duplicate(sh)), margs(args), misblocking(isblocking), mss(SendFailure)
00321     {
00322     }
00323 
00324     ~CorbaOperationCallerCollect() {
00325         try {
00326             msh->dispose();
00327         } catch(...) {}
00328     }
00329 
00330     SendStatus value() const { return mss; }
00331 
00332     SendStatus const& rvalue() const { return mss; }
00333 
00334     SendStatus get() const {
00335         try {
00336             // only try to collect if we didn't do so before:
00337             if ( mss != SendSuccess ) {
00338                 corba::CAnyArguments_var nargs;
00339                 if ( misblocking->get() ) {
00340                     mss = SendStatus( static_cast<int>(msh->collect( nargs.out() ) ) - 1 );
00341                 } else {
00342                     mss = SendStatus( static_cast<int>(msh->collectIfDone( nargs.out() ) ) - 1 );
00343                 }
00344                 // only convert results when we got a success:
00345                 if (mss == SendSuccess) {
00346                     assert( nargs->length() ==  margs.size() );
00347                     for (size_t i=0; i < margs.size(); ++i ) {
00348                         const types::TypeInfo* ti = margs[i]->getTypeInfo();
00349                         CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00350                         assert( ctt );
00351                         ctt->updateFromAny( &nargs[i], margs[i] );
00352                     }
00353                 }
00354             }
00355             return mss;
00356         }  catch ( corba::CWrongNumbArgException& ) {
00357             return mss;
00358         } catch ( corba::CWrongTypeArgException& ) {
00359             return mss;
00360         }
00361     }
00362 
00363     DataSource<SendStatus>* clone() const { return new CorbaOperationCallerCollect(CSendHandle::_duplicate( msh.in() ), margs, misblocking); }
00364 
00365     virtual DataSource<SendStatus>* copy( std::map<const DataSourceBase*, DataSourceBase*>& alreadyCloned ) const {
00366         vector<DataSourceBase::shared_ptr> argcopy( margs.size() );
00367         unsigned int v=0;
00368         for (vector<DataSourceBase::shared_ptr>::iterator it = argcopy.begin(); it != argcopy.end(); ++it, ++v)
00369             argcopy[v] = (*it)->copy(alreadyCloned);
00370         return new CorbaOperationCallerCollect(CSendHandle::_duplicate( msh.in() ), argcopy, misblocking);
00371     }
00372 };
00373 
00374 
00375 base::DataSourceBase::shared_ptr CorbaOperationCallerFactory::produceCollect(const std::vector<base::DataSourceBase::shared_ptr>& args, internal::DataSource<bool>::shared_ptr blocking) const {
00376     unsigned int expected = mfact->getCollectArity(method.c_str());
00377     if (args.size() !=  expected + 1) {
00378         throw wrong_number_of_args_exception( expected + 1, args.size() );
00379     }
00380     // isolate and check CSendHandle
00381     std::vector<base::DataSourceBase::shared_ptr> cargs( ++args.begin(), args.end() );
00382     DataSource<CSendHandle_var>::shared_ptr ds = DataSource<CSendHandle_var>::narrow( args.begin()->get() );
00383     if (!ds) {
00384         throw wrong_types_of_args_exception(0,"CSendHandle_var",(*args.begin())->getTypeName() );
00385     }
00386     // check if args matches what CSendHandle expects.
00387     try {
00388         corba::CAnyArguments_var nargs = new corba::CAnyArguments();
00389         nargs->length( cargs.size() );
00390         for (size_t i=0; i < cargs.size(); ++i ) {
00391             const types::TypeInfo* ti = cargs[i]->getTypeInfo();
00392             CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00393             assert( ctt );
00394             DataSourceBase::shared_ptr tryout = ti->buildValue();
00395             ctt->updateAny(tryout, nargs[i]);
00396         }
00397         ds->get()->checkArguments( nargs.in() );
00398     } catch ( CWrongNumbArgException& wna) {
00399         throw wrong_number_of_args_exception(wna.wanted, wna.received);
00400     } catch ( CWrongTypeArgException& wta) {
00401         throw wrong_types_of_args_exception(wta.whicharg,wta.expected.in(), wta.received.in());
00402     }
00403     // All went well, produce collect DataSource:
00404     return new CorbaOperationCallerCollect( ds->get().in(),cargs, blocking);
00405 }
00406 
00407 #ifdef ORO_SIGNALLING_OPERATIONS
00408 Handle CorbaOperationCallerFactory::produceSignal(base::ActionInterface* func, const std::vector<base::DataSourceBase::shared_ptr>& args) const {
00409     log(Error) << "Can not attach Signal to remote Corba Operation '"<<method <<"'" <<endlog();
00410     return Handle();
00411 }
00412 #endif