PageRenderTime 63ms CodeModel.GetById 1ms app.highlight 58ms RepoModel.GetById 1ms app.codeStats 0ms

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