/core/externals/update-engine/Samples/HelloEngine/HelloEngine.m

http://macfuse.googlecode.com/ · Objective C · 264 lines · 72 code · 42 blank · 150 comment · 13 complexity · 714a00bdc7d28b304884028ed7d75d98 MD5 · raw file

  1. // Copyright 2008 Google Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Welcome to HelloEngine, a minimal, but functional, Update Engine
  15. // program. See the README.html file for build and run instructions.
  16. //
  17. // Visit the Update Engine project page at
  18. // http://code.google.com/p/update-engine for more information.
  19. //
  20. // When using Update Engine, you first need to create a ticket
  21. // store that contains one or more "tickets". HelloEngine creates a
  22. // single ticket based on its command-line arguments and puts the
  23. // ticket into an in-memory ticket store. You can also load and save
  24. // tickets in the file system.
  25. //
  26. // So, what's in a ticket? It's a set of information that identifies
  27. // a product, along with a server URL which identifies the location
  28. // that the information that says "yes, this product needs to be
  29. // updated" can be found. Update Engine was designed to be flexible
  30. // in getting this information, but typically you'll use a
  31. // KSPlistServer to load a property list from a remote host (or from
  32. // the file system), which just happens to be the default. If you
  33. // need more sophisticated processing you can create a server subclass
  34. // that passes product and version information to a server and then
  35. // receives a response with the update info.
  36. //
  37. // In particular, the ticket contains:
  38. // * The productID : This is a key into the update information, used to
  39. // distinguish this product from all the others.
  40. // Usually you'll use a mac-style BundleID
  41. // (e.g. com.google.flurbage) since it's nice and
  42. // human readable. UUID/GUIDs are other popular
  43. // choices for product ids. If you're using the
  44. // KSPlistServer (the default), this product ID is
  45. // used during the evaluation of its rules, after
  46. // the plist has been downloaded.
  47. //
  48. // * Version number : A version number to use for the update check.
  49. // Although version numbers are usually dotted quads,
  50. // like 1.0.23.42, there is no strict requirement about
  51. // the format of a version number. Update Engine never
  52. // attempts to interpret the version number itself.
  53. //
  54. // * An Existence Checker : There's no need to actually do an update
  55. // check if there's no actual product in the file
  56. // system to update, since the user may have thrown
  57. // away your application. (Bummer, we know). An
  58. // existence checker is an Update Engine object that
  59. // evaluates to YES or NO if the given product
  60. // exists. If you know that the product already
  61. // exists, like you're running Update Engine directly
  62. // from your application or as a tool living in an
  63. // application bundle, you can use
  64. // +[KSExistenceChecker trueChecker], otherwise
  65. // you'll want to use one of the other existence
  66. // checkers, like KSPathExistenceChecker to look
  67. // somewhere in the file system,
  68. // KSLaunchServicesExistenceChecker to query the
  69. // launch services database, or even ask Spotlight
  70. // with KSSpotlightExistenceChecker.
  71. //
  72. // * A server URL : This is where to find the update information.
  73. // We're using KSPlistServer, so this is a URL
  74. // to a website which returns a property list, or it
  75. // could be a file:// URL to a pre-canned response.
  76. //
  77. // Once the ticket is created, it gets wrapped in a KSTicketStore,
  78. // which holds a collection of tickets. HelloEngine just uses one
  79. // ticket, but you're welcome to use more. The server communication
  80. // subclasses could bundle all of the tickets into one request to a
  81. // server, or it may make a sequence of requests. But from the point
  82. // of view of users of the Engine, it Just Happens.
  83. //
  84. // The ticket store is given to a new instance of KSUpdateEngine,
  85. // which drives all of the update machinery. The KSUpdateEngine is
  86. // told to update all the products in the ticket store, assuming that
  87. // they pass the existence check and the server says "yeah, go ahead
  88. // and update them."
  89. //
  90. // There's an optional delegate you can hang off of KSUpdateEngine
  91. // which will get called back at various interesting times during the
  92. // update process, giving you information on different aspects of the
  93. // update, as well as letting you control the Engine's behavior.
  94. // HelloEngine is just interested in seeing if the update finished
  95. // successfully or not.
  96. //
  97. // Here's a sample usage (all one line)
  98. // HelloEngine -productID com.google.HelloEngineTest
  99. // -version 1.2
  100. // -serverURL file:///tmp/ServerResponse.plist
  101. #import <Foundation/Foundation.h>
  102. // Let us do some logging.
  103. #import "GTMLogger.h"
  104. #import "KSUpdateEngine.h"
  105. // Utility functions.
  106. static KSTicketStore *TicketStoreFromDefaults(void);
  107. static void PrintUsage(void);
  108. // HelloDelegate is an Update Engine delegate.
  109. //
  110. // There are a bunch of delegate methods you can use to customize the
  111. // Update Engine behavior. Here we're just implementing the "engine has
  112. // finished" method to set a flag if the update succeeded.
  113. //
  114. @interface HelloDelegate : NSObject {
  115. BOOL updateSucceded_;
  116. }
  117. // Was the update a successful one?
  118. - (BOOL)updateSucceded;
  119. @end // HelloDelegate
  120. // And so it begins...
  121. //
  122. int main(int argc, const char *argv[]) {
  123. NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  124. // First, get the update information from the command line via
  125. // user defaults as an in-memory ticket store with a single ticket.
  126. KSTicketStore *ticketStore = TicketStoreFromDefaults();
  127. if (ticketStore == nil) goto bailout;
  128. // Then make an engine and tell it to run, checking for updates
  129. // using the ticket in the ticket store. Use a HelloDelegate object
  130. // to print the final success/failure result.
  131. HelloDelegate *helloDelegate = [[[HelloDelegate alloc] init] autorelease];
  132. KSUpdateEngine *vroom;
  133. vroom = [KSUpdateEngine engineWithTicketStore:ticketStore
  134. delegate:helloDelegate];
  135. // Start the wheels churning.
  136. [vroom updateAllProducts];
  137. // Give Update Engine some love by spinning the run loop,
  138. // polling until it's done.
  139. while ([vroom isUpdating]) {
  140. NSDate *spin = [NSDate dateWithTimeIntervalSinceNow:1];
  141. [[NSRunLoop currentRunLoop] runUntilDate:spin];
  142. }
  143. // Have the last word be a success / failure message.
  144. if ([helloDelegate updateSucceded]) {
  145. GTMLoggerInfo(@"Update Succeeded");
  146. } else {
  147. GTMLoggerInfo(@"Update Failed");
  148. }
  149. // Clean up and run away.
  150. [pool release];
  151. bailout:
  152. return EXIT_SUCCESS;
  153. } // main
  154. // Inform the user the syntax for this program. It's pretty minimal.
  155. //
  156. static void PrintUsage(void) {
  157. fprintf(stderr, "HelloEngine -productID com.some.product\n"
  158. "-version 1.2.3.4\n"
  159. "-serverURL http://example.com/product/check\n");
  160. } // PrintUsage
  161. // Flags on the command line can be read via NSUserDefaults. This saves
  162. // us the pain of pulling apart the command line ourselves, and also
  163. // allows the developer / user / tester to set semipermanent values
  164. // with "defaults write" and not have to supply them on the command line.
  165. //
  166. static KSTicketStore *TicketStoreFromDefaults(void) {
  167. NSUserDefaults *defs = [NSUserDefaults standardUserDefaults];
  168. // This is the identifier for the product. It can be whatever you
  169. // want, since it's just used as a name.
  170. // Usually you'll use the bundle ID
  171. // for the application, since it's already nice and human-readable.
  172. NSString *productID = [defs stringForKey:@"productID"];
  173. // This is the current version of the product. Update Engine will
  174. // use this version when making update decisions. Depending on the
  175. // server subclass being used, this might be sent to the server, or
  176. // the version number might be compared to a version number in
  177. // a downloaded plist file.
  178. NSString *version = [defs stringForKey:@"version"];
  179. // This is the URL to hit for the update check. Update Engine
  180. // defaults to using a KSPlistServer, so this URL should return a
  181. // plist. It's OK to use a file in the file system (file:// URLS)
  182. // or to serve a static file from a website. There's no need to
  183. // have a database-backed scalable monstrosity on the back-end before
  184. // you get started.
  185. NSString *serverURLString = [defs stringForKey:@"serverURL"];
  186. NSURL *serverURL = nil;
  187. if (serverURLString != nil) serverURL = [NSURL URLWithString:serverURLString];
  188. // Make sure everything has been provided. If not, tell the user
  189. // what the syntax is, and bail out.
  190. if (productID == nil || version == nil || serverURL == nil) {
  191. PrintUsage();
  192. return nil;
  193. }
  194. // To keep things simple, use a trueChecker so that the update will
  195. // always run.
  196. KSExistenceChecker *existenceChecker;
  197. existenceChecker = [KSExistenceChecker trueChecker];
  198. // Create a ticket that describes the product that might need updating.
  199. KSTicket *ticket;
  200. ticket = [KSTicket ticketWithProductID:productID
  201. version:version
  202. existenceChecker:existenceChecker
  203. serverURL:serverURL];
  204. // Wrap the ticket in a ticket store. Ticket stores are just collections
  205. // of tickets, and are the update engine's
  206. KSTicketStore *ticketStore = [[[KSMemoryTicketStore alloc] init] autorelease];
  207. [ticketStore storeTicket:ticket];
  208. return ticketStore;
  209. } // TicketStoreFromDefaults
  210. @implementation HelloDelegate
  211. - (void)engine:(KSUpdateEngine *)engine
  212. finished:(KSUpdateInfo *)updateInfo
  213. wasSuccess:(BOOL)wasSuccess
  214. wantsReboot:(BOOL)wantsReboot {
  215. GTMLoggerInfo(@"WOOP! Finished! %@ %d %d",
  216. updateInfo, wasSuccess, wantsReboot);
  217. updateSucceded_ = wasSuccess;
  218. } // finished
  219. - (BOOL)updateSucceded {
  220. return updateSucceded_;
  221. } // updateSucceded
  222. @end // HelloDelegate