# Licensed to the Apache Software Foundation (ASF) under one # or more contributor license agreements. See the NOTICE file # distributed with this work for additional information # regarding copyright ownership. The ASF licenses this file # to you under the Apache License, Version 2.0 (the # "License"); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # SCDL parsing functions from xml.etree.cElementTree import iterparse from sys import stderr from os import environ from util import * from httputil import * # Element tree utility functions, used to parse SCDL documents def parse(file): return map(lambda x: x, iterparse(file, events=("start", "end"))) def evt(e): return car(e) def elt(e): return cadr(e) def att(e): return elt(e).attrib def text(e): return elt(e).text def match(e, ev, tag): return evt(e) == ev and elt(e).tag.find("}" + tag) != -1 # Make a callable component class component: def __init__(self, name, impl, svcs, refs, props): self.name = name self.impl = impl self.mod = None self.svcs = svcs self.refs = refs self.props = props self.proxies = () def __call__(self, func, *args): return self.mod.__getattribute__(func)(*(args + self.proxies)) def __getattr__(self, name): if name[0] == '_': raise AttributeError() if name == "eval": return self l = lambda *args: self.__call__(name, *args) self.__dict__[name] = l return l def __repr__(self): return repr((self.name, self.impl, self.mod, self.svcs, self.refs, self.props, self.proxies)) def mkcomponent(name, impl, svcs, refs, props): return component(name, impl, svcs, refs, props) # Return the Python module name of a component implementation def implementation(e): if len(e) == 0 or match(car(e), "end", "component") == True: return "" if match(car(e), "start", "implementation.python") == False: return implementation(cdr(e)) if "script" in att(car(e)): s = att(car(e))["script"] return s[0:len(s) - 3] return None # Return the URI of a binding under a SCDL service or reference element def binding(e): if len(e) == 0 or match(car(e), "end", "reference") == True or match(car(e), "end", "service") == True: return () if match(car(e), "start", "binding.") == False: return binding(cdr(e)) return att(car(e))["uri"] # Return the list of references under a SCDL component element def references(e): if len(e) == 0 or match(car(e), "end", "component") == True: return () if match(car(e), "start", "reference") == False: return references(cdr(e)) if "target" in att(car(e)): return cons((att(car(e))["name"], car(tokens(att(car(e))["target"]))), references(cdr(e))) return cons((att(car(e))["name"], binding(e)), references(cdr(e))) # Return the list of properties under a SCDL component element def properties(e): if len(e) == 0 or match(car(e), "end", "component") == True: return () if match(car(e), "start", "property") == False: return properties(cdr(e)) return cons((att(car(e))["name"], text(car(e))), properties(cdr(e))) # Return the list of services under a SCDL component element def services(e): if len(e) == 0 or match(car(e), "end", "component") == True: return () if match(car(e), "start", "service") == False: return services(cdr(e)) return cons(tokens(binding(e)), services(cdr(e))) # Return the name attribute of a SCDL element def name(e): return att(car(e))["name"] # Return the list of components under a SCDL composite element def components(e): if len(e) == 0: return () if match(car(e), "start", "component") == False: return components(cdr(e)) n = name(e) return cons(mkcomponent(n, implementation(e), services(e), references(e), properties(e)), components(cdr(e))) # Find a component with a given name def nameToComponent(name, comps): if comps == (): return None if car(comps).name == name: return car(comps) return nameToComponent(name, cdr(comps)) # Find the URI matching a given URI in a list of service URIs def matchingURI(u, svcs): if svcs == (): return None if car(svcs) == u[0:len(car(svcs))]: return car(svcs) return matchingURI(u, cdr(svcs)) # Return the (service URI, component) pair matching a given URI def uriToComponent(u, comps): if car(u) == "components": return componentURIToComponent(u, comps) if car(u) == "references": return referenceURIToComponent(u, comps) return serviceURIToComponent(u, comps) def serviceURIToComponent(u, comps): if comps == (): return (None, None) m = matchingURI(u, car(comps).svcs) if m != None: return (m, car(comps)) return serviceURIToComponent(u, cdr(comps)) def componentURIToComponent(u, comps): comp = nameToComponent(cadr(u), comps) if comps == None: return (None, None) return (u[0:2], comp) def referenceURIToComponent(u, comps): sc = nameToComponent(cadr(u), comps) if sc == None: return (None, None) def referenceToComponent(r, refs): if refs == (): return None if r == car(car(refs)): return cadr(car(refs)) return referenceToComponent(r, cdr(refs)) tn = referenceToComponent(caddr(u), sc.refs) if tn == None: return (None, None) tc = nameToComponent(tn, comps) if tc == None: return (None, None) return (u[0:3], tc) # Evaluate a reference, return a proxy to the resolved component or an # HTTP client configured with the reference target uri def evalReference(r, comps): t = cadr(r) if t.startswith("http://") or t.startswith("https://"): return mkclient(t) return nameToComponent(t, comps) # Make a callable property class property: def __init__(self, name, l): self.name = name self.l = l def __call__(self, *args): return self.l(*args) def __getattr__(self, name): if name == "eval": return self raise AttributeError() def __repr__(self): return repr((self.name, self.l())) def mkproperty(name, l): return property(name, l) # Evaluate a property, return a lambda function returning the property # value. The host, user and email properties are configured with the # values from the HTTP request, if any def evalProperty(p): if car(p) == "host": return mkproperty(car(p), lambda: hostProperty(cadr(p), environ)) if car(p) == "user": return mkproperty(car(p), lambda: userProperty(cadr(p))) if car(p) == "nickname": return mkproperty(car(p), lambda: nicknameProperty(cadr(p))) if car(p) == "email": return mkproperty(car(p), lambda: emailProperty(cadr(p))) return mkproperty(car(p), lambda: cadr(p)) def currentUser(): try: from google.appengine.api import users return users.get_current_user() except: return None def userProperty(v): user = currentUser() return user.federated_identity() if user else v def nicknameProperty(v): user = currentUser() return user.nickname() if user else v def hostProperty(v, e): return e.get("HTTP_HOST", e.get("SERVER_NAME", v)).split(":")[0] def emailProperty(v): user = currentUser() return user.email() if user else v # Evaluate a component, resolve its implementation, references and # properties def evalComponent(comp, comps): comp.mod = __import__(comp.impl) # Make a list of proxy lambda functions for the component references and properties # A reference proxy is the callable lambda function of the component wired to the reference # A property proxy is a lambda function that returns the value of the property print >> stderr, "evalComponent", comp.impl, comp.svcs, comp.refs, comp.props comp.proxies = tuple(map(lambda r: evalReference(r, comps), comp.refs)) + tuple(map(lambda p: evalProperty(p), comp.props)) return comp # Evaluate a list of components def evalComponents(comps): return tuple(map(lambda c: evalComponent(c, comps), comps))