Orocos Real-Time Toolkit  2.6.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         } catch ( corba::CCallError& e) {
00209             throw std::runtime_error(std::string(e.what.in()));
00210         }
00211     }
00212 
00213     ActionInterface* clone() const { return new CorbaOperationCallerCall(CService::_duplicate( mfact.in() ), mop, margs, mcaller, mctt, mresult, mdocall); }
00214 
00215     virtual ActionInterface* copy( std::map<const DataSourceBase*, DataSourceBase*>& alreadyCloned ) const {
00216         vector<DataSourceBase::shared_ptr> argcopy( margs.size() );
00217         unsigned int v=0;
00218         for (vector<DataSourceBase::shared_ptr>::iterator it = argcopy.begin(); it != argcopy.end(); ++it, ++v)
00219             argcopy[v] = (*it)->copy(alreadyCloned);
00220         return new CorbaOperationCallerCall(CService::_duplicate( mfact.in() ), mop, argcopy, mcaller, mctt, mresult->copy(alreadyCloned), mdocall);
00221     }
00222 };
00223 
00224 base::DataSourceBase::shared_ptr CorbaOperationCallerFactory::produce(const std::vector<base::DataSourceBase::shared_ptr>& args, ExecutionEngine* caller) const {
00225     corba::CAnyArguments_var nargs = new corba::CAnyArguments();
00226     nargs->length( args.size() );
00227 
00228     // this part is only done to feed to checkOperation() with some defaults.
00229     // We don't want to evaluate() the *real* data sources yet !
00230     for (size_t i=0; i < args.size(); ++i ) {
00231         const types::TypeInfo* ti = args[i]->getTypeInfo();
00232         CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00233         if (!ctt)
00234         throw wrong_types_of_args_exception(i+1,"type known to CORBA transport", ti->getTypeName());
00235         DataSourceBase::shared_ptr tryout = ti->buildValue();
00236         ctt->updateAny(tryout, nargs[i]);
00237     }
00238     // check argument types and produce:
00239     try {
00240         // will throw if wrong args.
00241         mfact->checkOperation(method.c_str(), nargs.in() );
00242         // convert returned any to local type:
00243         const types::TypeInfo* ti = this->getArgumentType(0);
00244         if ( ti ) {
00245             if ( ti != Types()->type("void") ) {
00246                 // create a method call object and a return value and let the former store results in the latter.
00247                 CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00248                 DataSourceBase::shared_ptr result = ti->buildValue();
00249                 // evaluate()/get() will cause the method to be called and remote return value will end up in result.
00250                 return ti->buildActionAlias(new CorbaOperationCallerCall(mfact.in(),method,args,caller, ctt, result, true), result );
00251             } else {
00252                 return new DataSourceCommand( new CorbaOperationCallerCall(mfact.in(),method,args,caller, 0, DataSourceBase::shared_ptr() , true) );
00253             }
00254         } else {
00255             // it's returning a type we don't know ! Return a DataSource<Any>
00256             DataSource<CORBA::Any_var>::shared_ptr result = new AnyDataSource( new CORBA::Any() );
00257             // todo Provide a ctt  implementation for 'CORBA::Any_var' such that the result is updated !
00258             // The result is only for dummy reasons used now, since no ctt is set, no updating will be done.
00259             return new ActionAliasDataSource<CORBA::Any_var>(new CorbaOperationCallerCall(mfact.in(),method,args,caller, 0, result, true), result.get() );
00260         }
00261     } catch ( corba::CNoSuchNameException& nsn ) {
00262         throw  name_not_found_exception( nsn.name.in() );
00263     } catch ( corba::CWrongNumbArgException& wa ) {
00264         throw  wrong_number_of_args_exception( wa.wanted, wa.received );
00265     } catch ( corba::CWrongTypeArgException& wta ) {
00266         throw  wrong_types_of_args_exception( wta.whicharg, wta.expected.in(), wta.received.in() );
00267     }
00268     return 0; // not reached.
00269 }
00270 
00271 base::DataSourceBase::shared_ptr CorbaOperationCallerFactory::produceSend(const std::vector<base::DataSourceBase::shared_ptr>& args, ExecutionEngine* caller) const {
00272     corba::CAnyArguments_var nargs = new corba::CAnyArguments();
00273     nargs->length( args.size() );
00274     for (size_t i=0; i < args.size(); ++i ) {
00275         const types::TypeInfo* ti = args[i]->getTypeInfo();
00276         CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00277         if (!ctt)
00278         throw wrong_types_of_args_exception(i+1,"type known to CORBA transport", ti->getTypeName());
00279         DataSourceBase::shared_ptr tryout = ti->buildValue();
00280         ctt->updateAny(tryout, nargs[i]);
00281     }
00282     try {
00283         // will throw if wrong args.
00284         mfact->checkOperation(method.c_str(), nargs.inout() );
00285         // Will return a CSendHandle_var:
00286         DataSource<CSendHandle_var>::shared_ptr result = new ValueDataSource<CSendHandle_var>();
00287         return new ActionAliasDataSource<CSendHandle_var>(new CorbaOperationCallerCall(mfact.in(),method,args,caller, 0, result, false), result.get() );
00288     } catch ( corba::CNoSuchNameException& nsn ) {
00289         throw  name_not_found_exception( nsn.name.in() );
00290     } catch ( corba::CWrongNumbArgException& wa ) {
00291         throw  wrong_number_of_args_exception( wa.wanted, wa.received );
00292     } catch ( corba::CWrongTypeArgException& wta ) {
00293         throw  wrong_types_of_args_exception( wta.whicharg, wta.expected.in(), wta.received.in() );
00294     }
00295     return 0; // not reached.
00296 }
00297 
00298 base::DataSourceBase::shared_ptr CorbaOperationCallerFactory::produceHandle() const {
00299     // collect expects a handle of this type. Also send returns a CSendHandle_var, which can be copied into this handle object.
00300     return new ValueDataSource<CSendHandle_var>();
00301 }
00302 
00311 class CorbaOperationCallerCollect: public DataSource<SendStatus>
00312 {
00313     CSendHandle_var msh;
00314     std::vector<base::DataSourceBase::shared_ptr> margs;
00315     DataSource<bool>::shared_ptr misblocking;
00316     mutable SendStatus mss;
00317 public:
00318     CorbaOperationCallerCollect(CSendHandle_ptr sh,
00319                        std::vector<base::DataSourceBase::shared_ptr> const& args,
00320                        DataSource<bool>::shared_ptr isblocking)
00321     : msh( CSendHandle::_duplicate(sh)), margs(args), misblocking(isblocking), mss(SendFailure)
00322     {
00323     }
00324 
00325     ~CorbaOperationCallerCollect() {
00326         try {
00327             msh->dispose();
00328         } catch(...) {}
00329     }
00330 
00331     SendStatus value() const { return mss; }
00332 
00333     SendStatus const& rvalue() const { return mss; }
00334 
00335     SendStatus get() const {
00336         try {
00337             // only try to collect if we didn't do so before:
00338             if ( mss != SendSuccess ) {
00339                 corba::CAnyArguments_var nargs;
00340                 if ( misblocking->get() ) {
00341                     mss = SendStatus( static_cast<int>(msh->collect( nargs.out() ) ) - 1 );
00342                 } else {
00343                     mss = SendStatus( static_cast<int>(msh->collectIfDone( nargs.out() ) ) - 1 );
00344                 }
00345                 // only convert results when we got a success:
00346                 if (mss == SendSuccess) {
00347                     assert( nargs->length() ==  margs.size() );
00348                     for (size_t i=0; i < margs.size(); ++i ) {
00349                         const types::TypeInfo* ti = margs[i]->getTypeInfo();
00350                         CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00351                         assert( ctt );
00352                         ctt->updateFromAny( &nargs[i], margs[i] );
00353                     }
00354                 }
00355             }
00356             return mss;
00357         }  catch ( corba::CWrongNumbArgException& ) {
00358             return mss;
00359         } catch ( corba::CWrongTypeArgException& ) {
00360             return mss;
00361         } catch ( corba::CCallError& e) {
00362             throw std::runtime_error(std::string(e.what.in()));
00363         }
00364     }
00365 
00366     DataSource<SendStatus>* clone() const { return new CorbaOperationCallerCollect(CSendHandle::_duplicate( msh.in() ), margs, misblocking); }
00367 
00368     virtual DataSource<SendStatus>* copy( std::map<const DataSourceBase*, DataSourceBase*>& alreadyCloned ) const {
00369         vector<DataSourceBase::shared_ptr> argcopy( margs.size() );
00370         unsigned int v=0;
00371         for (vector<DataSourceBase::shared_ptr>::iterator it = argcopy.begin(); it != argcopy.end(); ++it, ++v)
00372             argcopy[v] = (*it)->copy(alreadyCloned);
00373         return new CorbaOperationCallerCollect(CSendHandle::_duplicate( msh.in() ), argcopy, misblocking);
00374     }
00375 };
00376 
00377 
00378 base::DataSourceBase::shared_ptr CorbaOperationCallerFactory::produceCollect(const std::vector<base::DataSourceBase::shared_ptr>& args, internal::DataSource<bool>::shared_ptr blocking) const {
00379     unsigned int expected = mfact->getCollectArity(method.c_str());
00380     if (args.size() !=  expected + 1) {
00381         throw wrong_number_of_args_exception( expected + 1, args.size() );
00382     }
00383     // isolate and check CSendHandle
00384     std::vector<base::DataSourceBase::shared_ptr> cargs( ++args.begin(), args.end() );
00385     DataSource<CSendHandle_var>::shared_ptr ds = DataSource<CSendHandle_var>::narrow( args.begin()->get() );
00386     if (!ds) {
00387         throw wrong_types_of_args_exception(0,"CSendHandle_var",(*args.begin())->getTypeName() );
00388     }
00389     // check if args matches what CSendHandle expects.
00390     try {
00391         corba::CAnyArguments_var nargs = new corba::CAnyArguments();
00392         nargs->length( cargs.size() );
00393         for (size_t i=0; i < cargs.size(); ++i ) {
00394             const types::TypeInfo* ti = cargs[i]->getTypeInfo();
00395             CorbaTypeTransporter* ctt = dynamic_cast<CorbaTypeTransporter*>( ti->getProtocol(ORO_CORBA_PROTOCOL_ID) );
00396             assert( ctt );
00397             DataSourceBase::shared_ptr tryout = ti->buildValue();
00398             ctt->updateAny(tryout, nargs[i]);
00399         }
00400         ds->get()->checkArguments( nargs.in() );
00401     } catch ( CWrongNumbArgException& wna) {
00402         throw wrong_number_of_args_exception(wna.wanted, wna.received);
00403     } catch ( CWrongTypeArgException& wta) {
00404         throw wrong_types_of_args_exception(wta.whicharg,wta.expected.in(), wta.received.in());
00405     }
00406     // All went well, produce collect DataSource:
00407     return new CorbaOperationCallerCollect( ds->get().in(),cargs, blocking);
00408 }
00409 
00410 #ifdef ORO_SIGNALLING_OPERATIONS
00411 Handle CorbaOperationCallerFactory::produceSignal(base::ActionInterface* func, const std::vector<base::DataSourceBase::shared_ptr>& args) const {
00412     log(Error) << "Can not attach Signal to remote Corba Operation '"<<method <<"'" <<endlog();
00413     return Handle();
00414 }
00415 #endif