+2004-10-23 Helge Hess <helge.hess@opengroupware.org>
+
+ * v4.3.74
+
+ * WODirectAction.m: ignore requests on unbound KVC keys on non-lF
+ libraries
+
+ * SoObjects/SoObjectXmlRpcDispatcher.m: do not call -setUserInfo: to
+ annotate NSException's on Cocoa Foundation
+
+ * SoObjects/SoActionInvocation.m: added support for calling actions
+ and pages with positional parameters (eg from XML-RPC)
+
+ * SoObjects/SoObjectXmlRpcDispatcher.m: when looking up a SoMethod for
+ an XML-RPC method name containing a dot (like system.listmethods),
+ first check the fully qualified name prior traversing the package
+ namespaces
+
+ * SoObjects/SoObjectXmlRpcDispatcher.m: print a warning if server was
+ not linked against libNGXmlRpc
+
2004-10-22 Marcus Mueller <znek@mulle-kybernetik.com>
* WOElement.m: fixed unwanted behaviour introduced in v4.3.72 (v4.3.73)
#import <Foundation/NSObject.h>
/*
+ SoActionInvocation
+
An invocation object for WODirectAction based SoClass methods.
If the invocation is bound, the action is instantiated and initialized,
if it is called, the "actionName" is called and the result is returned or
if no "actionName" is set, the default action is called.
+
+ Usage:
+ methods = {
+ view = {
+ protectedBy = "View";
+ actionClass = "DirectAction"; // class to be instantiated
+ actionName = "view"; // 'viewAction' will get called
+ };
+ ...
+ }
+
+ Positional Arguments
+
+ When being queried using some method which supplies positional arguments
+ (for example XML-RPC), you can assign keys to each position using the
+ 'positionalKeys' argument info.
+
+ If more arguments are passed in than the number of keys specified in the
+ argument info, the additional arguments will be ignore (a warning will get
+ printed).
+
+ If less arguments are passed in, the additional keys will be resetted to
+ 'nil' (-takeValue:nil forKey:key will get called) to ensure that the call
+ signature is deterministic.
+
+ methods = {
+ "blogger.getUsersBlogs" = {
+ protectedBy = "View";
+ actionClass = BloggerGetUserBlogs;
+ arguments = {
+ positionalKeys = ( appId, login, password );
+ };
+ };
+ ...
+ }
+
+ SOAP Arguments
+
+ When being queried using SOAP, you can extract keys from the SOAP message
+ using EDOM query pathes. The values are assigned to the specified keys of
+ the action/page object.
+
+ methods = {
+ "cms.login" = {
+ actionClass = CMSLoginAction;
+ arguments = {
+ SOAP = { // key is from context.soRequestType
+ login = "/envelope/loginRequest/login";
+ password = "/envelope/loginRequest/password";
+ };
+ }
+ };
+ }
*/
-@class NSString, NSDictionary, NSMutableString;
+@class NSString, NSArray, NSDictionary, NSMutableString;
@interface SoActionInvocation : NSObject
{
- (BOOL)isBound;
- (id)bindToObject:(id)_object inContext:(id)_ctx;
+/* calling the method */
+
+- (id)callOnObject:(id)_client inContext:(id)_ctx;
+- (id)callOnObject:(id)_client
+ withPositionalParameters:(NSArray *)_args
+ inContext:(id)_ctx;
+
/* description */
- (void)appendAttributesToDescription:(NSMutableString *)_ms;
return nil;
}
- /* for each positional selector argument we have a query path */
+ /* for each key argument we have a query path */
args = [NSMutableDictionary dictionaryWithCapacity:8];
keys = [_spec keyEnumerator];
argspec = [self->argumentSpecifications objectForKey:[_ctx soRequestType]];
if (argspec == nil)
return;
-
+
args = [self extractArgumentsFromContext:_ctx
forRequestType:[_ctx soRequestType]
specification:argspec];
if (args != nil) [_method takeValuesFromDictionary:args];
}
-- (id)callOnObject:(id)_client inContext:(id)_ctx {
+- (void)_applyPositionalArguments:(NSArray *)_args onMethod:(id)_method
+ inContext:(id)_ctx
+{
+ NSArray *info;
+ unsigned i, argCount, infoCount;
+
+ info = [self->argumentSpecifications objectForKey:@"positionalKeys"];
+ infoCount = [info count];
+ argCount = [_args count];
+ if ((info == nil) && (argCount > 0)) {
+ [self logWithFormat:
+ @"WARNING: found no argument specification for positional keys!"];
+ return;
+ }
+
+ /* copy available arguments to key */
+
+ for (i = 0; i < argCount; i++) {
+ if (i >= infoCount) {
+ [self logWithFormat:
+ @"WARNING: could not apply argument %d (no key info)", (i + 1)];
+ continue;
+ }
+
+ [_method takeValue:[_args objectAtIndex:i] forKey:[info objectAtIndex:i]];
+ }
+
+ /* fill up missing arguments */
+
+ for (i = argCount; i < infoCount; i++)
+ [_method takeValue:nil forKey:[info objectAtIndex:i]];
+}
+
+/* calling the method */
+
+- (id)callOnObject:(id)_client
+ withPositionalParametersWhenNotNil:(NSArray *)_positionalArgs
+ inContext:(id)_ctx
+{
+ /* method used for both, positional and key arguments */
id method;
if (self->object != _client) {
if ([method respondsToSelector:@selector(setClientObject:)])
[method setClientObject:_client];
- /* TODO: what should be done first?, take values or args? */
- [self _prepareMethod:method inContext:_ctx];
- [self _applyArgumentsOnMethod:method inContext:_ctx];
+ if ([_positionalArgs isNotNull]) {
+ [self _applyPositionalArguments:_positionalArgs onMethod:method
+ inContext:_ctx];
+ }
+ else {
+ /* TODO: what should be done first?, take values or args? */
+ [self _prepareMethod:method inContext:_ctx];
+ [self _applyArgumentsOnMethod:method inContext:_ctx];
+ }
/* call action */
}
}
+- (id)callOnObject:(id)_client inContext:(id)_ctx {
+ return [self callOnObject:_client
+ withPositionalParametersWhenNotNil:nil /* not positional */
+ inContext:_ctx];
+}
+- (id)callOnObject:(id)_client
+ withPositionalParameters:(NSArray *)_args
+ inContext:(id)_ctx
+{
+ if (_args == nil) _args = [NSArray array];
+ return [self callOnObject:_client
+ withPositionalParametersWhenNotNil:_args
+ inContext:_ctx];
+}
+
/* bindings */
- (BOOL)isBound {
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
-// $Id$
#include "SoObjectXmlRpcDispatcher.h"
#include "SoObject.h"
+#include "SoClass.h"
#include "NSException+HTTP.h"
#include <NGXmlRpc/XmlRpcMethodCall+WO.h>
#include <NGXmlRpc/XmlRpcMethodResponse+WO.h>
/* perform call on object */
+- (BOOL)isPackagePathMethodName:(NSString *)_name inContext:(WOContext *)_ctx {
+ /*
+ If the XML-RPC method name contains a dot (eg system.listmethods), we need
+ to check whether the namespace is to be treated as a path or whether we
+ should lookup the fully qualified name.
+
+ So we first check whether it contains a dot, then we check whether the
+ object implements the fully qualified name. Otherwise we treat the
+ namespace as a path.
+ */
+ NSRange r;
+
+ /* check whether the name contains a dot ... */
+
+ r = [_name rangeOfString:@"." options:(NSLiteralSearch|NSBackwardsSearch)];
+ if (r.length == 0)
+ return NO; /* no dot ... */
+
+ /* check whether the object implements the fully qualified name */
+
+ if ([self->object hasName:_name inContext:_ctx])
+ return NO;
+
+ [self debugWithFormat:
+ @"Note: did not find pkg name '%@' in %@", _name, self->object];
+
+ /* otherwise, perform a path lookup ... */
+ return YES;
+}
+
+- (NSException *)getClientObject:(id *)_object andMethodName:(NSString **)_mn
+ forPackageMethodPath:(NSString *)_path inContext:(WOContext *)_ctx
+{
+ /* has a prefix, eg "folder.folder.a()" => traverse */
+ NSArray *nsParts;
+ NSException *error = nil;
+ unsigned count;
+
+ nsParts = [_path componentsSeparatedByString:@"."];
+ count = [nsParts count];
+ *_mn = [[[nsParts objectAtIndex:(count - 1)] copy] autorelease];
+ nsParts = [nsParts subarrayWithRange:NSMakeRange(0, count - 1)];
+
+ [self debugWithFormat:@"XML-RPC traversal: %@",
+ [nsParts componentsJoinedByString:@" => "]];
+
+ /*
+ TODO: we might not want to use -traverse.. so that the clientObject
+ stays the same (the one bound to the URL)?
+ */
+ *_object = [self->object
+ traversePathArray:nsParts inContext:_ctx error:&error
+ acquire:YES];
+ return error;
+}
+
- (id)performActionNamed:(NSString *)_name parameters:(NSArray *)_params
inContext:(WOContext *)_ctx
{
NSString *methodName;
- NSRange r;
id clientObject;
id method;
[self debugWithFormat:@"should perform: %@", _name];
methodName = nil;
- r = [_name rangeOfString:@"." options:(NSLiteralSearch|NSBackwardsSearch)];
- if (r.length > 0) {
+ if ([self isPackagePathMethodName:_name inContext:_ctx]) {
/* has a prefix, eg "folder.folder.a()" => traverse */
- NSArray *nsParts;
NSException *error = nil;
- unsigned count;
-
- nsParts = [_name componentsSeparatedByString:@"."];
- count = [nsParts count];
- methodName = [[[nsParts objectAtIndex:(count - 1)] copy] autorelease];
- nsParts = [nsParts subarrayWithRange:NSMakeRange(0, count - 1)];
-
- [self debugWithFormat:@"XML-RPC traversal: %@",
- [nsParts componentsJoinedByString:@" => "]];
+
+ [self debugWithFormat:@"Note: traversing path to XML-RPC method: %@",
+ _name];
- /*
- TODO: we might not want to use -traverse.. so that the clientObject
- stays the same (the one bound to the URL)?
- */
- clientObject = [self->object
- traversePathArray:nsParts inContext:_ctx error:&error
- acquire:YES];
+ error = [self getClientObject:&clientObject andMethodName:&methodName
+ forPackageMethodPath:_name inContext:_ctx];
if (error) {
[self debugWithFormat:@" XML-RPC traversal error: %@", error];
return error;
if ([_params count] > 0) {
[self logWithFormat:
@"WARNING: invoking SOPE method via XML-RPC without "
- @"positional paramters (%i parameters defined)",
- [_params count]];
+ @"positional paramters (%i parameters defined): %@",
+ [_params count], method];
}
return [method callOnObject:clientObject inContext:_ctx];
}
- (id)faultFromException:(NSException *)_exception
methodCall:(XmlRpcMethodCall *)_call
{
+#if !APPLE_FOUNDATION_LIBRARY && !NeXT_Foundation_LIBRARY
/* add some more information to generic exceptions ... */
- if (_call) {
+ if (_call != nil) {
NSMutableDictionary *ui;
ui = [[_exception userInfo] mutableCopy];
[_exception setUserInfo:ui];
[ui release];
}
+#endif
[self logWithFormat:@"%s: turning exception into fault %@\n",
__PRETTY_FUNCTION__,
XmlRpcMethodCall *call;
id result;
+ if (![XmlRpcMethodCall
+ instancesRespondToSelector:@selector(initWithRequest:)]) {
+ [self logWithFormat:
+ @"ERROR: XmlRpcMethodCall does not respond to -initWithRequest:, "
+ @"this method is part of libNGXmlRpc which you might want to link "
+ @"against to get XML-RPC support."];
+ return [NSException exceptionWithHTTPStatus:501 /* Not Implemented */
+ reason:@"server does not support XML-RPC"];
+ }
+
+ /* decode XML-RPC call */
+
call = [XmlRpcMethodCall alloc];
call = [call initWithRequest:[_ctx request]];
call = [call autorelease];
if (call == nil)
return [self couldNotDecodeXmlRpcRequestInContext:_ctx];
+
+ /* perform call */
if ((result = [self performMethodCall:call inContext:_ctx]) == nil)
/* TODO: should we return a fault instead? */
/* pass WOResponse objects through ... */
return result;
+ /* encode result as XML-RPC */
+
NS_DURING {
if ([result isKindOfClass:[XmlRpcMethodResponse class]])
r = result;
# version file
-SUBMINOR_VERSION:=73
+SUBMINOR_VERSION:=74
# v4.3.42 requires libNGExtensions v4.3.116
# v4.3.40 requires libNGExtensions v4.3.115
Free Software Foundation, 59 Temple Place - Suite 330, Boston, MA
02111-1307, USA.
*/
-// $Id$
+// $Id: WODirectAction.m 1 2004-08-20 10:08:27Z znek $
#include <NGObjWeb/WODirectAction.h>
#include "NSObject+WO.h"
#include <NGObjWeb/WOSessionStore.h>
#include "common.h"
+#if !LIB_FOUNDATION_LIBRARY
+# define NG_USE_KVC_FALLBACK 1
+#endif
+
@implementation WODirectAction
+ (int)version {
return WOGetKVCValueUsingMethod(self, _key);
}
+#if !LIB_FOUNDATION_LIBRARY
+
+- (void)setValue:(id)_value forUndefinedKey:(NSString *)_key {
+ [self logWithFormat:
+ @"WARNING: tried to set value for undefined KVC key: '%@'", _key];
+}
+- (id)valueForUndefinedKey:(NSString *)_key {
+ [self logWithFormat:
+ @"WARNING: tried to access undefined KVC key: '%@'", _key];
+ return nil;
+}
+
+#endif
+
@end /* WODirectAction */