]> err.no Git - sope/blob - sope-appserver/NGObjWeb/WOComponentRequestHandler.m
fixed an issue with lowercase 'webobject name' tags
[sope] / sope-appserver / NGObjWeb / WOComponentRequestHandler.m
1 /*
2   Copyright (C) 2000-2005 SKYRIX Software AG
3
4   This file is part of SOPE.
5
6   SOPE is free software; you can redistribute it and/or modify it under
7   the terms of the GNU Lesser General Pulic License as published by the
8   Free Software Foundation; either version 2, or (at your option) any
9   later version.
10
11   SOPE is distributed in the hope that it will be useful, but WITHOUT ANY
12   WARRANTY; without even the implied warranty of MERCHANTABILITY or
13   FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
14   License for more details.
15
16   You should have received a copy of the GNU Lesser General Public
17   License along with SOPE; see the file COPYING.  If not, write to the
18   Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
19   02111-1307, USA.
20 */
21
22 #include "WOComponentRequestHandler.h"
23 #include "WORequestHandler+private.h"
24 #include "WOContext+private.h"
25 #include <NGObjWeb/WOApplication.h>
26 #include <NGObjWeb/WORequest.h>
27 #include <NGObjWeb/WOResponse.h>
28 #include <NGObjWeb/WOSession.h>
29 #include <NGObjWeb/WOComponent.h>
30 #include "common.h"
31
32 @interface WOApplication(Privates)
33 - (WOSession *)_initializeSessionInContext:(WOContext *)_ctx;
34 - (void)_setCurrentContext:(WOContext *)_ctx;
35 @end
36
37 @interface WORequestHandler(URI)
38 - (BOOL)doesRejectFavicon;
39 @end
40
41 @implementation WOComponentRequestHandler
42
43 + (int)version {
44   return [super version] + 0 /* 2 */;
45 }
46 + (void)initialize {
47   NSAssert2([super version] == 2,
48             @"invalid superclass (%@) version %i !",
49             NSStringFromClass([self superclass]), [super version]);
50 }
51
52 - (WOResponse *)restoreSessionWithID:(NSString *)_sid
53   inContext:(WOContext *)_ctx
54 {
55   WOApplication *app;
56   WOSession *session;
57   
58   app = [WOApplication application];
59   if (_sid == nil) {
60     // invalid session ID (or no session-ID ?!, which is no error ?) */
61     return [app handleSessionRestorationErrorInContext:_ctx];
62   }
63   
64   if ((session = [app restoreSessionWithID:_sid inContext:_ctx]) != nil) {
65     /* awake restored session */
66     [_ctx setSession:session];
67     [session _awakeWithContext:_ctx];
68     
69     [session awake];
70     return nil;
71   }
72   
73   return [app handleSessionRestorationErrorInContext:_ctx];
74 }
75
76 /*
77   The request handler path of a component URI looks like this:
78
79     sessionID/componentName/contextID/elementID/instance/server
80 */
81
82 - (WOResponse *)handleRequest:(WORequest *)_request {
83   // TODO: this should be integrated into the WORequestHandler default
84   //       mechanism
85   NSString      *sessionID        = nil;
86   WOApplication *application      = nil;
87   WOContext     *context;
88   WOResponse    *response         = nil;
89   WOSession     *session          = nil;
90   WOComponent   *component        = nil;
91   BOOL          isLocked          = NO;
92   NSString      *handlerPath      = nil;
93
94   if (_request == nil) return nil;
95   
96   if ([self doesRejectFavicon] && [[_request uri] isNotNull]) {
97     // TODO: code copied from WORequestHandler ...
98     if ([@"/favicon.ico" isEqualToString:[_request uri]]) {
99       response = [WOResponse responseWithRequest:_request];
100       [response setStatus:404 /* not found */];
101       [self debugWithFormat:@"rejected favicon request: %@", [_request uri]];
102       return response;
103     }
104   }
105   
106   application = [WOApplication application];
107   handlerPath = [_request requestHandlerPath];
108   
109 #if 0
110   [self logWithFormat:@"[component request handler] path: '%@'", handlerPath];
111 #endif
112
113   if (![application allowsConcurrentRequestHandling]) {
114     [application lockRequestHandling];
115     isLocked = YES;
116   }
117   
118   context = [WOContext contextWithRequest:_request];
119   [application _setCurrentContext:context];
120   
121   /*
122     parse handler path (URL)
123
124     The format is:
125
126       session/context.element-id
127   */
128   if ([handlerPath isNotEmpty]) {
129     NSArray *spath = [_request requestHandlerPathArray];
130     
131     if ([spath count] > 1)
132       [context setRequestSenderID:[spath objectAtIndex:1]];
133     if ([spath isNotEmpty])
134       sessionID = [spath objectAtIndex:0];
135   }
136   
137   if (![sessionID isNotEmpty])
138     sessionID = [application sessionIDFromRequest:_request];
139   
140 #if 1
141   [self logWithFormat:@"%s: made context %@ (cid=%@, sn=%@) ..",
142           __PRETTY_FUNCTION__, context, [context contextID], sessionID];
143 #endif
144   
145   [application awake];
146   
147   /* restore or create session */
148   if ([sessionID isNotEmpty]) {
149     if ((response = [self restoreSessionWithID:sessionID inContext:context]))
150       session = nil;
151     else {
152       /* 
153          Note: this creates a _new_ session if the restoration handler did not
154                return a response! We check that below by comparing the session
155                IDs.
156       */
157       session = [context session];
158     }
159     
160     [self debugWithFormat:@"restored session (id=%@): %@", sessionID, session];
161     
162     if (session && (![sessionID isEqualToString:[session sessionID]])) {
163       [self errorWithFormat:@"session-ids do not match (%@ vs %@)",
164               sessionID, [session sessionID]];
165     }
166     
167     if ([session isNotNull]) {
168       NSString *eid;
169       
170       /*
171          only try to restore a page if we still have the same session and if
172          the request contains an element-id (eg if we reconnect to the main
173          URL we do not have an element-id
174       */
175       eid = [context currentElementID];
176       if ([sessionID isEqualToString:[session sessionID]] && eid != nil) {
177         /* awake stored page from "old" session */
178         component = [session restorePageForContextID:eid];
179         
180         if (component == nil) {
181           [self logWithFormat:@"could not restore component from session: %@",
182                 session];
183           response = [application handlePageRestorationErrorInContext:context];
184         }
185       }
186       else /* a new session was created (but no restore-error response ret.) */
187         component = [application pageWithName:nil inContext:context];
188     }
189     else if (response == nil) {
190       [[WOApplication application] warnWithFormat:
191                                      @"got no session restoration error, "
192                                      @"but missing session!"];
193     }
194   }
195   else {
196     /* create new session */
197     session = [application _initializeSessionInContext:context];
198     if ([session isNotNull]) {
199       /* awake created session */
200       [session awake];
201       component = [application pageWithName:nil inContext:context];
202     }
203     else
204       response = [application handleSessionCreationErrorInContext:context];
205   }
206   
207   if ((session != nil) && (component != nil) && (response == nil)) {
208     WOComponent *newPage = nil;
209
210     [[session retain] autorelease];
211     
212 #if DEBUG
213     NSAssert(application, @"missing application object ..");
214     NSAssert(session,     @"missing session object ..");
215 #endif
216     
217     /* set request page in context */
218     [context setPage:component];
219     
220     /* run take-values phase */
221     [application takeValuesFromRequest:_request inContext:context];
222     
223     /* run invoke-action phase */
224     newPage = [application invokeActionForRequest:_request inContext:context];
225     
226     /* process resulting page */
227     if (newPage == nil) {
228       if ((newPage = [context page]) == nil) {
229         newPage = [application pageWithName:nil inContext:context];
230         [context setPage:newPage];
231       }
232     }
233     else if ([newPage isKindOfClass:[WOComponent class]])
234       [context setPage:newPage];
235
236     [self debugWithFormat:@"%s: new page: %@", __PRETTY_FUNCTION__, newPage];
237     
238     /* generate response */
239     
240     response = [self generateResponseForComponent:[context page]
241                      inContext:context
242                      application:application];
243   }
244   else {
245     [self warnWithFormat:@"%s: did not enter request/response transaction ...",
246             __PRETTY_FUNCTION__];
247   }
248   
249   /* tear down */
250
251   /* sleep objects */
252   [context sleepComponents];
253   [session sleep];
254   
255   /* save objects */
256   
257   if (session != nil) {
258     if ([context savePageRequired])
259       [session savePage:[context page]];
260     
261     [self debugWithFormat:@"saving session %@", [session sessionID]];
262     
263     if ([session storesIDsInCookies]) {
264       [self debugWithFormat:@"add cookie to session: %@", session];
265       [self addCookiesForSession:session
266             toResponse:response
267             inContext:context];
268     }
269     
270 #if 1 // TODO: explain that
271     [application saveSessionForContext:context];
272 #else
273     [self saveSession:session
274           inContext:context
275           withResponse:response
276           application:application];
277 #endif
278   }
279   
280   [application sleep];
281   
282   /* locking */
283   
284   if (isLocked) {
285     [application unlockRequestHandling];
286     isLocked = NO;
287   }
288   
289   [application _setCurrentContext:nil];
290   return response;
291 }
292
293 @end /* WOComponentRequestHandler */