Google APIs Client Library for C++
service_request_pager.h
Go to the documentation of this file.
00001 /*
00002  * \copyright Copyright 2013 Google Inc. All Rights Reserved.
00003  * \license @{
00004  *
00005  * Licensed under the Apache License, Version 2.0 (the "License");
00006  * you may not use this file except in compliance with the License.
00007  * You may obtain a copy of the License at
00008  *
00009  *     http://www.apache.org/licenses/LICENSE-2.0
00010  *
00011  * Unless required by applicable law or agreed to in writing, software
00012  * distributed under the License is distributed on an "AS IS" BASIS,
00013  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
00014  * See the License for the specific language governing permissions and
00015  * limitations under the License.
00016  *
00017  * @}
00018  */
00019 // A service request pager acts as a high level iterator for paging
00020 // through results. Each page involves a round-trip request the the server.
00021 
00022 #ifndef APISERVING_CLIENTSCPP_SERVICE_SERVICE_REQUEST_PAGER_H_
00023 #define APISERVING_CLIENTSCPP_SERVICE_SERVICE_REQUEST_PAGER_H_
00024 
00025 #include "googleapis/base/macros.h"
00026 #include "googleapis/base/scoped_ptr.h"
00027 #include "googleapis/client/transport/http_response.h"
00028 #include "googleapis/client/util/status.h"
00029 #include "googleapis/strings/strcat.h"
00030 #include "googleapis/strings/stringpiece.h"
00031 #include "googleapis/util/status.h"
00032 namespace googleapis {
00033 
00034 namespace client {
00035 
00036 class ClientServiceRequest;
00037 class HttpRequest;
00038 
00039 /*
00040  * Base class for component that pages through REST results.
00041  * @ingroup ClientServiceLayer
00042  *
00043  * This class is abstract requiring ExecuteNextPage to be implemented
00044  * to instruct the pager how to specify (and determine) the next page.
00045  *
00046  * Normally the concrete ServiceRequestPager is used.
00047  */
00048 class BaseServiceRequestPager {
00049  public:
00050   /*
00051    * Standard constructor.
00052    *
00053    * @param[in] request A reference to a prototype request used to fetch
00054    *                    the next page. Ownership is retained by the caller.
00055    */
00056   explicit BaseServiceRequestPager(ClientServiceRequest* request);
00057 
00058   /*
00059    * Standard destructor.
00060    */
00061   virtual ~BaseServiceRequestPager();
00062 
00063   /*
00064    * Returns the current page request.
00065    *
00066    * @return Ownership is retained by this instance.
00067    */
00068   ClientServiceRequest* request() { return request_; }
00069 
00070   /*
00071    * Returns the current page response.
00072    *
00073    * @return Ownership is retained by this instance.
00074    */
00075   HttpResponse* http_response()   { return &http_response_; }
00076 
00077   /*
00078    * Determine if this was the last known page.
00079    * @return true if we are done, false if not.
00080    *         We might still be done even if false is returned if the end
00081    *         was on a page boundary.
00082    */
00083   bool is_done() const { return done_; }
00084 
00085   /*
00086    * Fetch the next page.
00087    *
00088    * @return true if we could fetch another page, false if we are done.
00089    */
00090   bool NextPage();
00091 
00092   /*
00093    * Resets the pager back to the start.
00094    *
00095    * The next iteration may be different than the previous one depending
00096    * on the backend service.
00097    */
00098   void Reset();
00099 
00100  protected:
00101   /*
00102    * Does the actual messaging to the service to get the next page.
00103    *
00104    * @return ok or reason for failure.
00105    */
00106   virtual util::Status ExecuteNextPage() = 0;
00107 
00108   /*
00109    * Returns the token parameter to use when fetching the next page.
00110    */
00111   const string& next_page_token() const { return next_page_token_; }
00112 
00113   /*
00114    * Sets the [scalar] request token identifying the next desired page.
00115    *
00116    * This is for service APIs that use scalar token values.
00117    *
00118    * @param[in] token Specifies the desired page.
00119    * @see Reset
00120    */
00121   void set_next_page_token(int64 token) {
00122     if (token == 0) {
00123       set_next_page_token("");
00124     } else {
00125       set_next_page_token(StrCat("", token));
00126     }
00127   }
00128 
00129   /*
00130    * Sets the [string] request token identifying the next desired page.
00131    *
00132    * This is for service APIs that use string token values.
00133    *
00134    * @param[in] token Specifies the desired page.
00135    * @see Reset
00136    */
00137   void set_next_page_token(const StringPiece& token) {
00138     done_ = token.empty();
00139     next_page_token_ = token.as_string();
00140   }
00141 
00142  private:
00143   HttpResponse http_response_;
00144   ClientServiceRequest* request_;
00145   string next_page_token_;
00146 
00147   bool done_;
00148 
00149   DISALLOW_COPY_AND_ASSIGN(BaseServiceRequestPager);
00150 };
00151 
00152 /*
00153  * A pager over referenced REST APIs having a standard paging interface.
00154  * @ingroup ClientServiceLayer
00155  *
00156  * This template relies on the existence of REQUEST.set_page_token and
00157  * RESPONSE.get_next_page_token methods to control the page iteration.
00158  *
00159  * This class does not own the request or data objects. See the
00160  * EncapsulatedServiceRequestPager as a variant that adds memory management.
00161  *
00162  * @tparam REQUEST must be a subclass of ClientServiceRequest
00163  *                  and have a set_page_token method.
00164  * @tparam DATA must be a subclass of SerializableJson and have a
00165  *               get_next_page_token method.
00166  */
00167 template<class REQUEST, class DATA>
00168 class ServiceRequestPager : public BaseServiceRequestPager {
00169  public:
00170   /*
00171    * Standard constructor.
00172    *
00173    * @param[in] request The prototype request used to fetch pages.
00174    *                    The caller retains ownershp.
00175    * @param[in] page_data_storage Holds the underlying response data returned
00176    *            for the last requested page. The claeer retains ownership.
00177    */
00178   ServiceRequestPager(REQUEST* request, DATA* page_data_storage)
00179     : BaseServiceRequestPager(request), page_data_storage_(page_data_storage) {
00180   }
00181 
00182   /*
00183    * Standard destructor.
00184    */
00185   virtual ~ServiceRequestPager() {}
00186 
00187   /*
00188    * Returns the current page data.
00189    *
00190    * @return Ownership is retained by this instance as far at the caller is
00191    *         concerned.
00192    */
00193   DATA* data() { return page_data_storage_; }
00194 
00195   /*
00196    * Returns the current page request.
00197    *
00198    * @return Ownership is retained by this instance as far at the caller is
00199    *         concerned.
00200    */
00201   REQUEST* request() {
00202     return static_cast<REQUEST*>(BaseServiceRequestPager::request());
00203   }
00204 
00205   /*
00206    * Fetches the next page, if any.
00207    *
00208    * To distinguish the difference between a failure and no more pages,
00209    * check the http_response()->http_status().
00210    *
00211    * @return false on failure or when there are no more pages.
00212    */
00213   virtual util::Status ExecuteNextPage() {
00214     // This method is called by the base class which guards with is_done
00215     // so we dont need to check here. But we'll do so anyway just to be
00216     // sure it didnt get here through some other route.
00217     if (is_done()) {
00218       return StatusOutOfRange("Finished Paging");
00219     }
00220 
00221     if (next_page_token().empty()) {
00222       request()->clear_page_token();
00223     } else {
00224       request()->set_page_token(next_page_token());
00225     }
00226 
00227     util::Status status =
00228           request()->mutable_http_request()->PrepareToReuse();
00229     if (!status.ok()) return status;
00230 
00231     status = request()->ExecuteAndParseResponse(page_data_storage_);
00232     if (!status.ok()) return status;
00233 
00234     set_next_page_token(page_data_storage_->get_next_page_token());
00235     return status;
00236   }
00237 
00238  private:
00239   DATA* page_data_storage_;
00240 
00241   DISALLOW_COPY_AND_ASSIGN(ServiceRequestPager);
00242 };
00243 
00244 /*
00245  * A ServiceRequestPager that owns the request and data objects.
00246  * @ingroup ClientServiceLayer
00247  *
00248  * The request instance still needs to be injected since requests do not have
00249  * standard constructors.
00250  */
00251 template<class REQUEST, class DATA>
00252 class EncapsulatedServiceRequestPager
00253   : public ServiceRequestPager<REQUEST, DATA> {
00254  public:
00255   /*
00256    * Standard constructor
00257    *
00258    * @param[in] request The request prototype used to ask for pages.
00259    */
00260   explicit EncapsulatedServiceRequestPager(REQUEST* request)
00261       : ServiceRequestPager<REQUEST, DATA>(request, DATA::New()) {
00262     request_.reset(request);
00263     data_storage_.reset(ServiceRequestPager<REQUEST, DATA>::data());
00264   }
00265 
00266   /*
00267    * Standard destructor.
00268    */
00269   virtual ~EncapsulatedServiceRequestPager() {}
00270 
00271  private:
00272   scoped_ptr<REQUEST> request_;    // access through base class
00273   scoped_ptr<DATA> data_storage_;  // access through base class
00274 
00275   DISALLOW_COPY_AND_ASSIGN(EncapsulatedServiceRequestPager);
00276 };
00277 
00278 }  // namespace client
00279 
00280 } // namespace googleapis
00281 #endif  // APISERVING_CLIENTSCPP_SERVICE_SERVICE_REQUEST_PAGER_H_
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines