PageRenderTime 71ms CodeModel.GetById 8ms app.highlight 59ms RepoModel.GetById 1ms app.codeStats 0ms

/core/externals/update-engine/Core/KSCheckAction.m

http://macfuse.googlecode.com/
Objective C | 197 lines | 127 code | 35 blank | 35 comment | 16 complexity | dd9ae674f5fa0e4b6930636cc3ef692a 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#import "KSCheckAction.h"
 16
 17#import "KSActionConstants.h"
 18#import "KSActionPipe.h"
 19#import "KSActionProcessor.h"
 20#import "KSFrameworkStats.h"
 21#import "KSPlistServer.h"
 22#import "KSTicket.h"
 23#import "KSTicketStore.h"
 24#import "KSUpdateCheckAction.h"
 25
 26
 27// The KSServer class used by this action is configurable. This variable holds
 28// the objc Class representing the KSServer subclass to use. This variable
 29// should not be directly accessed. Instead, the +serverClass class method
 30// should be used. That class method will return a default KSServer class if one
 31// is not set.
 32static Class gServerClass;  // Weak
 33
 34
 35@implementation KSCheckAction
 36
 37+ (id)actionWithTickets:(NSArray *)tickets params:(NSDictionary *)params
 38                 engine:(KSUpdateEngine *)engine {
 39  return [[[self alloc] initWithTickets:tickets params:params engine:engine]
 40           autorelease];
 41}
 42
 43+ (id)actionWithTickets:(NSArray *)tickets params:(NSDictionary *)params {
 44  return [[[self alloc] initWithTickets:tickets params:params] autorelease];
 45}
 46
 47+ (id)actionWithTickets:(NSArray *)tickets {
 48  return [[[self alloc] initWithTickets:tickets] autorelease];
 49}
 50
 51- (id)initWithTickets:(NSArray *)tickets params:(NSDictionary *)params
 52               engine:(KSUpdateEngine *)engine {
 53  if ((self = [super init])) {
 54    tickets_ = [tickets copy];
 55    params_ = [params retain];
 56    engine_ = [engine retain];
 57    updateInfos_ = [[NSMutableArray alloc] init];
 58    outOfBandData_ = [[NSMutableDictionary alloc] init];
 59  }
 60  return self;
 61}
 62
 63- (id)initWithTickets:(NSArray *)tickets params:(NSDictionary *)params {
 64  return [self initWithTickets:tickets params:params engine:nil];
 65}
 66
 67- (id)initWithTickets:(NSArray *)tickets {
 68  return [self initWithTickets:tickets params:nil];
 69}
 70
 71- (void)dealloc {
 72  [tickets_ release];
 73  [updateInfos_ release];
 74  [outOfBandData_ release];
 75  [params_ release];
 76  [engine_ release];
 77  [super dealloc];
 78}
 79
 80- (void)performAction {
 81  NSDictionary *tixMap = [tickets_ ticketsByURL];
 82  if (tixMap == nil) {
 83    GTMLoggerInfo(@"no tickets to check on.");
 84    [[self outPipe] setContents:nil];
 85    [[self processor] finishedProcessing:self successfully:YES];
 86    return;
 87  }
 88
 89  NSURL *url = nil;
 90  NSEnumerator *tixMapEnumerator = [tixMap keyEnumerator];
 91
 92  while ((url = [tixMapEnumerator nextObject])) {
 93    NSArray *tickets = [tixMap objectForKey:url];
 94    [[KSFrameworkStats sharedStats] incrementStat:kStatTickets
 95                                               by:[tickets count]];
 96
 97    // We don't want to check for products that are currently not installed, so
 98    // we need to filter the array of tickets to only those ticktes whose
 99    // existence checker indicates that they are currently installed.
100    // NSPredicate makes this very easy.
101    NSArray *filteredTickets =
102      [tickets filteredArrayUsingPredicate:
103       [NSPredicate predicateWithFormat:@"existenceChecker.exists == YES"]];
104
105    if ([filteredTickets count] == 0)
106      continue;
107
108    GTMLoggerInfo(@"filteredTickets = %@", filteredTickets);
109    [[KSFrameworkStats sharedStats] incrementStat:kStatValidTickets
110                                               by:[filteredTickets count]];
111
112    Class serverClass = [[self class] serverClass];
113    // Creates a concrete KSServer instance using the designated initializer
114    // declared on KSServer.  Pass along |engine_| so the server can call
115    // delegate methods, if it needs to.
116    KSServer *server = [[[serverClass alloc] initWithURL:url
117                                                  params:params_
118                                                  engine:engine_] autorelease];
119    KSAction *checker = [KSUpdateCheckAction checkerWithServer:server
120                                                       tickets:filteredTickets];
121    [[self subProcessor] enqueueAction:checker];
122  }
123
124  if ([[[self subProcessor] actions] count] == 0) {
125    GTMLoggerInfo(@"No checkers created.");
126    [[self processor] finishedProcessing:self successfully:YES];
127    return;
128  }
129
130  // Our output needs to be the aggregate of all our sub-action checkers' output
131  // For now, we'll just set our output to a dictionary holding
132  // |updateInfos_| and |outOfBandData_|.  When subactions complete,
133  // we'll add their output to these two structures.
134
135  [updateInfos_ removeAllObjects];
136  [outOfBandData_ removeAllObjects];
137  NSMutableDictionary *outPipeContents =
138    [NSDictionary dictionaryWithObjectsAndKeys:
139                  updateInfos_, KSActionUpdateInfosKey,
140                  outOfBandData_, KSActionOutOfBandDataKey,
141                  nil];
142  [[self outPipe] setContents:outPipeContents];
143
144  [[self subProcessor] startProcessing];
145}
146
147// KSActionProcessor callback method that will be called by our subProcessor
148- (void)processor:(KSActionProcessor *)processor
149   finishedAction:(KSAction *)action
150     successfully:(BOOL)wasOK {
151  [[KSFrameworkStats sharedStats] incrementStat:kStatChecks];
152  if (wasOK) {
153    // Get the checker's output contents and append it to our own output.
154    NSDictionary *checkerOutput = [[action outPipe] contents];
155
156    NSDictionary *oobData =
157      [checkerOutput objectForKey:KSActionOutOfBandDataKey];
158    NSURL *url = [checkerOutput objectForKey:KSActionServerURLKey];
159    if (oobData && url) {
160      [outOfBandData_ setObject:oobData forKey:[url description]];
161    }
162
163    NSArray *infos = [checkerOutput objectForKey:KSActionUpdateInfosKey];
164    if (infos) {
165      [updateInfos_ addObjectsFromArray:infos];
166    }
167
168    // See header comments about why this gets set to YES here.
169    wasSuccessful_ = YES;
170  } else {
171    [[KSFrameworkStats sharedStats] incrementStat:kStatFailedChecks];
172  }
173}
174
175// Overridden from KSMultiAction. Called by our subProcessor when it finishes.
176// We tell our parent processor that we succeeded if *any* of our subactions
177// succeeded.
178- (void)processingDone:(KSActionProcessor *)processor {
179  [[self processor] finishedProcessing:self successfully:wasSuccessful_];
180}
181
182@end
183
184
185@implementation KSCheckAction (Configuration)
186
187+ (Class)serverClass {
188  return gServerClass ? gServerClass : [KSPlistServer class];
189}
190
191+ (void)setServerClass:(Class)serverClass {
192  if (serverClass != Nil && ![serverClass isSubclassOfClass:[KSServer class]])
193    return;
194  gServerClass = serverClass;
195}
196
197@end