springpython-1.2.0+ds/0000755000000000000000000000000011526401330013351 5ustar rootrootspringpython-1.2.0+ds/springpython/0000755000000000000000000000000011544154725016132 5ustar rootrootspringpython-1.2.0+ds/springpython/jms/0000755000000000000000000000000011544154725016723 5ustar rootrootspringpython-1.2.0+ds/springpython/jms/factory.py0000644000000000000000000007222211412453030020732 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ # stdlib import sys import logging from threading import RLock from cStringIO import StringIO from struct import pack, unpack from xml.sax.saxutils import escape from binascii import hexlify, unhexlify from time import time, mktime, strptime, altzone from traceback import format_exc try: import cElementTree as etree except ImportError: try: import xml.etree.ElementTree as etree except ImportError: from elementtree import ElementTree as etree # Spring Python from springpython.context import DisposableObject from springpython.jms.core import reserved_attributes, TextMessage from springpython.util import TRACE1, synchronized from springpython.jms import JMSException, WebSphereMQJMSException, \ NoMessageAvailableException, DELIVERY_MODE_NON_PERSISTENT, \ DELIVERY_MODE_PERSISTENT # Don't pollute the caller's namespace __all__ = ["WebSphereMQConnectionFactory"] # Internal constants, don't touch. # Some WMQ constants are not exposed by pymqi. _WMQ_MQRFH_VERSION_2 = "\x00\x00\x00\x02" _WMQ_DEFAULT_ENCODING = 273 _WMQ_DEFAULT_ENCODING_WIRE_FORMAT = pack("!l", _WMQ_DEFAULT_ENCODING) # 1208 = UTF-8 _WMQ_DEFAULT_CCSID = 1208 _WMQ_DEFAULT_CCSID_WIRE_FORMAT = pack("!l", _WMQ_DEFAULT_CCSID) # From cmqc.h _WMQ_MQFMT_RF_HEADER_2 = "MQHRF2 " # MQRFH_NO_FLAGS_WIRE is in cmqc.h _WMQ_MQRFH_NO_FLAGS_WIRE_FORMAT = "\x00\x00\x00\x00" # Java documentation says "214748364.7 seconds". _WMQ_MAX_EXPIRY_TIME = 214748364.7 _WMQ_ID_PREFIX = "ID:" # In current implementation, an mcd JMS folder is constant for every message # sent, so let's build it here. _mcd = etree.Element("mcd") _msd = etree.Element("Msd") _mcd.append(_msd) # For now, it's always a TextMessage _msd.text = "jms_text" _msgbody = etree.Element("msgbody") _msgbody.set("xmlns:xsi", "dummy") # We're using a dummy namespace _msgbody.set("xsi:nil", "true") _mcd.append(_msgbody) # Clean up namespace. del(_msd, _msgbody) def unhexlify_wmq_id(wmq_id): """ Converts the WebSphere MQ generated identifier back to bytes, i.e. "ID:414d5120535052494e47505954484f4ecc90674a041f0020" -> "AMQ SPRINGPYTHON\xcc\x90gJ\x04\x1f\x00 ". """ return unhexlify(wmq_id.replace(_WMQ_ID_PREFIX, "", 1)) class WebSphereMQConnectionFactory(DisposableObject): def __init__(self, queue_manager=None, channel=None, host=None, listener_port=None, cache_open_send_queues=True, cache_open_receive_queues=True, use_shared_connections=True, dynamic_queue_template="SYSTEM.DEFAULT.MODEL.QUEUE", ssl=False, ssl_cipher_spec=None, ssl_key_repository=None): self.queue_manager = queue_manager self.channel = channel self.host = host self.listener_port = listener_port self.use_shared_connections = use_shared_connections self.dynamic_queue_template = dynamic_queue_template # SSL support self.ssl = ssl self.ssl_cipher_spec = ssl_cipher_spec self.ssl_key_repository = ssl_key_repository self.logger = logging.getLogger("springpython.jms.factory.WebSphereMQConnectionFactory") import CMQC import pymqi self.CMQC = CMQC self.mq = pymqi self._open_send_queues_cache = {} self._open_receive_queues_cache = {} self._open_dynamic_queues_cache = {} self.cache_open_send_queues = cache_open_send_queues self.cache_open_receive_queues = cache_open_receive_queues self._is_connected = False self._disconnecting = False self.logger.log(TRACE1, "Finished __init__") @synchronized() def destroy(self): if self._is_connected: self._disconnecting = True try: self.logger.info("Deleting queues from caches") self._open_send_queues_cache.clear() self._open_receive_queues_cache.clear() self._open_dynamic_queues_cache.clear() self.logger.info("Caches cleared") except Exception, e: try: self.logger.error("Could not clear the caches. Exception [%s]" % format_exc()) except: pass try: self.logger.info("Disconnecting from queue manager [%s]" % self.queue_manager) self.mgr.disconnect() self.logger.info("Disconnected from queue manager [%s]" % self.queue_manager) except Exception, e: try: self.logger.error("Could not disconnect from queue manager [%s], exception [%s] " % (self.queue_manager, format_exc())) except Exception: pass self._is_connected = False else: self.logger.debug("Not connected, skipping cleaning up the resources") def get_connection_info(self): return "queue manager=[%s], channel=[%s], conn_name=[%s(%s)]" % ( self.queue_manager, self.channel, self.host, self.listener_port) @synchronized() def _connect(self): if self._is_connected: return conn_name = "%s(%s)" % (self.host, self.listener_port) self.logger.info("Connecting to queue manager [%s], channel [%s]" \ ", connection info [%s]" % (self.queue_manager, self.channel, conn_name)) self.mgr = self.mq.QueueManager(None) sco = self.mq.sco() cd = self.mq.cd() cd.ChannelName = self.channel cd.ConnectionName = conn_name cd.ChannelType = self.CMQC.MQCHT_CLNTCONN cd.TransportType = self.CMQC.MQXPT_TCP if self.ssl: if not(self.ssl_cipher_spec and self.ssl_key_repository): msg = "SSL support requires setting both ssl_cipher_spec and ssl_key_repository" self.logger.error(msg) raise JMSException(msg) sco.KeyRepository = self.ssl_key_repository cd.SSLCipherSpec = self.ssl_cipher_spec if self.use_shared_connections: connect_options = self.CMQC.MQCNO_HANDLE_SHARE_BLOCK else: connect_options = self.CMQC.MQCNO_HANDLE_SHARE_NONE try: self.mgr.connectWithOptions(self.queue_manager, cd=cd, opts=connect_options, sco=sco) except self.mq.MQMIError, e: exc = WebSphereMQJMSException(e, e.comp, e.reason) raise exc except Exception, e: self.logger.error("Could not connect to queue manager, e=[%s]" % e) exc = WebSphereMQJMSException(e, None, None) raise exc else: self._is_connected = True self.logger.info("Successfully connected to queue manager [%s]" \ ", channel [%s], connection info [%s]" % (self.queue_manager, self.channel, conn_name)) def _get_queue_from_cache(self, destination, cache): lock = RLock() lock.acquire() try: # Will usually choose this path and find the queue here. if destination in cache: return cache[destination] else: self.logger.debug("Adding queue [%s] to the cache" % destination) cache[destination] = self.mq.Queue(self.mgr, destination, self.CMQC.MQOO_INPUT_SHARED | self.CMQC.MQOO_OUTPUT) self.logger.debug("Queue [%s] added to the cache" % destination) self.logger.log(TRACE1, "Cache contents [%s]" % cache) return cache[destination] finally: lock.release() def get_queue_for_sending(self, destination): if self.cache_open_send_queues: queue = self._get_queue_from_cache(destination, self._open_send_queues_cache) else: queue = self.mq.Queue(self.mgr, destination) return queue def get_queue_for_receiving(self, destination): if self.cache_open_receive_queues: queue = self._get_queue_from_cache(destination, self._open_receive_queues_cache) else: queue = self.mq.Queue(self.mgr, destination) return queue def send(self, message, destination): if self._disconnecting: self.logger.info("Connection factory disconnecting, aborting receive") return else: self.logger.log(TRACE1, "send -> not disconnecting") if not self._is_connected: self.logger.log(TRACE1, "send -> _is_connected1 %s" % self._is_connected) self._connect() self.logger.log(TRACE1, "send -> _is_connected2 %s" % self._is_connected) destination = self._strip_prefixes_from_destination(destination) # Will consist of an MQRFH2 header and the actual business payload. buff = StringIO() # Build the message descriptor (MQMD) md = self._build_md(message) # Create MQRFH2 header now = long(time() * 1000) mqrfh2jms = MQRFH2JMS().build_header(message, destination, self.CMQC, now) buff.write(mqrfh2jms) if message.text != None: buff.write(message.text.encode("utf-8")) body = buff.getvalue() buff.close() queue = self.get_queue_for_sending(destination) try: queue.put(body, md) except self.mq.MQMIError, e: self.logger.error("MQMIError in queue.put, e.comp [%s], e.reason [%s] " % ( e.comp, e.reason)) exc = WebSphereMQJMSException(e, e.comp, e.reason) raise exc if not self.cache_open_send_queues: queue.close() # Map the JMS headers overwritten by calling queue.put message.jms_message_id = _WMQ_ID_PREFIX + hexlify(md.MsgId) message.jms_priority = md.Priority message.jms_correlation_id = _WMQ_ID_PREFIX + hexlify(md.CorrelId) message.JMSXUserID = md.UserIdentifier message.JMSXAppID = md.PutApplName if md.PutDate and md.PutTime: message.jms_timestamp = self._get_jms_timestamp_from_md(md.PutDate.strip(), md.PutTime.strip()) message.JMS_IBM_PutDate = md.PutDate.strip() message.JMS_IBM_PutTime = md.PutTime.strip() else: self.logger.warning("No md.PutDate and md.PutTime found, md [%r]" % repr(md)) # queue.put has succeeded, so overwrite expiration time as well if message.jms_expiration != None: message.jms_expiration += now self.logger.debug("Successfully sent a message [%s], connection info [%s]" % ( message, self.get_connection_info())) self.logger.log(TRACE1, "message [%s], body [%r], md [%r]" % (message, body, repr(md))) def receive(self, destination, wait_interval): if self._disconnecting: self.logger.info("Connection factory disconnecting, aborting receive") return else: self.logger.log(TRACE1, "receive -> not disconnecting") if not self._is_connected: self.logger.log(TRACE1, "receive -> _is_connected1 %s" % self._is_connected) self._connect() self.logger.log(TRACE1, "receive -> _is_connected2 %s" % self._is_connected) queue = self.get_queue_for_receiving(destination) try: # Default message descriptor .. md = self.mq.md() # .. and custom get message options gmo = self.mq.gmo() gmo.Options = self.CMQC.MQGMO_WAIT | self.CMQC.MQGMO_FAIL_IF_QUIESCING gmo.WaitInterval = wait_interval message = queue.get(None, md, gmo) return self._build_text_message(md, message) except self.mq.MQMIError, e: if e.reason == self.CMQC.MQRC_NO_MSG_AVAILABLE: text = "No message available for destination [%s], " \ "wait_interval [%s] ms" % (destination, wait_interval) raise NoMessageAvailableException(text) else: self.logger.log(TRACE1, "Exception caught in get, e.comp=[%s], e.reason=[%s]" % (e.comp, e.reason)) exc = WebSphereMQJMSException(e, e.comp, e.reason) raise exc def open_dynamic_queue(self): if self._disconnecting: self.logger.info("Connection factory disconnecting, aborting open_dynamic_queue") return else: self.logger.log(TRACE1, "open_dynamic_queue -> not disconnecting") if not self._is_connected: self.logger.log(TRACE1, "open_dynamic_queue -> _is_connected1 %s" % self._is_connected) self._connect() self.logger.log(TRACE1, "open_dynamic_queue -> _is_connected2 %s" % self._is_connected) dynamic_queue = self.mq.Queue(self.mgr, self.dynamic_queue_template, self.CMQC.MQOO_INPUT_SHARED) # A bit hackish, but there's no other way to get its name. dynamic_queue_name = dynamic_queue._Queue__qDesc.ObjectName.strip() lock = RLock() lock.acquire() try: self._open_dynamic_queues_cache[dynamic_queue_name] = dynamic_queue finally: lock.release() self.logger.log(TRACE1, "Successfully created a dynamic queue, descriptor [%s]" % ( dynamic_queue._Queue__qDesc)) return dynamic_queue_name def close_dynamic_queue(self, dynamic_queue_name): if self._disconnecting: self.logger.info("Connection factory disconnecting, aborting close_dynamic_queue") return else: self.logger.log(TRACE1, "close_dynamic_queue -> not disconnecting") if not self._is_connected: # If we're not connected then all dynamic queues had been already closed. self.logger.log(TRACE1, "close_dynamic_queue -> _is_connected1 %s" % self._is_connected) return else: self.logger.log(TRACE1, "close_dynamic_queue -> _is_connected2 %s" % self._is_connected) lock = RLock() lock.acquire() try: dynamic_queue = self._open_dynamic_queues_cache[dynamic_queue_name] dynamic_queue.close() self._open_dynamic_queues_cache.pop(dynamic_queue_name, None) self._open_send_queues_cache.pop(dynamic_queue_name, None) self._open_receive_queues_cache.pop(dynamic_queue_name, None) self.logger.log(TRACE1, "Successfully closed a dynamic queue [%s]" % ( dynamic_queue_name)) finally: lock.release() def _get_jms_timestamp_from_md(self, put_date, put_time): pattern = "%Y%m%d%H%M%S" centi = int(put_time[6:]) / 100.0 strp = strptime(put_date + put_time[:6], pattern) mk = mktime(strp) return long((mk - altzone + centi) * 1000.0) def _build_text_message(self, md, message): self.logger.log(TRACE1, "Building a text message [%r], md [%r]" % (repr(message), repr(md))) mqrfh2 = MQRFH2JMS() mqrfh2.build_folders_and_payload_from_message(message) jms_folder = mqrfh2.folders.get("jms", None) mcd_folder = mqrfh2.folders.get("mcd", None) usr_folder = mqrfh2.folders.get("usr", None) # Create a message instance .. text_message = TextMessage() if usr_folder: for attr_name, attr_value in usr_folder.items(): setattr(text_message, attr_name, str(attr_value)) # .. set its JMS properties .. if jms_folder: if jms_folder.find("Dst") is not None: text_message.jms_destination = jms_folder.find("Dst").text.strip() if jms_folder.find("Exp") is not None: text_message.jms_expiration = long(jms_folder.find("Exp").text) else: text_message.jms_expiration = 0 # Same as in Java if jms_folder.find("Cid") is not None: text_message.jms_correlation_id = jms_folder.find("Cid").text if md.Persistence == self.CMQC.MQPER_NOT_PERSISTENT: text_message.jms_delivery_mode = DELIVERY_MODE_NON_PERSISTENT elif md.Persistence in(self.CMQC.MQPER_PERSISTENT, self.CMQC.MQPER_PERSISTENCE_AS_Q_DEF): text_message.jms_delivery_mode = DELIVERY_MODE_PERSISTENT else: text = "Don't know how to handle md.Persistence mode [%s]" % (md.Persistence) self.logger.error(text) exc = WebSphereMQJMSException(text) raise exc if md.ReplyToQ.strip(): self.logger.log(TRACE1, "Found md.ReplyToQ=[%r]" % md.ReplyToQ) text_message.jms_reply_to = "queue://" + md.ReplyToQMgr.strip() + "/" + md.ReplyToQ.strip() text_message.jms_priority = md.Priority text_message.jms_message_id = _WMQ_ID_PREFIX + hexlify(md.MsgId) text_message.jms_timestamp = self._get_jms_timestamp_from_md(md.PutDate.strip(), md.PutTime.strip()) text_message.jms_redelivered = bool(int(md.BackoutCount)) text_message.JMSXUserID = md.UserIdentifier.strip() text_message.JMSXAppID = md.PutApplName.strip() text_message.JMSXDeliveryCount = md.BackoutCount text_message.JMSXGroupID = md.GroupId.strip() text_message.JMSXGroupSeq = md.MsgSeqNumber md_report_to_jms = { self.CMQC.MQRO_EXCEPTION: "Exception", self.CMQC.MQRO_EXPIRATION: "Expiration", self.CMQC.MQRO_COA: "COA", self.CMQC.MQRO_COD: "COD", self.CMQC.MQRO_PAN: "PAN", self.CMQC.MQRO_NAN: "NAN", self.CMQC.MQRO_PASS_MSG_ID: "Pass_Msg_ID", self.CMQC.MQRO_PASS_CORREL_ID: "Pass_Correl_ID", self.CMQC.MQRO_DISCARD_MSG: "Discard_Msg", } for report_name, jms_header_name in md_report_to_jms.iteritems(): report_value = md.Report & report_name if report_value: header_value = report_value else: header_value = None setattr(text_message, "JMS_IBM_Report_" + jms_header_name, header_value) text_message.JMS_IBM_MsgType = md.MsgType text_message.JMS_IBM_Feedback = md.Feedback text_message.JMS_IBM_Format = md.Format.strip() text_message.JMS_IBM_PutApplType = md.PutApplType text_message.JMS_IBM_PutDate = md.PutDate.strip() text_message.JMS_IBM_PutTime = md.PutTime.strip() if md.MsgFlags & self.CMQC.MQMF_LAST_MSG_IN_GROUP: text_message.JMS_IBM_Last_Msg_In_Group = self.CMQC.MQMF_LAST_MSG_IN_GROUP else: text_message.JMS_IBM_Last_Msg_In_Group = None # .. and its payload too. if mqrfh2.payload: text_message.text = mqrfh2.payload return text_message def _strip_prefixes_from_destination(self, destination): if destination.startswith("queue:///"): return destination.replace("queue:///", "", 1) elif destination.startswith("queue://"): no_qm_dest = destination.replace("queue://", "", 1) no_qm_dest = no_qm_dest.split("/")[1:] return "/".join(no_qm_dest) else: return destination def _build_md(self, message): md = self.mq.md() md.Format = _WMQ_MQFMT_RF_HEADER_2 md.CodedCharSetId = _WMQ_DEFAULT_CCSID md.Encoding = _WMQ_DEFAULT_ENCODING # Map JMS headers to MQMD if message.jms_correlation_id: if message.jms_correlation_id.startswith(_WMQ_ID_PREFIX): md.CorrelId = unhexlify_wmq_id(message.jms_correlation_id) else: md.CorrelId = message.jms_correlation_id.ljust(24)[:24] if message.jms_delivery_mode: if message.jms_delivery_mode == DELIVERY_MODE_NON_PERSISTENT: persistence = self.CMQC.MQPER_NOT_PERSISTENT elif message.jms_delivery_mode == DELIVERY_MODE_PERSISTENT: persistence = self.CMQC.MQPER_PERSISTENT else: info = "jms_delivery_mode should be equal to DELIVERY_MODE_NON_PERSISTENT or DELIVERY_MODE_PERSISTENT, not [%s]" % message.jms_delivery_mode self.logger.error(info) exc = JMSException(info) raise exc md.Persistence = persistence if message.jms_priority: md.Priority = message.jms_priority if message.jms_reply_to: md.ReplyToQ = message.jms_reply_to self.logger.log(TRACE1, ("Set jms_reply_to. md.ReplyToQ=[%r]," " message.jms_reply_to=[%r]" % (md.ReplyToQ, message.jms_reply_to))) # jms_expiration is in milliseconds, md.Expiry is in centiseconds. if message.jms_expiration: if message.jms_expiration / 1000 > _WMQ_MAX_EXPIRY_TIME: md.Expiry = self.CMQC.MQEI_UNLIMITED else: md.Expiry = message.jms_expiration / 10 # WebSphere MQ provider-specific JMS headers jmsxgroupseq = getattr(message, "JMSXGroupSeq", None) if jmsxgroupseq != None: md.MsgSeqNumber = jmsxgroupseq md.MsgFlags |= self.CMQC.MQMF_MSG_IN_GROUP jmsxgroupid = getattr(message, "JMSXGroupID", None) if jmsxgroupid != None: if jmsxgroupid.startswith(_WMQ_ID_PREFIX): md.GroupId = unhexlify_wmq_id(jmsxgroupid) else: md.GroupId = jmsxgroupid.ljust(24)[:24] md.MsgFlags |= self.CMQC.MQMF_MSG_IN_GROUP for report_name in("Exception", "Expiration", "COA", "COD", "PAN", "NAN", "Pass_Msg_ID", "Pass_Correl_ID", "Discard_Msg"): report = getattr(message, "JMS_IBM_Report_" + report_name, None) if report != None: md.Report |= report # Doesn't make much sense to map feedback options as we're stuffed into # request messages (MQMT_REQUEST) not report messages (MQMT_REPORT) # but different types of messages are still possible to implement in # the future so let's leave it. jms_ibm_feedback = getattr(message, "JMS_IBM_Feedback", None) if jms_ibm_feedback != None: md.Feedback = jms_ibm_feedback jms_ibm_last_msg_in_group = getattr(message, "JMS_IBM_Last_Msg_In_Group", None) if jms_ibm_last_msg_in_group != None: md.MsgFlags |= self.CMQC.MQMF_LAST_MSG_IN_GROUP return md class MQRFH2JMS(object): """ A class for representing a subset of MQRFH2, suitable for passing WebSphere MQ JMS headers around. """ # 4 bytes - MQRFH_STRUC_ID # 4 bytes - _WMQ_MQRFH_VERSION_2 # 4 bytes - the whole MQRFH2 header length # 4 bytes - Encoding # 4 bytes - CodedCharacterSetId # 8 bytes - MQFMT_STRING # 4 bytes - MQRFH_NO_FLAGS # 4 bytes - NameValueCCSID FIXED_PART_LENGTH = 36 # MQRFH2 folder length must be a multiple of 4. FOLDER_LENGTH_MULTIPLE = 4 # Size of a folder header is always 4 bytes. FOLDER_SIZE_HEADER_LENGTH = 4 def __init__(self): self.folders = {} self.payload = None self.logger = logging.getLogger("springpython.jms.factory.MQRFH2JMS") def _pad_folder(self, folder): """ Pads the folder to a multiple of 4, as required by WebSphere MQ. """ folder_len = len(folder) if folder_len % MQRFH2JMS.FOLDER_LENGTH_MULTIPLE == 0: return folder else: padding = MQRFH2JMS.FOLDER_LENGTH_MULTIPLE - folder_len % MQRFH2JMS.FOLDER_LENGTH_MULTIPLE return folder.ljust(folder_len + padding) def build_folders_and_payload_from_message(self, message): total_mqrfh2_length = unpack("!l", message[8:12])[0] mqrfh2 = message[MQRFH2JMS.FIXED_PART_LENGTH:total_mqrfh2_length] self.payload = message[MQRFH2JMS.FIXED_PART_LENGTH + len(mqrfh2):] self.logger.log(TRACE1, "message [%r]" % message) self.logger.log(TRACE1, "mqrfh2 [%r]" % mqrfh2) self.logger.log(TRACE1, "self.payload [%r]" % self.payload) left = mqrfh2 while left: current_folder_length = unpack("!l", left[:4])[0] raw_folder = left[MQRFH2JMS.FOLDER_SIZE_HEADER_LENGTH:MQRFH2JMS.FOLDER_SIZE_HEADER_LENGTH + current_folder_length] self.logger.log(TRACE1, "raw_folder [%r]" % raw_folder) self.build_folder(raw_folder) left = left[MQRFH2JMS.FOLDER_SIZE_HEADER_LENGTH + current_folder_length:] def build_folder(self, raw_folder): # Java JMS sends folders with unbound prefixes, i.e. # which is in no way a valid XML so we have to insert the prefix ourselves # in order to avoid parser bailing out with an ExpatError. I can't think # of any other way to work around it if we'd like to treat folders as # XML(-like) structures. if 'xsi:nil="true"' in raw_folder and not 'xmlns' in raw_folder: self.logger.log(TRACE1, "Binding xsi:nil to a dummy namespace [%s]" % raw_folder) raw_folder = raw_folder.replace('xsi:nil="true"', 'xmlns:xsi="dummy" xsi:nil="true"') self.logger.log(TRACE1, "raw_folder after binding [%s]" % raw_folder) folder = etree.fromstring(raw_folder) root_name = folder.tag if root_name in("mcd", "jms", "usr"): self.folders[root_name] = folder else: self.logger.warn("Ignoring unrecognized JMS folder [%s]=[%s]" % (root_name, raw_folder)) def build_header(self, message, queue_name, CMQC, now): self.folders["mcd"] = _mcd self.add_jms(message, queue_name, now) self.add_usr(message) mcd = self._pad_folder(etree.tostring(self.folders["mcd"])) jms = self._pad_folder(etree.tostring(self.folders["jms"])) if "usr" in self.folders: usr = self._pad_folder(etree.tostring(self.folders["usr"])) usr_len = len(usr) else: usr_len = 0 mcd_len = len(mcd) jms_len = len(jms) total_header_length = 0 total_header_length += MQRFH2JMS.FIXED_PART_LENGTH # Each folder has a 4-byte header describing its length, # hence the "len(self.folders) * 4" below. variable_part_length = len(self.folders) * 4 + mcd_len + jms_len + usr_len total_header_length += variable_part_length buff = StringIO() buff.write(CMQC.MQRFH_STRUC_ID) buff.write(_WMQ_MQRFH_VERSION_2) buff.write(pack("!l", total_header_length)) buff.write(_WMQ_DEFAULT_ENCODING_WIRE_FORMAT) buff.write(_WMQ_DEFAULT_CCSID_WIRE_FORMAT) buff.write(CMQC.MQFMT_STRING) buff.write(_WMQ_MQRFH_NO_FLAGS_WIRE_FORMAT) buff.write(_WMQ_DEFAULT_CCSID_WIRE_FORMAT) buff.write(pack("!l", mcd_len)) buff.write(mcd) buff.write(pack("!l", jms_len)) buff.write(jms) if "usr" in self.folders: buff.write(pack("!l", usr_len)) buff.write(usr) value = buff.getvalue() buff.close() return value def add_jms(self, message, queue_name, now): jms = etree.Element("jms") dst = etree.Element("Dst") tms = etree.Element("Tms") dlv = etree.Element("Dlv") jms.append(dst) jms.append(tms) jms.append(dlv) tms.text = unicode(now) dst.text = u"queue:///" + queue_name dlv.text = unicode(message.jms_delivery_mode) if message.jms_expiration: exp = etree.Element("Exp") exp.text = unicode(now + message.jms_expiration) self.logger.log(TRACE1, "jms.Exp [%r]" % exp.text) jms.append(exp) if message.jms_priority: pri = etree.Element("Pri") pri.text = unicode(message.jms_priority) self.logger.log(TRACE1, "jms.Pri [%r]" % pri.text) jms.append(pri) if message.jms_correlation_id: cid = etree.Element("Cid") cid.text = unicode(message.jms_correlation_id) self.logger.log(TRACE1, "jms.Cid [%r]" % cid.text) jms.append(cid) self.folders["jms"] = jms def add_usr(self, message): user_attrs = set(dir(message)) - reserved_attributes self.logger.log(TRACE1, "user_attrs [%s]" % user_attrs) if user_attrs: usr = etree.Element("usr") for user_attr in user_attrs: user_attr_value = getattr(message, user_attr) # Some values are integers, e.g. delivery_mode if isinstance(user_attr_value, basestring): user_attr_value = escape(user_attr_value) # Create a JMS attribute and set its value. user_attr = etree.Element(unicode(user_attr)) user_attr.text = unicode(user_attr_value) usr.append(user_attr) self.folders["usr"] = usr springpython-1.2.0+ds/springpython/jms/listener.py0000644000000000000000000001133711412453030021110 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ # stdlib import logging # Circuits from circuits import Component, Manager, Debugger # ThreadPool from threadpool import ThreadPool, WorkRequest, NoResultsPending # Spring Python from springpython.util import TRACE1 from springpython.context import InitializingObject from springpython.jms import WebSphereMQJMSException, NoMessageAvailableException class MessageHandler(object): def handle(self, message): raise NotImplementedError("Should be overridden by subclasses.") class WebSphereMQListener(Component): """ A JMS listener for receiving the messages off WebSphere MQ queues. """ def __init__(self): super(Component, self).__init__() self.logger = logging.getLogger("springpython.jms.listener.WebSphereMQListener(%s)" % (hex(id(self)))) def _get_destination_info(self): return "destination=[%s], %s" % (self.destination, self.factory.get_connection_info()) def run(self, *ignored): while True: try: message = self.factory.receive(self.destination, self.wait_interval) self.logger.log(TRACE1, "Message received [%s]" % str(message).decode("utf-8")) work_request = WorkRequest(self.handler.handle, [message]) self.handlers_pool.putRequest(work_request) try: self.handlers_pool.poll() except NoResultsPending, e: pass except NoMessageAvailableException, e: self.logger.log(TRACE1, "Consumer did not receive a message. %s" % self._get_destination_info()) except WebSphereMQJMSException, e: self.logger.error("%s in run, e.completion_code=[%s], " "e.reason_code=[%s]" % (e.__class__.__name__, e.completion_code, e.reason_code)) raise class SimpleMessageListenerContainer(InitializingObject): """ A container for individual JMS listeners. """ def __init__(self, factory=None, destination=None, handler=None, concurrent_listeners=1, handlers_per_listener=2, wait_interval=1000): """ factory - reference a to JMS connection factory destination - name of a queue to get the messages off handler - reference to an object which will be passed the incoming messages concurrent_listeners - how many concurrent JMS listeners the container will manage handlers_per_listener - how many handler threads each listener will receive wait_interval - time, in milliseconds, indicating how often each JMS listener will check for new messages """ self.factory = factory self.destination = destination self.handler = handler self.concurrent_listeners = concurrent_listeners self.handlers_per_listener = handlers_per_listener self.wait_interval = wait_interval self.logger = logging.getLogger("springpython.jms.listener.SimpleMessageListenerContainer") def after_properties_set(self): """ Run by Spring Python after all the JMS container's properties have been set. """ for idx in range(self.concurrent_listeners): # Create as many Circuits managers as there are JMS listeners. manager = Manager() manager.start() # A pool of handler threads for each listener. handlers_pool = ThreadPool(self.handlers_per_listener) # Each manager gets assigned its own listener. listener = WebSphereMQListener() # Assign the listener and a debugger component to the manager. manager += listener manager += Debugger(logger=self.logger) listener.factory = self.factory listener.destination = self.destination listener.handler = self.handler listener.handlers_pool = handlers_pool listener.wait_interval = self.wait_interval listener.start() springpython-1.2.0+ds/springpython/jms/__init__.py0000644000000000000000000000271211412453030021017 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ # JMS constants DELIVERY_MODE_NON_PERSISTENT = 1 DELIVERY_MODE_PERSISTENT = 2 RECEIVE_TIMEOUT_INDEFINITE_WAIT = 0 RECEIVE_TIMEOUT_NO_WAIT = -1 DEFAULT_DELIVERY_MODE = DELIVERY_MODE_PERSISTENT DEFAULT_TIME_TO_LIVE = 0 class JMSException(Exception): """ Base class for all JMS-related exceptions. """ class NoMessageAvailableException(JMSException): """ Raised when the jms_template's call to receive returned no message in the expected wait interval. """ class WebSphereMQJMSException(JMSException): """ Class for exceptions related to WebSphereMQ only. """ def __init__(self, message=None, completion_code=None, reason_code=None): JMSException.__init__(self, message) self.message = message self.completion_code = completion_code self.reason_code = reason_code springpython-1.2.0+ds/springpython/jms/core.py0000644000000000000000000001623711412453030020217 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ # stdlib import locale from string import Template from cStringIO import StringIO # Spring Python from springpython.jms import JMSException from springpython.jms import DEFAULT_DELIVERY_MODE __all__ = ["MessageConverter", "JmsTemplate", "TextMessage"] # These attributes have special meaning to connection factories. reserved_attributes = set(("text", "jms_correlation_id", "jms_delivery_mode", "jms_destination", "jms_expiration", "jms_message_id", "jms_priority", "jms_redelivered", "jms_reply_to", "jms_timestamp", "max_chars_printed", "JMS_IBM_Report_Exception", "JMS_IBM_Report_Expiration", "JMS_IBM_Report_COA", "JMS_IBM_Report_COD", "JMS_IBM_Report_PAN", "JMS_IBM_Report_NAN", "JMS_IBM_Report_Pass_Msg_ID", "JMS_IBM_Report_Pass_Correl_ID", "JMS_IBM_Report_Discard_Msg", "JMSXGroupID", "JMSXGroupSeq", "JMS_IBM_Feedback", "JMS_IBM_Last_Msg_In_Group", "JMSXUserID", "JMS_IBM_PutTime", "JMS_IBM_PutDate", "JMSXAppID")) # Magic methods are also forbidden. reserved_attributes.update(set(dir(object) + ["__weakref__", "__dict__", "__module__"])) text_message_template = """ JMS message class: jms_text jms_delivery_mode: $jms_delivery_mode jms_expiration: $jms_expiration jms_priority: $jms_priority jms_message_id: $jms_message_id jms_timestamp: $jms_timestamp jms_correlation_id: $jms_correlation_id jms_destination: $jms_destination jms_reply_to: $jms_reply_to jms_redelivered: $jms_redelivered """.lstrip() class MessageConverter(object): def to_message(self, object_to_be_converted_to_a_message): raise NotImplementedError("Should be implemented by subclasses") def from_message(self, message_to_be_converted_to_an_object): raise NotImplementedError("Should be implemented by subclasses") class JmsTemplate(object): def __init__(self, factory=None, delivery_persistent=None, priority=None, time_to_live=None, message_converter=None, default_destination=None): self.factory = factory # QoS self.delivery_persistent = delivery_persistent self.priority = priority self.time_to_live = time_to_live self.message_converter = message_converter self.default_destination = default_destination def convert_and_send(self, object_, destination=None): if not self.message_converter: raise JMSException("Couldn't send the message, no message converter set") self.send(self.message_converter.to_message(object_), destination) def send(self, message, destination=None): if isinstance(message, basestring): message = TextMessage(message) if destination: dest = destination elif self.default_destination: dest = self.default_destination else: raise JMSException("No destination given and no default destination set") message.jms_destination = dest self.factory.send(message, dest) def receive(self, destination=None, timeout=1000): if destination: dest = destination elif self.default_destination: dest = self.default_destination else: raise JMSException("No destination given and no default destination set") return self.factory.receive(dest, timeout) def receive_and_convert(self, destination=None, timeout=1000): if not self.message_converter: raise JMSException("Couldn't receive a message, no message converter set") return self.message_converter.from_message(self.receive(destination, timeout)) def open_dynamic_queue(self): return self.factory.open_dynamic_queue() def close_dynamic_queue(self, dynamic_queue_name): self.factory.close_dynamic_queue(dynamic_queue_name) class TextMessage(object): def __init__(self, text=None, jms_correlation_id=None, jms_delivery_mode=None, jms_destination=None, jms_expiration=None, jms_message_id=None, jms_priority=None, jms_redelivered=None, jms_reply_to=None, jms_timestamp=None, max_chars_printed=100): self.text = text self.jms_correlation_id = jms_correlation_id self.jms_delivery_mode = jms_delivery_mode or DEFAULT_DELIVERY_MODE self.jms_destination = jms_destination self.jms_expiration = jms_expiration self.jms_message_id = jms_message_id self.jms_priority = jms_priority self.jms_redelivered = jms_redelivered self.jms_reply_to = jms_reply_to self.jms_timestamp = jms_timestamp self.max_chars_printed = max_chars_printed def __str__(self): basic_data = { "jms_delivery_mode": self.jms_delivery_mode, "jms_expiration":self.jms_expiration, "jms_priority":self.jms_priority, "jms_message_id":self.jms_message_id, "jms_timestamp":self.jms_timestamp, "jms_correlation_id":self.jms_correlation_id, "jms_destination":self.jms_destination, "jms_reply_to":self.jms_reply_to, "jms_redelivered":self.jms_redelivered, } buff = StringIO() buff.write(Template(text_message_template).safe_substitute(basic_data)) user_attrs = set(dir(self)) - reserved_attributes user_attrs = list(user_attrs) user_attrs.sort() if user_attrs: for user_attr in user_attrs: user_attr_value = getattr(self, user_attr) if isinstance(user_attr_value, unicode): user_attr_value = user_attr_value.encode("utf-8") buff.write(" %s:%s\n" % (user_attr, user_attr_value)) if self.text != None: text_to_show = self.text[:self.max_chars_printed] if isinstance(text_to_show, unicode): text_to_show = text_to_show.encode("utf-8") buff.write(text_to_show) if len(text_to_show) < len(self.text): omitted = locale.format("%d", (len(self.text) - len(text_to_show)), True) buff.write("\nAnother ") buff.write(omitted) buff.write(" character(s) omitted") else: buff.write("") value = buff.getvalue() buff.close() return value springpython-1.2.0+ds/springpython/database/0000755000000000000000000000000011544154725017676 5ustar rootrootspringpython-1.2.0+ds/springpython/database/factory.py0000644000000000000000000001353211514756470021725 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import re import sys import types class ConnectionFactory(object): def __init__(self, acceptable_types): self.__db = None self.acceptable_types = acceptable_types """This interface defines an object that is able to make database connections. This allows database connections to be defined inside application contexts, and fed to DAO and DatabaseTemplates.""" def connect(self): raise NotImplementedError() def getConnection(self): if self.__db is None: self.__db = self.connect() return self.__db def close(self): "Need to offer API call to close the connection to the database." if self.__db is not None: self.__db.close() self.__db = None def commit(self): if self.in_transaction(): self.getConnection().commit() def rollback(self): if self.in_transaction(): self.getConnection().rollback() def in_transaction(self): raise NotImplementedError() def count_type(self): raise NotImplementedError() def convert_sql_binding(self, sql_query): """This is to help Java users migrate to Python. Java notation defines binding variables points with '?', while Python uses '%s', and this method will convert from one format to the other.""" return re.sub(pattern="\?", repl="%s", string=sql_query) class MySQLConnectionFactory(ConnectionFactory): def __init__(self, username = None, password = None, hostname = None, db = None): ConnectionFactory.__init__(self, [types.TupleType]) self.username = username self.password = password self.hostname = hostname self.db = db def connect(self): """The import statement is delayed so the library is loaded ONLY if this factory is really used.""" import MySQLdb return MySQLdb.connect(self.hostname, self.username, self.password, self.db) def in_transaction(self): return True def count_type(self): return types.LongType class PgdbConnectionFactory(ConnectionFactory): def __init__(self, user = None, password = None, host = None, database = None): ConnectionFactory.__init__(self, [types.TupleType]) self.user = user self.password = password self.host = host self.database = database def connect(self): """The import statement is delayed so the library is loaded ONLY if this factory is really used.""" import pgdb return pgdb.connect(user=self.user, password=self.password, database=self.database, host=self.host) def in_transaction(self): return True def count_type(self): return types.LongType class Sqlite3ConnectionFactory(ConnectionFactory): def __init__(self, db = None, check_same_thread=True): ConnectionFactory.__init__(self, [types.TupleType]) self.db = db self.check_same_thread = check_same_thread self.using_sqlite3 = True def connect(self): """The import statement is delayed so the library is loaded ONLY if this factory is really used.""" try: import sqlite3 return sqlite3.connect(self.db, check_same_thread=self.check_same_thread) except: import sqlite self.using_sqlite3 = False return sqlite.connect(self.db, check_same_thread=self.check_same_thread) def in_transaction(self): return True def count_type(self): return types.IntType def convert_sql_binding(self, sql_query): if self.using_sqlite3: """sqlite3 uses the ? notation, like Java's JDBC.""" return re.sub(pattern="%s", repl="?", string=sql_query) else: """Older versions of sqlite use the %s notation""" return re.sub(pattern="\?", repl="%s", string=sql_query) class cxoraConnectionFactory(ConnectionFactory): def __init__(self, username = None, password = None, hostname = None, db = None): ConnectionFactory.__init__(self, [types.DictType]) self.username = username self.password = password self.hostname = hostname self.db = db def connect(self): """The import statement is delayed so the library is loaded ONLY if this factory is really used.""" import cx_Oracle return cx_Oracle.connect(self.username, self.password, self.db) class SQLServerConnectionFactory(ConnectionFactory): def __init__(self, **odbc_info): ConnectionFactory.__init__(self, [types.TupleType]) self.odbc_info = odbc_info def connect(self): """The import statement is delayed so the library is loaded ONLY if this factory is really used.""" import pyodbc odbc_info = ";".join(["%s=%s" % (key, value) for key, value in self.odbc_info.items()]) return pyodbc.connect(odbc_info) def in_transaction(self): return True def count_type(self): return types.IntType def convert_sql_binding(self, sql_query): """SQL Server expects parameters to be passed as question marks.""" return re.sub(pattern="%s", repl="?", string=sql_query) springpython-1.2.0+ds/springpython/database/__init__.py0000644000000000000000000000222611412453030021772 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ class DataAccessException(Exception): pass class IncorrectResultSizeDataAccessException(DataAccessException): pass class ArgumentMustBeNamed(DataAccessException): def __init__(self, arg_name, msg = ""): DataAccessException.__init__(self, msg) self.arg_name = arg_name class InvalidArgumentType(DataAccessException): def __init__(self, arg_type, valid_types, msg = ""): DataAccessException.__init__(self, msg) self.arg_type = arg_type self.valid_types = valid_types springpython-1.2.0+ds/springpython/database/transaction.py0000644000000000000000000003124611412453030022564 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import inspect import logging import re import types from springpython.aop import MethodInterceptor from springpython.aop import ProxyFactoryObject from springpython.context import ObjectPostProcessor from springpython.config.decorator import decorator logger = logging.getLogger("springpython.database.transaction") class TransactionException(Exception): pass class TransactionPropagationException(TransactionException): pass class TransactionStatus(object): pass class DefaultTransactionStatus(TransactionStatus): pass class PlatformTransactionManager(object): """This interface is used to define the operations necessary in handling transactions.""" def commit(self, status): raise NotImplementedError() def getTransaction(self, definition): raise NotImplementedError() def rollback(self, status): raise NotImplementedError() class ConnectionFactoryTransactionManager(PlatformTransactionManager): """ This transaction manager is based upon using a connection factory to control transactions. Since connection factories are tied to vendor-specific databases, this allows delegation of various transactional functions on a per-vendor basis. """ def __init__(self, connection_factory): self.connection_factory = connection_factory self.logger = logging.getLogger("springpython.database.transaction.ConnectionFactoryTransactionManager") self.status = [] def getTransaction(self, definition): """According to PEP 249, commits and rollbacks silently start new transactions. Until a more robust transaction manager is implemented to handle save points and so forth, this must suffice.""" self.logger.debug("Analyzing %s" % definition.propagation) start_tx = False if definition.propagation == "PROPAGATION_REQUIRED": if len(self.status) == 0: self.logger.debug("There is no current transaction, and one is required, so starting one.") start_tx = True self.status.append(DefaultTransactionStatus()) elif definition.propagation == "PROPAGATION_SUPPORTS": self.logger.debug("This code can execute inside or outside a transaction.") elif definition.propagation == "PROPAGATION_MANDATORY": if len(self.status) == 0: raise TransactionPropagationException("Trying to execute PROPAGATION_MANDATORY operation while outside TX") self.status.append(DefaultTransactionStatus()) elif definition.propagation == "PROPAGATION_NEVER": if len(self.status) != 0: raise TransactionPropagationException("Trying to execute PROPAGATION_NEVER operation while inside TX") else: raise TransactionPropagationException("Transaction propagation level %s is not supported!" % definition.start_tx) if start_tx: self.logger.debug("START TRANSACTION") self.logger.debug("Creating a transaction, propagation = %s, isolation = %s, timeout = %s, read_only = %s" % (definition.propagation, definition.isolation, definition.timeout, definition.read_only)) self.connection_factory.commit() return self.status def commit(self, status): self.status = status try: self.status.pop() if len(self.status) == 0: self.logger.debug("Commit the changes") self.connection_factory.commit() self.logger.debug("END TRANSACTION") except IndexError: pass def rollback(self, status): self.status = status try: self.status.pop() if len(self.status) == 0: self.logger.debug("Rolling back the transaction.") self.connection_factory.rollback() self.logger.debug("END TRANSACTION") except IndexError: pass class TransactionDefinition(object): def __init__(self, isolation = None, name = None, propagation = None, timeout = None, read_only = None): self.isolation = isolation self.name = name self.propagation = propagation self.timeout = timeout self.read_only = read_only class DefaultTransactionDefinition(TransactionDefinition): def __init__(self, isolation = "ISOLATION_DEFAULT", name = "", propagation = "PROPAGATION_REQUIRED", timeout = "TIMEOUT_DEFAULT", read_only = False): TransactionDefinition.__init__(self, isolation, name, propagation, timeout, read_only) class TransactionTemplate(DefaultTransactionDefinition): """This utility class is used to simplify defining transactional blocks. Any exceptions thrown inside the transaction block will be propagated to whom ever is calling the template execute method.""" def __init__(self, tx_manager): DefaultTransactionDefinition.__init__(self) self.tx_manager = tx_manager self.logger = logging.getLogger("springpython.database.transaction.TransactionTemplate") def execute(self, transactionCallback): """Execute the action specified by the given callback object within a transaction.""" status = self.tx_manager.getTransaction(self) result = None try: self.logger.debug("Execute the steps inside the transaction") result = transactionCallback.do_in_transaction(status) self.tx_manager.commit(status) except Exception, e: self.logger.debug("Exception: (%s)" % e) self.tx_manager.rollback(status) raise e return result def setTxAttributes(self, tx_attributes): for tx_def_prop in tx_attributes: if tx_def_prop.startswith("ISOLATION"): if tx_def_prop != self.isolation: self.isolation = tx_def_prop elif tx_def_prop.startswith("PROPAGATION"): if tx_def_prop != self.propagation: self.propagation = tx_def_prop elif tx_def_prop.startswith("TIMEOUT"): if tx_def_prop != self.timeout: self.timeout = tx_def_prop elif tx_def_prop == "read_only": if not self.read_only: self.read_only = True else: self.logger.debug("Don't know how to handle %s" % tx_def_prop) class TransactionCallback(object): """This interface defines the basic action needed to plug into the TransactionTemplate""" def do_in_transaction(self, status): raise NotImplementedError() class TransactionCallbackWithoutResult(TransactionCallback): """This abstract class implements the TransactionCallback, but assumes no value is being returned.""" def __init__(self): self.logger = logging.getLogger("springpython.database.transaction.TransactionCallbackWithoutResult") def do_in_transaction(self, status): self.logger.debug("Starting a transaction without result") self.do_in_tx_without_result(status) self.logger.debug("Completing a transaction without result") return None def do_in_tx_without_result(self, status): pass class TransactionalInterceptor(MethodInterceptor): """This interceptor is used by the TransactionProxyFactoryObject in order to wrap method calls with transactions.""" def __init__(self, tx_manager, tx_attributes): self.logger = logging.getLogger("springpython.database.transaction.TransactionalInterceptor") self.tx_attributes = tx_attributes self.tx_manager = tx_manager def invoke(self, invocation): class tx_def(TransactionCallback): def do_in_transaction(s, status): return invocation.proceed() tx_template = TransactionTemplate(self.tx_manager) # Iterate over the tx_attributes, and when a method match is found, apply the properties for pattern, tx_def_props in self.tx_attributes: if re.compile(pattern).match(invocation.method_name): self.logger.debug("%s matches pattern %s, tx attributes = %s" % (invocation.method_name, pattern, tx_def_props)) tx_template.setTxAttributes(tx_def_props) break self.logger.debug("Call TransactionTemplate") try: results = tx_template.execute(tx_def()) except Exception, e: self.logger.debug("Exception => %s" % e) raise e self.logger.debug("Return from TransactionTemplate") return results class TransactionProxyFactoryObject(ProxyFactoryObject): """This class acts like the target object, and routes function calls through a transactional interceptor.""" def __init__(self, tx_manager, target, tx_attributes): self.logger = logging.getLogger("springpython.database.transaction.TransactionProxyFactoryObject") ProxyFactoryObject.__init__(self, target, TransactionalInterceptor(tx_manager, tx_attributes)) def transactional(tx_attributes = None): """ This decorator is actually a utility function that returns an embedded decorator, in order to handle whether it was called in any of the following ways: @transactional() def foo(): pass @transactional def foo(): pass The first two ways get parsed by Python as: foo = transactional("some contextual string")(foo) # first way foo = transactional()(foo) # second way Since this is expected, they are granted direct access to the embedded transactional_wrapper. However, the third way ends up getting parsed by Python as: foo = Transactional(foo) This causes context to improperly get populated with a function instead of a string. This requires recalling this utility like: return Transactional()(context) """ @decorator def transactional_wrapper(f, *args, **kwargs): """ transactional_wrapper is used to wrap the decorated function in a TransactionTemplate callback, and then return the results. """ class tx_def(TransactionCallback): """TransactionTemplate requires a callback defined this way.""" def do_in_transaction(s, status): return f(*args, **kwargs) try: # Assumes tx_manager is supplied by AutoTransactionalObject tx_template = TransactionTemplate(tx_manager) if tx_attributes is not None: tx_template.setTxAttributes(tx_attributes) else: logger.debug("There are NO tx_attributes! %s" % tx_attributes) return tx_template.execute(tx_def()) except NameError: # If no AutoTransactionalObject found in IoC container, then pass straight through. return tx_def().do_in_transaction(None) if type(tx_attributes) == types.FunctionType: return transactional()(tx_attributes) else: return transactional_wrapper class AutoTransactionalObject(ObjectPostProcessor): """ This object is used to automatically scan objects in an IoC container, and if @Transaction is found applied to any of the object's methods, link it with a TransactionManager. """ def __init__(self, tx_manager): self.tx_manager = tx_manager self.logger = logging.getLogger("springpython.database.transaction.AutoTransactionalObject") def post_process_after_initialization(self, obj, obj_name): """This setup is run after all objects in the container have been created.""" # Check every method in the object... for name, method in inspect.getmembers(obj, inspect.ismethod): try: # If the method contains _call_, then you are looking at a wrapper... wrapper = method.im_func.func_globals["_call_"] if wrapper.func_name == "transactional_wrapper": # name of @transactional's wrapper method self.logger.debug("Linking tx_manager with %s" % name) wrapper.func_globals["tx_manager"] = self.tx_manager except KeyError, e: # If the method is NOT wrapped, there will be no _call_ attribute pass return obj springpython-1.2.0+ds/springpython/database/core.py0000644000000000000000000002355311526401170021175 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import types from springpython.database import ArgumentMustBeNamed from springpython.database import DataAccessException from springpython.database import IncorrectResultSizeDataAccessException from springpython.database import InvalidArgumentType from springpython.database import factory class DaoSupport(object): """ Any class that extends this one will be provided with a DatabaseTemplate class to help carry out database operations. It requires that a connection object be provided during instantion. """ def __init__(self, connection_factory = None): self.database_template = DatabaseTemplate() self.connection_factory = connection_factory def __setattr__(self, name, value): """When the connection factory is set, pass it on through to the database template.""" self.__dict__[name] = value if name == "connection_factory" and value: self.__dict__["database_template"].connection_factory = value class DatabaseTemplate(object): """ This class is meant to mimic the Spring framework's JdbcTemplate class. Since Python doesn't use JDBC, the name is generalized to "Database" """ def __init__(self, connection_factory = None): self.connection_factory = connection_factory self.logger = logging.getLogger("springpython.database.core.DatabaseTemplate") def __del__(self): "When this template goes out of scope, need to close the connection it formed." if self.connection_factory is not None: self.connection_factory.close() def _execute(self, sql_statement, args = None): """Issue a single SQL execute, typically a DDL statement.""" sql_statement = self.connection_factory.convert_sql_binding(sql_statement) cursor = self.connection_factory.getConnection().cursor() error = None rows_affected = 0 try: try: if args: cursor.execute(sql_statement, args) rows_affected = cursor.rowcount lastrowid = cursor.lastrowid else: cursor.execute(sql_statement) rows_affected = cursor.rowcount lastrowid = cursor.lastrowid except Exception, e: self.logger.debug("execute.execute: Trapped %s while trying to execute '%s'" % (e, sql_statement)) error = e finally: try: cursor.close() except Exception, e: self.logger.debug("execute.close: Trapped %s, and throwing away." % e) if error: raise DataAccessException(error) return {"rows_affected":rows_affected, "lastrowid":lastrowid} def execute(self, sql_statement, args = None): """Execute a single SQL statement, and return the number of rows affected.""" return self._execute(sql_statement, args)["rows_affected"] def insert_and_return_id(self, sql_statement, args = None): """Execute a single INSERT statement, and return the PK of the new row.""" return self._execute(sql_statement, args)["lastrowid"] def query(self, sql_query, args = None, rowhandler = None): """Execute a query given static SQL, reading the ResultSet on a per-row basis with a RowMapper. If args is provided, bind the arguments (to avoid SQL injection attacks).""" # This is the case where only two, non-named arguments were provided, the sql_query and one other. # If the second argument was 'args', it is invalid since 'rowhandler' is required. # It is was 'rowhandler', it shifted into 'args' position, and requires naming. if args and not rowhandler: raise ArgumentMustBeNamed(arg_name="rowhandler") results, metadata = self.__query_for_list(sql_query, args) return [rowhandler.map_row(row, metadata) for row in results] def query_for_list(self, sql_query, args = None): results, metadata = self.__query_for_list(sql_query, args) return results def __query_for_list(self, sql_query, args = None): """Execute a query for a result list, given static SQL. If args is provided, bind the arguments (to avoid SQL injection attacks).""" if args and type(args) not in self.connection_factory.acceptable_types: raise InvalidArgumentType(type(args), self.connection_factory.acceptable_types) sql_query = self.connection_factory.convert_sql_binding(sql_query) cursor = self.connection_factory.getConnection().cursor() error = None results = None metadata = None try: try: if args: cursor.execute(sql_query, args) else: cursor.execute(sql_query) results = cursor.fetchall() metadata = [{"name":row[0], "type_code":row[1], "display_size":row[2], "internal_size":row[3], "precision":row[4], "scale":row[5], "null_ok":row[6]} for row in cursor.description] except Exception, e: self.logger.debug("query_for_list.execute: Trapped %s while trying to execute '%s'" % (e, sql_query)) error = e finally: try: cursor.close() except Exception, e: self.logger.debug("query_for_list.close: Trapped %s, and throwing away." % e) if error: self.logger.debug("query_for_list: I thought about kicking this up the chain => %s" % error) # Convert multi-item tuple into list return [result for result in results or []], metadata def query_for_int(self, sql_query, args = None): """Execute a query that results in an int value, given static SQL. If args is provided, bind the arguments (to avoid SQL injection attacks).""" return self.query_for_object(sql_query, args, types.IntType) def query_for_long(self, sql_query, args = None): """Execute a query that results in an int value, given static SQL. If args is provided, bind the arguments (to avoid SQL injection attacks).""" return self.query_for_object(sql_query, args, types.LongType) def query_for_object(self, sql_query, args = None, required_type = None): """Execute a query that results in an int value, given static SQL. If args is provided, bind the arguments (to avoid SQL injection attacks).""" # This is the case where only two, non-named arguments were provided, the sql_query and one other. # If the second argument was 'args', it is invalid since 'required_type' is required. # It is was 'required_type', it shifted into 'args' position, and requires naming. if args and not required_type: raise ArgumentMustBeNamed(arg_name="required_type") results = self.query_for_list(sql_query, args) if len(results) != 1: raise IncorrectResultSizeDataAccessException("Instead of getting one row, this query returned %s" % len(results)) if len(results[0]) != 1: raise IncorrectResultSizeDataAccessException("Instead of getting one column, this query returned %s" % len(results[0])) equivalentTypes = [ [types.UnicodeType, types.StringType] ] if type(results[0][0]) != required_type: foundEquivType = False for equivType in equivalentTypes: if type(results[0][0]) in equivType and required_type in equivType: foundEquivType = True break if not foundEquivType: raise DataAccessException("Expected %s, but instead got %s"% (required_type, type(results[0][0]))) return results[0][0] def update(self, sql_statement, args = None): """Issue a single SQL update. If args is provided, bind the arguments (to avoid SQL injection attacks).""" return self.execute(sql_statement, args) class RowMapper(object): """ This is an interface to handle one row of data. """ def map_row(self, row, metadata=None): raise NotImplementedError() class DictionaryRowMapper(RowMapper): """ This row mapper converts the tuple into a dictionary using the column names as the keys. """ def map_row(self, row, metadata=None): if metadata is not None: obj = {} for i, column in enumerate(metadata): obj[column["name"]] = row[i] return obj else: raise DataAccessException("metadata is None, unable to convert result set into a dictionary") class SimpleRowMapper(RowMapper): """ This row mapper uses convention over configuration to create and populate attributes of an object. """ def __init__(self, clazz): self.clazz = clazz def map_row(self, row, metadata=None): if metadata is not None: obj = self.clazz() for i, column in enumerate(metadata): setattr(obj, column["name"], row[i]) return obj else: raise DataAccessException("metadata is None, unable to map result set into %s instance" % self.clazz) springpython-1.2.0+ds/springpython/util.py0000644000000000000000000000346011412453030017445 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import traceback from threading import RLock, currentThread try: from cStringIO import StringIO except ImportError, e: from StringIO import StringIO TRACE1 = 6 logging.addLevelName(TRACE1, "TRACE1") # Original code by Anand Balachandran Pillai (abpillai at gmail.com) # http://code.activestate.com/recipes/533135/ class synchronized(object): """ Class enapsulating a lock and a function allowing it to be used as a synchronizing decorator making the wrapped function thread-safe """ def __init__(self, *args): self.lock = RLock() self.logger = logging.getLogger("springpython.util.synchronized") def __call__(self, f): def lockedfunc(*args, **kwargs): try: self.lock.acquire() self.logger.log(TRACE1, "Acquired lock [%s] thread [%s]" % (self.lock, currentThread())) try: return f(*args, **kwargs) except Exception, e: raise finally: self.lock.release() self.logger.log(TRACE1, "Released lock [%s] thread [%s]" % (self.lock, currentThread())) return lockedfunc springpython-1.2.0+ds/springpython/__init__.py0000644000000000000000000000121211412453030020220 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ springpython-1.2.0+ds/springpython/LICENSE.txt0000644000000000000000000002613511364062063017755 0ustar rootroot Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed 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. springpython-1.2.0+ds/springpython/security/0000755000000000000000000000000011544154725020001 5ustar rootrootspringpython-1.2.0+ds/springpython/security/__init__.py0000644000000000000000000000262311412453030022076 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ class SecurityException(Exception): pass class AuthenticationException(SecurityException): pass class BadCredentialsException(AuthenticationException): pass class DisabledException(AuthenticationException): pass class LockedException(AuthenticationException): pass class AccessDeniedException(SecurityException): pass class AuthenticationCredentialsNotFoundException(AuthenticationException): pass class UsernameNotFoundException(BadCredentialsException): pass class AuthenticationServiceException(AccessDeniedException): pass import sys, os, os.path if "java" in sys.platform.lower(): from glob import glob jars = os.path.join(glob("%s/lib/*.jar" % os.path.dirname(os.path.abspath(__file__)))) sys.path.extend(jars) springpython-1.2.0+ds/springpython/security/intercept.py0000644000000000000000000002246211412453030022337 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import re from springpython.security import AuthenticationCredentialsNotFoundException from springpython.security.context import SecurityContextHolder from springpython.aop import MethodInterceptor from springpython.aop import MethodInvocation logger = logging.getLogger("springpython.security.intercept") class ObjectDefinitionSource(object): """Implemented by classes that store and can identify the ConfigAttributeDefinition that applies to a given secure object invocation.""" def get_attributes(obj): """Accesses the ConfigAttributeDefinition that applies to a given secure object.""" raise NotImplementedError() def get_conf_attr_defs(): """If available, all of the ConfigAttributeDefinitions defined by the implementing class.""" raise NotImplementedError() def supports(cls): """Indicates whether the ObjectDefinitionSource implementation is able to provide ConfigAttributeDefinitions for the indicated secure object type.""" raise NotImplementedError() class InterceptorStatusToken(object): """ A return object received by AbstractSecurityInterceptor subclasses. This class reflects the status of the security interception, so that the final call to AbstractSecurityInterceptor.after_invocation(InterceptorStatusToken, Object) can tidy up correctly. """ def __init__(self, authentication = None, attr = None, secure_obj = None): self.authentication = authentication self.attr = attr self.secure_obj = secure_obj class AbstractSecurityInterceptor(object): """ Abstract class that implements security interception for secure objects. It will implements the proper handling of secure object invocations, being: 1. Obtain the Authentication object from the SecurityContextHolder. 2. Determine if the request relates to a secured or public invocation by looking up the secure object request against the ObjectDefinitionSource. 3. For an invocation that is secured (there is a ConfigAttributeDefinition for the secure object invocation): 1. If either the Authentication.isAuthenticated() returns false, or the alwaysReauthenticate is true, authenticate the request against the configured AuthenticationManager. When authenticated, replace the Authentication object on the SecurityContextHolder with the returned value. 2. Authorize the request against the configured AccessDecisionManager. (3. Perform any run-as replacement via the configured RunAsManager. FUTURE) 4. Pass control back to the concrete subclass, which will actually proceed with executing the object. An InterceptorStatusToken is returned so that after the subclass has finished proceeding with execution of the object, its finally clause can ensure the AbstractSecurityInterceptor is re-called and tidies up correctly. 5. The concrete subclass will re-call the AbstractSecurityInterceptor via the after_invocation(InterceptorStatusToken, Object) method. (6. If the RunAsManager replaced the Authentication object, return the SecurityContextHolder to the object that existed after the call to AuthenticationManager. FUTURE) 7. If an AfterInvocationManager is defined, invoke the invocation manager and allow it to replace the object due to be returned to the caller. (4. For an invocation that is public (there is no ConfigAttributeDefinition for the secure object invocation): 1. As described above, the concrete subclass will be returned an InterceptorStatusToken which is subsequently re-presented to the AbstractSecurityInterceptor after the secure object has been executed. The AbstractSecurityInterceptor will take no further action when its after_invocation(InterceptorStatusToken, Object) is called. FUTURE) 5. Control again returns to the concrete subclass, along with the Object that should be returned to the caller. The subclass will then return that result or exception to the original caller. """ def __init__(self, auth_manager = None, access_decision_mgr = None, obj_def_source = None): self.auth_manager = auth_manager self.access_decision_mgr = access_decision_mgr self.obj_def_source = obj_def_source self.logger = logging.getLogger("springpython.security.intercept.AbstractSecurityInterceptor") def obtain_obj_def_source(self): raise NotImplementedError() def before_invocation(self, invocation): attr = self.obtain_obj_def_source().get_attributes(invocation) if attr: self.logger.debug("Secure object: %s; ConfigAttributes: %s" % (invocation, attr)) if not SecurityContextHolder.getContext().authentication: raise AuthenticationCredentialsNotFoundException("An Authentication object was not found in the security credentials") if not SecurityContextHolder.getContext().authentication.isAuthenticated(): authenticated = self.auth_manager.authenticate(SecurityContextHolder.getContext().authentication) self.logger.debug("Successfully Authenticated: " + authenticated) SecurityContextHolder.getContext().authentication = authenticated else: authenticated = SecurityContextHolder.getContext().authentication self.logger.debug("Previously Authenticated: %s" % authenticated) self.access_decision_mgr.decide(authenticated, invocation, attr) self.logger.debug("Authorization successful") return InterceptorStatusToken(authenticated, attr, invocation) else: return None def after_invocation(self, token, results): """As a minimum, this needs to pass the results right on through. Subclasses can extend this behavior to utilize the token information.""" return results class AbstractMethodDefinitionSource(ObjectDefinitionSource): """Abstract implementation of ObjectDefinitionSource.""" def get_attributes(self, obj): try: module_name = obj.instance.__module__ class_name = obj.instance.__class__.__name__ method_name = obj.method_name full_method_name = "%s.%s.%s" % (module_name, class_name, method_name) return self.lookupAttributes(full_method_name) except AttributeError: raise TypeError("obj must be a MethodInvocation") def lookupAttributes(self, method): raise NotImplementedError() class MethodDefinitionMap(AbstractMethodDefinitionSource): """ Stores an obj_def_source for each method signature defined in a object. Regular expressions are used to match a method request in a ConfigAttributeDefinition. The order of registering the regular expressions is very important. The system will identify the first matching regular expression for a given method. It will not proceed to evaluate later regular expressions if a match has already been found. Accordingly, the most specific regular expressions should be registered first, with the most general regular expressions registered last. """ def __init__(self, obj_def_source): self.obj_def_source = obj_def_source def lookupAttributes(self, method): if self.obj_def_source: for rule, attr in self.obj_def_source: if re.compile(rule).match(method): return attr return None class MethodSecurityInterceptor(MethodInterceptor, AbstractSecurityInterceptor): """ Provides security interception of Spring Python AOP-based method invocations. The ObjectDefinitionSource required by this security interceptor is of type MethodDefinitionMap. Refer to AbstractSecurityInterceptor for details on the workflow. """ def __init__(self): MethodInterceptor.__init__(self) AbstractSecurityInterceptor.__init__(self) self.validate_config_attributes = False self.obj_def_source = None def __setattr__(self, name, value): if name == "obj_def_source" and value is not None: self.__dict__[name] = MethodDefinitionMap(value) else: self.__dict__[name] = value def obtain_obj_def_source(self): return self.obj_def_source def invoke(self, invocation): token = self.before_invocation(invocation) results = None try: results = invocation.proceed() finally: results = self.after_invocation(token, results) return results springpython-1.2.0+ds/springpython/security/web.py0000644000000000000000000004672211412453030021124 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import Cookie import logging import re import pickle import types from springpython.context import ApplicationContextAware from springpython.aop import utils from springpython.security import AccessDeniedException from springpython.security import AuthenticationException from springpython.security.context import SecurityContext from springpython.security.context import SecurityContextHolder from springpython.security.intercept import AbstractSecurityInterceptor from springpython.security.intercept import ObjectDefinitionSource from springpython.security.providers import UsernamePasswordAuthenticationToken logger = logging.getLogger("springpython.security.web") class Filter(object): """This is the interface definition of a filter. It must process a request/response.""" def doNextFilter(self, environ, start_response): results = None try: nextFilter = environ["SPRINGPYTHON_FILTER_CHAIN"].next() if isinstance(nextFilter, tuple): func = nextFilter[0] args = nextFilter[1] results = func(args) else: results = nextFilter(environ, start_response) except StopIteration: pass # Apparently, passing back a generator trips up CherryPy and causes it to skip # the filters. If a generator is detected, convert it to a standard array. if type(results) == types.GeneratorType: results = [line for line in results] return results class FilterChain(object): """ Collection of WSGI filters. It allows dynamic re-chaining of filters as the situation is needed. In order to link in 3rd party WSGI middleware, see MiddlewareFilter. """ def __init__(self): self.chain = [] def addFilter(self, filter): self.chain.append(filter) def getFilterChain(self): for filter in self.chain: yield filter class FilterChainProxy(Filter, ApplicationContextAware): """ This acts as filter, and delegates to a chain of filters. Each time a web page is called, it dynamically assembles a FilterChain, and then iterates over it. This is different than the conventional style of wrapping applications for WSGI, because each URL pattern might have a different chained combination of the WSGI filters. Because most middleware objects define the wrapped application using __init__, Spring provides the MiddlewareFilter, to help wrap any middleware object so that it can participate in a FilterChain. """ def __init__(self, filterInvocationDefinitionSource=None): """This class must be application-context aware in case it is instantiated inside an IoC container.""" ApplicationContextAware.__init__(self) if filterInvocationDefinitionSource is None: self.filterInvocationDefinitionSource = [] else: self.filterInvocationDefinitionSource = filterInvocationDefinitionSource self.logger = logging.getLogger("springpython.security.web.FilterChainProxy") self.application = None def __call__(self, environ, start_response): """This will route all requests/responses through the chain of filters.""" filterChain = FilterChain() for urlPattern, chainOfFilters in self.filterInvocationDefinitionSource: if re.compile(urlPattern).match(environ["PATH_INFO"].lower()): self.logger.debug("We had a match of %s against %s" % (environ["PATH_INFO"], urlPattern)) for filter in chainOfFilters: try: filterChain.addFilter(self.app_context.get_object(filter)) except AttributeError, e: filterChain.addFilter(filter) break # Put the actual application on the end of the chain. if self.application: filterChain.addFilter(self.application) environ["SPRINGPYTHON_FILTER_CHAIN"] = filterChain.getFilterChain() return self.doNextFilter(environ, start_response) class SessionStrategy(object): """ This is an interface definition in defining access to session data. There may be many ways to implement session data. This makes the mechanism pluggable. """ def getHttpSession(self, environ): raise NotImplementedError() def setHttpSession(self, key, value): raise NotImplementedError() class HttpSessionContextIntegrationFilter(Filter): """ This filter is meant to pull security context information from the HttpSession, and store it in the SecurityContextHolder. Then on the response, copy and SecurityContext information back into the HttpSession. """ # Key to the SecurityContext data stored in an HttpSession dictionary. SPRINGPYTHON_SECURITY_CONTEXT_KEY = "SPRINGPYTHON_SECURITY_CONTEXT_KEY" # Class name used context = SecurityContext def __init__(self, sessionStrategy=None): self.sessionStrategy = sessionStrategy self.logger = logging.getLogger("springpython.security.web.HttpSessionContextIntegrationFilter") def __call__(self, environ, start_response): """This filter copies SecurityContext information back and forth between the HttpSession and the SecurityContextHolder.""" httpSession = self.sessionStrategy.getHttpSession(environ) contextWhenChainProceeded = None if httpSession is not None: contextFromSessionObject = None if self.SPRINGPYTHON_SECURITY_CONTEXT_KEY in httpSession: contextFromSessionObject = pickle.loads(httpSession[self.SPRINGPYTHON_SECURITY_CONTEXT_KEY]) if contextFromSessionObject is not None: if isinstance(contextFromSessionObject, SecurityContext): self.logger.debug("Obtained from SPRINGPYTHON_SECURITY_CONTEXT_KEY a valid SecurityContext and set " + "to SecurityContextHolder: '%s'" % contextFromSessionObject) SecurityContextHolder.setContext(contextFromSessionObject) else: self.logger.warn("SPRINGPYTHON_SECURITY_CONTEXT_KEY did not contain a SecurityContext but contained: '%s'" % contextFromSessionObject + "'; are you improperly modifying the HttpSession directly (you should always use " + "SecurityContextHolder) or using the HttpSession attribute reserved for this class? " + "- new SecurityContext instance associated with SecurityContextHolder") SecurityContextHolder.setContext(self.generateNewContext()) else: self.logger.debug("HttpSession returned null object for SPRINGPYTHON_SECURITY_CONTEXT_KEY " + "- new SecurityContext instance associated with SecurityContextHolder") SecurityContextHolder.setContext(self.generateNewContext()) else: self.logger.debug("No HttpSession currently exists - new SecurityContext instance associated with SecurityContextHolder") SecurityContextHolder.setContext(self.generateNewContext()) self.logger.debug("Setting contextWhenChainProceeded to %s" % SecurityContextHolder.getContext()) contextWhenChainProceeded = str(SecurityContextHolder.getContext()) results = self.doNextFilter(environ, start_response) self.sessionStrategy.setHttpSession(self.SPRINGPYTHON_SECURITY_CONTEXT_KEY, pickle.dumps(SecurityContextHolder.getContext())) self.logger.debug("SecurityContext stored to HttpSession: '%s'" % SecurityContextHolder.getContext()) SecurityContextHolder.clearContext() self.logger.debug("SecurityContextHolder cleared out, as request processing completed") return results def setContext(self, clazz): """This is a factory setter. The context parameter is used to create new security context objects.""" self.context = clazz def generateNewContext(self): """This is a factory method that instantiates the assigned class, and populates it with an empty token.""" context = self.context() context.authentication = UsernamePasswordAuthenticationToken() return context def saveContext(self): self.sessionStrategy.setHttpSession(self.SPRINGPYTHON_SECURITY_CONTEXT_KEY, pickle.dumps(SecurityContextHolder.getContext())) class RedirectStrategy(object): """ This class provides a mechanism to redirect users to another page. Currently, it returns a standard forwarding message to the browser. This may not be the most efficient, but it guarantees the entire WSGI stack is processed on both request and response. """ def redirect(self, url): """This is a 0-second redirect.""" return """""" % url class AuthenticationProcessingFilter(Filter): """ This filter utilizes the authentication manager to make sure the requesting person is authenticated. It expects the SecurityContextHolder to be populated when it runs, so it is always good to preceed it with the HttpSessionContextIntegrationFilter. """ def __init__(self, auth_manager=None, alwaysReauthenticate=False): self.auth_manager = auth_manager self.alwaysReauthenticate = alwaysReauthenticate self.logger = logging.getLogger("springpython.security.web.AuthenticationProcessingFilter") def __call__(self, environ, start_response): """ Check if the user is trying to access the login url. Then see if they are already authenticated (and alwaysReauthenticate is disabled). Finally, try to authenticate the user. If successful, stored credentials in SecurityContextHolder. Otherwise, redirect to the login page. """ # If the user is already authenticated, skip this filter. if not self.alwaysReauthenticate and SecurityContextHolder.getContext().authentication.isAuthenticated(): self.logger.debug("You are not required to reauthenticate everytime, and appear to already be authenticted, access GRANTED.") return self.doNextFilter(environ, start_response) try: # Authenticate existing credentials using the authentication manager. token = SecurityContextHolder.getContext().authentication self.logger.debug("Trying to authenticate %s using the authentication manager" % token) SecurityContextHolder.getContext().authentication = self.auth_manager.authenticate(token) self.logger.debug("%s was successfully authenticated, access GRANTED." % token.username) except AuthenticationException, e: self.logger.debug("Authentication failure, access DENIED.") raise return self.doNextFilter(environ, start_response) def logout(self): SecurityContextHolder.getContext().authentication = UsernamePasswordAuthenticationToken() class FilterInvocation: """Holds objects associated with a WSGI filter, such as environ. This is the web-application equivalent to MethodInvocation.""" def __init__(self, environ): self.environ = environ def requestUrl(self): return self.environ["PATH_INFO"] class AbstractFilterInvocationDefinitionSource(ObjectDefinitionSource): """Abstract implementation of ObjectDefinitionSource.""" def get_attributes(self, obj): try: return self.lookupAttributes(obj.requestUrl()) except AttributeError: raise TypeError("obj must be a FilterInvocation") def lookupAttributes(self, url): raise NotImplementedError() class RegExpBasedFilterInvocationDefinitionMap(AbstractFilterInvocationDefinitionSource): """ Maintains a list of ObjectDefinitionSource's associated with different HTTP request URL regular expression patterns. Regular expressions are used to match a HTTP request URL against a ConfigAttributeDefinition. The order of registering the regular expressions is very important. The system will identify the first matching regular expression for a given HTTP URL. It will not proceed to evaluate later regular expressions if a match has already been found. Accordingly, the most specific regular expressions should be registered first, with the most general regular expressions registered last. """ def __init__(self, obj_def_source): self.obj_def_source = obj_def_source def lookupAttributes(self, url): if self.obj_def_source: for rule, attr in self.obj_def_source: if re.compile(rule).match(url): return attr return None class FilterSecurityInterceptor(Filter, AbstractSecurityInterceptor): """ Performs security handling of HTTP resources via a filter implementation. The ObjectDefinitionSource required by this security interceptor is of type AbstractFilterInvocationDefinitionSource. Refer to AbstractSecurityInterceptor for details on the workflow. """ # Key to the FilterSecurityInterceptor's token data stored in an HttpSession dictionary. SPRINGPYTHON_FILTER_SECURITY_INTERCEPTOR_KEY = "SPRINGPYTHON_FILTER_SECURITY_INTERCEPTOR_KEY" def __init__(self, auth_manager = None, access_decision_mgr = None, obj_def_source = None, sessionStrategy=None): Filter.__init__(self) AbstractSecurityInterceptor.__init__(self, auth_manager, access_decision_mgr, obj_def_source) self.sessionStrategy = sessionStrategy self.obj_def_source = obj_def_source def __setattr__(self, name, value): if name == "obj_def_source" and value is not None: self.__dict__[name] = RegExpBasedFilterInvocationDefinitionMap(value) else: self.__dict__[name] = value def obtain_obj_def_source(self): return self.obj_def_source def __call__(self, environ, start_response): httpSession = self.sessionStrategy.getHttpSession(environ) self.logger.debug("Trying to check if you are authorized for this.") fi = FilterInvocation(environ) token = self.before_invocation(fi) if httpSession is not None: httpSession[self.SPRINGPYTHON_FILTER_SECURITY_INTERCEPTOR_KEY] = token return self.doNextFilter(environ, start_response) if httpSession is not None and self.SPRINGPYTHON_FILTER_SECURITY_INTERCEPTOR_KEY in httpSession: token = httpSession[self.SPRINGPYTHON_FILTER_SECURITY_INTERCEPTOR_KEY] self.after_invocation(token, None) return results class ExceptionTranslationFilter(Filter): """ Handles any AccessDeniedException and AuthenticationException thrown within the filter chain. This filter is necessary because it provides the bridge between Python exceptions and HTTP responses. It is solely concerned with maintaining the user interface. This filter does not do any actual security enforcement. If an AuthenticationException is detected, the filter will launch the authenticationEntryPoint. This allows common handling of authentication failures originating from any subclass of AuthenticationProcessingFilter. If an AccessDeniedException is detected, the filter will launch the accessDeniedHandler. This allows common handling of access failures originating from any subclass of AbstractSecurityInterceptor. """ def __init__(self, authenticationEntryPoint=None, accessDeniedHandler=None, redirectStrategy=None): Filter.__init__(self) self.authenticationEntryPoint = authenticationEntryPoint self.accessDeniedHandler = accessDeniedHandler self.logger = logging.getLogger("springpython.security.web.ExceptionTranslationFilter") def __call__(self, environ, start_response): try: return self.doNextFilter(environ, start_response) except AuthenticationException, e: self.logger.debug("AuthenticationException => %s, redirecting through authenticationEntryPoint" % e) return self.authenticationEntryPoint(environ, start_response) except AccessDeniedException, e: self.logger.debug("AccessDeniedException => %s, redirect through accessDeniedHandler" % e) return self.accessDeniedHandler(environ, start_response) class AuthenticationProcessingFilterEntryPoint(Filter): """This object holds the location of the login form, and is used to commence a redirect to that form.""" def __init__(self, loginFormUrl=None, redirectStrategy=None): Filter.__init__(self) self.loginFormUrl = loginFormUrl self.redirectStrategy = redirectStrategy self.logger = logging.getLogger("springpython.security.web.AuthenticationProcessingFilterEntryPoint") def __call__(self, environ, start_response): self.logger.debug("Redirecting to login page %s" % self.loginFormUrl) return self.redirectStrategy.redirect(self.loginFormUrl) class AccessDeniedHandler(Filter): """Used by ExceptionTranslationFilter to handle an AccessDeniedException.""" def __init__(self): Filter.__init__(self) class SimpleAccessDeniedHandler(AccessDeniedHandler): """A simple default implementation of the AccessDeniedHandler interface.""" def __init__(self, errorPage=None, redirectStrategy=None): AccessDeniedHandler.__init__(self) self.errorPage = errorPage self.redirectStrategy = redirectStrategy self.logger = logging.getLogger("springpython.security.web.SimpleAccessDeniedHandler") def __call__(self, environ, start_response): self.logger.debug("Redirecting to error page %s" % self.errorPage) return self.redirectStrategy.redirect(self.errorPage) class MiddlewareFilter(Filter): """ This filter allows you to wrap any WSGI-compatible middleware and use it as a Spring Python filter. This is primary because lots of middleware objects requires the wrapped WSGI app to be included in the __init__ method. Spring's IoC container currently doesn't support constructor arguments. """ def __init__(self, clazz = None, appAttribute = None): Filter.__init__(self) self.clazz = clazz self.appAttribute = appAttribute def __setattr__(self, name, value): if name == "clazz" and value is not None: self.__dict__[name] = value self.middleware = utils.getClass(value)(None) else: self.__dict__[name] = value def __call__(self, environ, start_response): setattr(self.middleware, self.appAttribute, environ["SPRINGPYTHON_FILTER_CHAIN"].next()) return self.middleware(environ, start_response) springpython-1.2.0+ds/springpython/security/userdetails/0000755000000000000000000000000011544154725022325 5ustar rootrootspringpython-1.2.0+ds/springpython/security/userdetails/__init__.py0000644000000000000000000000553111412453030024423 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging from springpython.security import UsernameNotFoundException class User(object): """ Models core user information retieved by an UserDetailsService. """ def __init__(self, username, password, enabled, accountNonExpired=True, accountNonLocked=True, credentialsNonExpired=True, authorities=None): self.username = username self.password = password if authorities is None: self.authorities = [] else: self.authorities = authorities self.accountNonExpired = accountNonExpired self.accountNonLocked = accountNonLocked self.credentialsNonExpired = credentialsNonExpired self.enabled = enabled def __str__(self): return "Username: %s Password: [PROTECTED] Authorities: %s Enabled: %s" % (self.username, self.authorities, self.enabled) class UserDetailsService(object): """ Defines an interface for implementations that wish to provide data access services to the DaoAuthenticationProvider. The interface requires only one read-only method, which simplifies support of new data access strategies. """ def load_user(self, username): raise NotImplementedError() class InMemoryUserDetailsService(UserDetailsService): def __init__(self, user_dict = None): super(InMemoryUserDetailsService, self).__init__() if user_dict is None: self.user_dict = {} else: self.user_dict = user_dict self.logger = logging.getLogger("springpython.security.userdetails.InMemoryUserDetailsService") def load_user(self, username): if username in self.user_dict and len(self.user_dict[username][1]) > 0: self.logger.debug("Found %s in %s" % (username, self.user_dict)) return User(username, self.user_dict[username][0], self.user_dict[username][2], True, True, True, self.user_dict[username][1]) error = None if username not in self.user_dict: error = UsernameNotFoundException("User not found in %s" % self.user_dict) else: error = UsernameNotFoundException("User has no GrantedAuthority") self.logger.debug(error) raise error springpython-1.2.0+ds/springpython/security/userdetails/dao.py0000644000000000000000000001013611412453030023424 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging from springpython.database.core import DatabaseTemplate from springpython.database.core import RowMapper from springpython.security import UsernameNotFoundException from springpython.security.userdetails import User from springpython.security.userdetails import UserDetailsService class DatabaseUserDetailsService(UserDetailsService): """ Retrieves user details (username, password, enabled flag, and authorities) from a database location. A default database structure is assumed, (see DEF_USERS_BY_USERNAME_QUERY and DEF_AUTHORITIES_BY_USERNAME_QUERY, which most users of this class will need to override, if using an existing scheme. This may be done by setting the default query strings used. If this does not provide enough flexibility, another strategy would be to subclass this class and override relevant parts. In order to minimise backward compatibility issues, this DAO does not recognise the expiration of user accounts or the expiration of user credentials. However, it does recognise and honour the user enabled/disabled column. """ DEF_USERS_BY_USERNAME_QUERY = "SELECT username,password,enabled FROM users WHERE username = ?" DEF_AUTHORITIES_BY_USERNAME_QUERY = "SELECT username,authority FROM authorities WHERE username = ?" class UsersByUsernameMapping(RowMapper): """A row handler that processes one user entry.""" def map_row(self, row, metadata=None): username = row[0] password = row[1] enabled = row[2] return User(username, password, enabled, True, True, True, ["HOLDER"]) class AuthoritiesByUsernameMapping(RowMapper): """A row handler that processes one granted authority for a given user.""" def __init__(self, role_prefix): self.role_prefix = role_prefix def map_row(self, row, metadata=None): return self.role_prefix + row[1] def __init__(self, dataSource = None): super(DatabaseUserDetailsService, self).__init__() self.users_by_username_query = self.DEF_USERS_BY_USERNAME_QUERY self.auth_by_username_query = self.DEF_AUTHORITIES_BY_USERNAME_QUERY self.dataSource = dataSource self.role_prefix = "" self.username_based_pk = True self.logger = logging.getLogger("springpython.security.userdetails.DatabaseUserDetailsService") def load_user(self, username): dt = DatabaseTemplate(self.dataSource) users = dt.query(self.users_by_username_query, (username,), self.UsersByUsernameMapping()) if len(users) == 0: raise UsernameNotFoundException("User not found") user = users[0] # First item in list, first column of tuple, containing no GrantedAuthority[] dbAuths = dt.query(self.auth_by_username_query, (user.username,), self.AuthoritiesByUsernameMapping(self.role_prefix)) self.add_custom_authorities(user.username, dbAuths) if len(dbAuths) == 0: raise UsernameNotFoundException("User has no GrantedAuthority") auths = [dbAuth for dbAuth in dbAuths] return_username = user.username if not self.username_based_pk: return_username = username self.logger.debug("Just fetched %s from the database" % user) return User(return_username, user.password, user.enabled, True, True, True, auths) def add_custom_authorities(self, username, authorities): pass springpython-1.2.0+ds/springpython/security/providers/0000755000000000000000000000000011544154725022016 5ustar rootrootspringpython-1.2.0+ds/springpython/security/providers/_Ldap_cpython.py0000644000000000000000000002226411464332043025151 0ustar rootroot""" Copyright 2006-2009 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import re import sys from springpython.security import AuthenticationException from springpython.security import AuthenticationServiceException from springpython.security import BadCredentialsException from springpython.security import DisabledException from springpython.security import UsernameNotFoundException from springpython.security.providers import AuthenticationProvider from springpython.security.providers import UsernamePasswordAuthenticationToken from springpython.security.providers.dao import AbstractUserDetailsAuthenticationProvider from springpython.security.providers.encoding import LdapShaPasswordEncoder """ The ldap library only works with CPython. You should NOT import this library directly. """ import ldap class DefaultSpringSecurityContextSource(object): """ This class is used to define the url of the ldap server. It expects a string like ldap://:/ It provides functions to retrieve the parts """ def __init__(self, url=None): self.url = url def server(self): """Extract the server's hostname/port from the url.""" return self.url.split("ldap://")[1].split("/")[0].split(":") def base(self): """Extract the baseDN from the url.""" return self.url.split("ldap://")[1].split("/")[1] class BindAuthenticator(object): """ This ldap authenticator uses binding to confirm the user's password. This means the password encoding depends on the ldap library's API as well as the directory server; NOT Spring Python's password hashing algorithms. """ def __init__(self, context_source=None, user_dn_patterns="uid={0},ou=people"): self.context_source = context_source self.user_dn_patterns = user_dn_patterns self.logger = logging.getLogger("springpython.security.providers.Ldap.BindAuthenticator") def authenticate(self, authentication): """Using the user_dn_patterns, find the user's entry, and then bind to the entry using supplied credentials.""" username = self.user_dn_patterns.replace("{0}", authentication.username) baseDn = self.context_source.base() parts = username.split(",") if len(parts) > 1: username = parts[0] baseDn = ",".join(parts[1:]) + "," + baseDn (host, port) = self.context_source.server() self.logger.debug("Opening connection to server %s/%s" % (host, int(port))) l = ldap.open(host, int(port)) self.logger.debug("Searching for %s in %s" % (username, baseDn)) result_set = l.search_s(baseDn, ldap.SCOPE_SUBTREE, username, None) if len(result_set) != 1: raise BadCredentialsException("Found %s entries at %s/%s. Should only be 1." % (len(result_set), baseDn, username)) dn = result_set[0][0] self.logger.debug("Attempting to bind %s" % dn) try: l.simple_bind_s(dn, authentication.password) self.logger.debug("Successfully bound to server!") return (result_set[0],l) except Exception, e: self.logger.debug("Error %s" % e) raise BadCredentialsException("Invalid password") class PasswordComparisonAuthenticator(object): """ This ldap authenticator uses string comparison to confirm the user's password. This means a password encoder must be provided, or the default LdapShaPasswordEncoder will be used. It searched for the user's entry, fetches the password, and then does a string comparison to confirm the password. """ def __init__(self, context_source=None, user_dn_patterns="uid={0},ou=people", password_attr_name="userPassword"): self.context_source = context_source self.user_dn_patterns = user_dn_patterns self.password_attr_name = password_attr_name self.encoder = LdapShaPasswordEncoder() self.logger = logging.getLogger("springpython.security.providers.Ldap.PasswordComparisonAuthenticator") def authenticate(self, authentication): """ Using the user_dn_patterns, find the user's entry, and then retrieve the password field. Encode the supplied password with the necessary hasher, and compare to the entry. """ username = self.user_dn_patterns.replace("{0}", authentication.username) baseDn = self.context_source.base() parts = username.split(",") if len(parts) > 1: username = parts[0] baseDn = ",".join(parts[1:]) + "," + baseDn (host, port) = self.context_source.server() self.logger.debug("Opening connection to server %s/%s" % (host, int(port))) l = ldap.open(host, int(port)) self.logger.debug("Searching for %s in %s" % (username, baseDn)) result_set = l.search_s(baseDn, ldap.SCOPE_SUBTREE, username, None) if len(result_set) != 1: raise BadCredentialsException("Found %s entries at %s/%s. Should only be 1." % (len(result_set), baseDn, username)) self.logger.debug("Looking for attributes...%s" % result_set[0][1]) stored_password = result_set[0][1][self.password_attr_name.lower()][0] self.logger.debug("Comparing passwords...") if self.encoder.isPasswordValid(stored_password, authentication.password, None): self.logger.debug("Successfully matched passwords!") return (result_set[0],l) else: raise BadCredentialsException("Invalid password") class DefaultLdapAuthoritiesPopulator(object): """ This ldap authorities populator follows a standard convention, where groups are created, with a member attribute, pointing at user entries in another part of the directory structure. It then combines ROLE_ with the name of the group, and names that as a granted role. """ def __init__(self, context_source=None, group_search_base="ou=groups", group_search_filter="member={0}", group_role_attr="cn", role_prefix="ROLE_", convert_to_upper=True): self.logger = logging.getLogger("springpython.security.providers.Ldap.DefaultLdapAuthoritiesPopulator") self.context_source = context_source self.group_search_base = group_search_base self.group_search_filter = group_search_filter self.group_role_attr = group_role_attr self.role_prefix = role_prefix self.convert_to_upper = convert_to_upper def get_granted_auths(self, user_details, l): group_filter = self.group_search_filter.replace("{0}", user_details[0]) baseDn = self.group_search_base + "," + self.context_source.base() self.logger.debug("Searching for groups for %s" % str(user_details[0])) result_set = l.search_s(baseDn, ldap.SCOPE_SUBTREE, group_filter, None) auths = [] for row in result_set: role = self.role_prefix + row[1][self.group_role_attr][0] if self.convert_to_upper: auths.append(role.upper()) else: auths.append(role) self.logger.debug("Authorities = %s" % auths) return auths class LdapAuthenticationProvider(AuthenticationProvider): """ This authenticator performs two steps: 1) Authenticate the user to confirm their credentials. 2) Lookup roles the user has stored in the directory server. It is possible to inject any type of authenticator as well as roles populator. Spring Python includes two authenticators that perform standard binding or password comparisons. You are able to code your own and use it instead, especially if you are using a non-conventional mechanism. Spring Python includes one role populator, based on the standard convention of defining groups elsewhere in the directory server's hierarchy. However, you can inject your own if you have a non-convential structure, such as storing the roles directly in the user's directory entry. """ def __init__(self, ldap_authenticator=None, ldap_authorities_populator=None): AuthenticationProvider.__init__(self) self.ldap_authenticator = ldap_authenticator self.ldap_authorities_populator = ldap_authorities_populator self.logger = logging.getLogger("springpython.security.providers.Ldap.LdapAuthenticationProvider") def authenticate(self, authentication): user_details, l = self.ldap_authenticator.authenticate(authentication) from copy import deepcopy results = deepcopy(authentication) results.granted_auths = self.ldap_authorities_populator.get_granted_auths(user_details, l) l.unbind() return results springpython-1.2.0+ds/springpython/security/providers/__init__.py0000644000000000000000000001174311412453030024116 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging from springpython.security import AuthenticationException from springpython.security import BadCredentialsException from springpython.security import DisabledException class ProviderNotFoundException(AuthenticationException): """ An exception thrown when a list of providers are polled for a security decision, and none of them supports the request. """ pass class Authentication: """ Abstract representation of credential data. The premise is that username and password are populated, and after authentication this record is returned with the third attribute, granted authorities, populated. """ def __init__(self): self.__authenticated = False def isAuthenticated(self): return self.__authenticated def setAuthenticated(self, authenticated): self.__authenticated = authenticated def getCredentials(self): raise NotImplementedError() def __str__(self): raise AuthenticationException("You should be using a concrete authentication object") class UsernamePasswordAuthenticationToken(Authentication): """ A basic concrete version of authentication. Works for most scenarios. """ def __init__(self, username = None, password = None, granted_auths = None): Authentication.__init__(self) self.username = username self.password = password if granted_auths is None: self.granted_auths = [] else: self.granted_auths = granted_auths def getCredentials(self): return self.password def __str__(self): return "[UsernamePasswordAuthenticationToken] User: [%s] Password: [PROTECTED] GrantedAuthorities: %s Authenticated: %s" % \ (self.username, self.granted_auths, self.isAuthenticated()) class AuthenticationManager: """ Iterates an Authentication request through a list of AuthenticationProviders. AuthenticationProviders are tried in order until one provides a non-null response. A non-null response indicates the provider had authority to decide on the authentication request and no further providers are tried. If an AuthenticationException is thrown by a provider, it is retained until subsequent providers are tried. If a subsequent provider successfully authenticates the request, the earlier authentication exception is disregarded and the successful authentication will be used. If no subsequent provider provides a non-null response, or a new AuthenticationException, the last AuthenticationException received will be used. If no provider returns a non-null response, or indicates it can even process an Authentication, the AuthenticationManager will throw a ProviderNotFoundException. """ def __init__(self, auth_providers = None): if auth_providers is None: self.auth_providers = [] else: self.auth_providers = auth_providers self.logger = logging.getLogger("springpython.security.providers.AuthenticationManager") def authenticate(self, authentication): """ Attempts to authenticate the passed Authentication object, returning a fully populated Authentication object (including granted authorities) if successful. """ authenticationException = ProviderNotFoundException() for auth_provider in self.auth_providers: try: results = auth_provider.authenticate(authentication) if results: results.setAuthenticated(True) return results except DisabledException, e: # Disabled means account found, but invalid raise e except AuthenticationException, e: authenticationException = e raise authenticationException class AuthenticationProvider(object): """ Indicates a class can process a specific Authentication implementation. """ def authenticate(self, authentication): """ Performs authentication with the same contract as AuthenticationManager.authenticate(Authentication). """ raise NotImplementedError() def supports(self, authentication): """ Returns true if this AuthenticationProvider supports the indicated Authentication object. """ raise NotImplementedError() springpython-1.2.0+ds/springpython/security/providers/encoding.py0000644000000000000000000001657311412453030024153 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging class PasswordEncoder(object): """Interface for performing authentication operations on a password.""" def encodePassword(self, rawPass, salt): """Encodes the specified raw password with an implementation specific algorithm.""" raise NotImplementedError() def isPasswordValid(self, encPass, rawPass, salt): """Validates a specified "raw" password against an encoded password.""" raise NotImplementedError() class BasePasswordEncoder(PasswordEncoder): """Convenience base for all password encoders.""" def __init__(self): super(BasePasswordEncoder, self).__init__() self.ignorePasswordCase = False self.logger = logging.getLogger("springpython.security.providers.encoding.BasePasswordEncoder") def mergePasswordAndSalt(self, password, salt, strict): """ Used by subclasses to generate a merged password and salt String. The generated password will be in the form of 'password{salt}'. A None can be passed to either parameter, and will be handled correctly. If the salt is None or empty, the resulting generated password will simply be the passed password. The __str__ method of the salt will be used to represent the salt. """ if password is None: password = "" if strict and salt is not None: if "{" in str(salt) or "}" in str(salt): raise ValueError("Cannot use { or } in salt.__str__") if salt is None or salt == "": return password else: return password + "{" + str(salt) + "}" class PlaintextPasswordEncoder(BasePasswordEncoder): """ Plaintext implementation of PasswordEncoder. As callers may wish to extract the password and salts separately from the encoded password, the salt must not contain reserved characters (specifically '{' and '}'). """ def __init__(self): super(PlaintextPasswordEncoder, self).__init__() self.logger = logging.getLogger("springpython.security.providers.encoding.PlaintextPasswordEncoder") def encodePassword(self, rawPass, salt): """Encodes the specified raw password with an implementation specific algorithm.""" return self.mergePasswordAndSalt(rawPass, salt, True) def isPasswordValid(self, encPass, rawPass, salt): """Validates a specified "raw" password against an encoded password.""" pass1 = encPass + "" # Strict delimiters is false because pass2 never persisted anywhere # and we want to avoid unnecessary exceptions as a result (the # authentication will fail as the encodePassword never allows them) pass2 = self.mergePasswordAndSalt(rawPass, salt, False) if not self.ignorePasswordCase: return pass1 == pass2 else: return pass1.upper() == pass2.upper() class AbstractOneWayPasswordEncoder(BasePasswordEncoder): """ This is an abstract one-way hashing encoder. It is abstract because the subclasses have to plugin their strategy. """ def __init__(self): super(AbstractOneWayPasswordEncoder, self).__init__() self.onewayHasher = None self.logger = logging.getLogger("springpython.security.providers.encoding.AbstractOneWayPasswordEncoder") def encodePassword(self, rawPass, salt): """Encodes the specified raw password with an implementation specific algorithm.""" hasher = self.onewayHashStrategy() if not self.ignorePasswordCase: hasher.update(self.mergePasswordAndSalt(rawPass, salt, False)) else: hasher.update(self.mergePasswordAndSalt(rawPass.lower(), salt, False)) return hasher.hexdigest() def isPasswordValid(self, encPass, rawPass, salt): """Validates a specified "raw" password against an encoded password.""" pass1 = encPass + "" # Strict delimiters is false because pass2 never persisted anywhere # and we want to avoid unnecessary exceptions as a result (the # authentication will fail as the encodePassword never allows them) pass2 = self.mergePasswordAndSalt(rawPass, salt, False) hasher = self.onewayHashStrategy() if self.ignorePasswordCase: hasher.update(pass2.lower()) else: hasher.update(pass2) pass2 = hasher.hexdigest() if not self.ignorePasswordCase: return pass1 == hasher.hexdigest() else: return pass1.lower() == hasher.hexdigest() class Md5PasswordEncoder(AbstractOneWayPasswordEncoder): """ MD5 implementation of PasswordEncoder. If a None password is presented, it will be treated as an empty String ("") password. As MD5 is a one-way hash, the salt can contain any characters. """ def __init__(self): super(Md5PasswordEncoder, self).__init__() try: import hashlib self.onewayHashStrategy = hashlib.md5 except ImportError: import md5 self.onewayHashStrategy = md5.new self.logger = logging.getLogger("springpython.security.providers.encoding.Md5PasswordEncoder") class ShaPasswordEncoder(AbstractOneWayPasswordEncoder): """ SHA implementation of PasswordEncoder. If a None password is presented, it will be treated as an empty String ("") password. As SHA is a one-way hash, the salt can contain any characters. """ def __init__(self): super(ShaPasswordEncoder, self).__init__() try: import hashlib self.onewayHashStrategy = hashlib.sha1 except ImportError: import sha self.onewayHashStrategy = sha.new self.logger = logging.getLogger("springpython.security.providers.encoding.ShaPasswordEncoder") class LdapShaPasswordEncoder(PasswordEncoder): def __init__(self): super(PasswordEncoder, self).__init__() self.sha_encoder = ShaPasswordEncoder() self.logger = logging.getLogger("springpython.security.providers.encoding.LdapShaPasswordEncoder") def encodePassword(self, rawPass, salt): """Encodes the specified raw password with an implementation specific algorithm.""" import base64 hasher = self.sha_encoder.onewayHashStrategy() hasher.update(rawPass) return "{SHA}" + base64.b64encode(hasher.digest()) def isPasswordValid(self, encPass, rawPass, salt): """Validates a raw password against an encrypted one. It checks the prefix, to tell if its encrypted or stored in the clear.""" if encPass.startswith("{SHA}"): return encPass == self.encodePassword(rawPass, salt) else: return encPass == rawPass springpython-1.2.0+ds/springpython/security/providers/_Ldap_jython.py0000644000000000000000000001515411464332043025000 0ustar rootroot""" Copyright 2006-2009 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import re import sys from springpython.security import AuthenticationException from springpython.security import AuthenticationServiceException from springpython.security import BadCredentialsException from springpython.security import DisabledException from springpython.security import UsernameNotFoundException from springpython.security.providers import AuthenticationProvider from springpython.security.providers import UsernamePasswordAuthenticationToken from springpython.security.providers.dao import AbstractUserDetailsAuthenticationProvider from springpython.security.providers.encoding import LdapShaPasswordEncoder """ The ldap library only works with Jython. You should NOT import this library directly. Due to the lack of a pure Python library, this version uses Spring Security/Spring LDAP jar files to perform authentication and LDAP lookups. """ import java import org.springframework.security.ldap.DefaultSpringSecurityContextSource import org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator import org.springframework.security.providers.ldap.authenticator.BindAuthenticator import org.springframework.security.providers.ldap.authenticator.PasswordComparisonAuthenticator import org.springframework.security.providers.UsernamePasswordAuthenticationToken from jarray import array print """ WARNING WARNING WARNING WARNING =============================== This doesn't yet work. There is some issue with Jython. See http://bugs.jython.org/issue1489 and http://jira.springframework.org/browse/SESPRINGPYTHONPY-121 for more details. =============================== WARNING WARNING WARNING WARNING """ class DefaultSpringSecurityContextSource(object): def __init__(self, url): self._context = org.springframework.security.ldap.DefaultSpringSecurityContextSource(url) java.lang.Thread.currentThread().setContextClassLoader(self._context.getClass().getClassLoader()) self._context.afterPropertiesSet() class BindAuthenticator(object): def __init__(self, context_source=None, user_dn_patterns="uid={0},ou=people"): self.context_source = context_source self.user_dn_patterns = user_dn_patterns self.logger = logging.getLogger("springpython.security.providers.Ldap.BindAuthenticator") self._authenticator = None def authenticate(self, authentication): if self._authenticator is None: self._authenticator = org.springframework.security.providers.ldap.authenticator.BindAuthenticator(self.context_source._context) self._authenticator.setUserDnPatterns(array([self.user_dn_patterns], java.lang.String)) self._authenticator.afterPropertiesSet() #java.lang.Thread.currentThread().setContextClassLoader(self._authenticator.getClass().getClassLoader()) #print "BindAuthenticator class loader %s" % self._authenticator.getClass().getClassLoader() token = org.springframework.security.providers.UsernamePasswordAuthenticationToken(authentication.username, authentication.password) return self._authenticator.authenticate(token) class PasswordComparisonAuthenticator(object): def __init__(self, context_source=None, user_dn_patterns="uid={0},ou=people", password_attr_name="userPassword"): self.context_source = context_source self.user_dn_patterns = user_dn_patterns self.password_attr_name = password_attr_name self.encoder = LdapShaPasswordEncoder() self.logger = logging.getLogger("springpython.security.providers.Ldap.PasswordComparisonAuthenticator") def authenticate(self, authentication): if jython: raise Exception("This code doesn't work inside Jython.") class DefaultLdapAuthoritiesPopulator(object): def __init__(self, context_source=None, group_search_base="ou=groups", group_search_filter="(member={0})", group_role_attr="cn", role_prefix="ROLE_", convert_to_upper=True): self.logger = logging.getLogger("springpython.security.providers.Ldap.DefaultLdapAuthoritiesPopulator") self.context_source = context_source self.group_search_base = group_search_base self.group_search_filter = group_search_filter self.group_role_attr = group_role_attr self.role_prefix = role_prefix self.convert_to_upper = convert_to_upper self._populator = org.springframework.security.ldap.populator.DefaultLdapAuthoritiesPopulator(self.context_source._context, self.group_search_base) #java.lang.Thread.currentThread().setContextClassLoader(self._populator.getClass().getClassLoader()) self._populator.setGroupSearchFilter(self.group_search_filter) self._populator.setGroupRoleAttribute(self.group_role_attr) self._populator.setRolePrefix(self.role_prefix) self._populator.setConvertToUpperCase(self.convert_to_upper) print "LdapAuthoritiesPopulator class loader %s" % self._populator.getClass().getClassLoader() def get_granted_auths(self, user_details, username): results = self._populator.getGrantedAuthorities(user_details, username) print results return results class LdapAuthenticationProvider(AuthenticationProvider): def __init__(self, ldap_authenticator=None, ldap_authorities_populator=None): AuthenticationProvider.__init__(self) self.ldap_authenticator = ldap_authenticator self.ldap_authorities_populator = ldap_authorities_populator self.logger = logging.getLogger("springpython.security.providers.Ldap.LdapAuthenticationProvider") def authenticate(self, authentication): user_details = self.ldap_authenticator.authenticate(authentication) print "Context class loader %s" % user_details.getClass().getClassLoader() from copy import deepcopy results = deepcopy(authentication) results.granted_auths = self.ldap_authorities_populator.get_granted_auths(user_details, authentication.username) results.setAuthenticated(True) l.unbind() return results springpython-1.2.0+ds/springpython/security/providers/dao.py0000644000000000000000000001735611412453030023130 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging from springpython.database import DataAccessException from springpython.security import AuthenticationException from springpython.security import AuthenticationServiceException from springpython.security import BadCredentialsException from springpython.security import DisabledException from springpython.security import UsernameNotFoundException from springpython.security.providers import AuthenticationProvider from springpython.security.providers import UsernamePasswordAuthenticationToken from springpython.security.providers.encoding import PlaintextPasswordEncoder class UserCache(object): def get_user(self, username): raise NotImplementedError() def put_user(self, user): raise NotImplementedError() def remove_user(self, username): raise NotImplementedError() class NullUserCache(UserCache): def get_user(self, username): return None def put_user(self, user): pass def remove_user(self, username): pass class AbstractUserDetailsAuthenticationProvider(AuthenticationProvider): def __init__(self): super(AbstractUserDetailsAuthenticationProvider, self).__init__() self.user_cache = NullUserCache() self.hide_user_not_found_exceptions = True self.force_principal_as_str = True self.logger = logging.getLogger("springpython.security.providers.AbstractUserDetailsAuthenticationProvider") def authenticate(self, authentication): # Determine username username = authentication.username cache_was_used = True user = self.user_cache.get_user(username) if user is None: cache_was_used = False try: user = self.retrieve_user(username, authentication) except UsernameNotFoundException, notFound: if self.hide_user_not_found_exceptions: raise BadCredentialsException("UsernameNotFound: Bad credentials") else: raise notFound if user is None: raise Exception("retrieve_user returned null - a violation of the interface contract") if not user.accountNonLocked: raise LockedException("User account is locked") if not user.enabled: raise DisabledException("User is disabled") if not user.accountNonExpired: raise AccountExpiredException("User account has expired") # This check must come here, as we don't want to tell users # about account status unless they presented the correct credentials try: self.additional_auth_checks(user, authentication) except AuthenticationException, exception: if cache_was_used: # There was a problem, so try again after checking we're using latest data (ie not from the cache) cache_was_used = False user = self.retrieve_user(username, authentication) self.additional_auth_checks(user, authentication) else: raise exception if not user.credentialsNonExpired: raise CredentialsExpiredException("User credentials have expired") if not cache_was_used: self.user_cache.put_user(user) principal_to_return = user if self.force_principal_as_str: principal_to_return = user.username return self.create_success_auth(principal_to_return, authentication, user) def additional_auth_checks(self, user_details, authentication): raise NotImplementedError() def retrieve_user(self, username, authentication): raise NotImplementedError() def create_success_auth(self, principal, authentication, user): # Ensure we return the original credentials the user supplied, # so subsequent attempts are successful even with encoded passwords. # Also ensure we return the original getDetails(), so that future # authentication events after cache expiry contain the details result = UsernamePasswordAuthenticationToken(principal, authentication.getCredentials(), user.authorities) #result.details = authentication.details return result class DaoAuthenticationProvider(AbstractUserDetailsAuthenticationProvider): def __init__(self, user_details_service = None, password_encoder = PlaintextPasswordEncoder()): super(DaoAuthenticationProvider, self).__init__() self.password_encoder = password_encoder self.salt_source = None self.user_details_service = user_details_service self.include_details_obj = True self.logger = logging.getLogger("springpython.security.providers.DaoAuthenticationProvider") def retrieve_user(self, username, authentication): loaded_user = None try: loaded_user = self.user_details_service.load_user(username) except DataAccessException, repositoryProblem: raise AuthenticationServiceException(repositoryProblem) if loaded_user is None: raise AuthenticationServiceException("UserDetailsService returned null, which is an interface contract violation") return loaded_user def additional_auth_checks(self, user_details, authentication): salt = None if self.salt_source is not None: salt = self.salt_source.get_salt(user_details) if not self.password_encoder.isPasswordValid(user_details.password, authentication.getCredentials(), salt): raise BadCredentialsException("additional_auth_checks: Bad credentials") class SaltSource(object): """Provides alternative sources of the salt to use for encoding passwords.""" def get_salt(self, user): """Returns the salt to use for the indicated user.""" raise NotImplementedError() class SystemWideSaltSource(SaltSource): """ Uses a static system-wide String as the salt. Does not supply a different salt for each User. This means users sharing the same password will still have the same digested password. Of benefit is the digested passwords will at least be more protected than if stored without any salt. """ def __init__(self, system_wide_salt = ""): super(SystemWideSaltSource, self).__init__() self.system_wide_salt = system_wide_salt def get_salt(self, user): return self.system_wide_salt class ReflectionSaltSource(SaltSource): """ Obtains a salt from a specified property of the User object. This allows you to subclass User and provide an additional bean getter for a salt. You should use a synthetic value that does not change, such as a database primary key. Do not use username if it is likely to change. """ def __init__(self, user_prop_to_use = ""): super(ReflectionSaltSource, self).__init__() self.user_prop_to_use = user_prop_to_use def get_salt(self, user): try: reflectionMethod = getattr(user, self.user_prop_to_use) return reflectionMethod() except Exception, e: raise AuthenticationServiceException(e); springpython-1.2.0+ds/springpython/security/providers/Ldap.py0000644000000000000000000000137711464332043023250 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import sys if "java" in sys.platform.lower(): from _Ldap_jython import * else: from _Ldap_cpython import * springpython-1.2.0+ds/springpython/security/cherrypy3.py0000644000000000000000000000471011464332043022275 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import cherrypy import logging from springpython.security import AuthenticationException from springpython.security.context import SecurityContextHolder, SecurityContext from springpython.security.intercept import AbstractSecurityInterceptor from springpython.security.providers import UsernamePasswordAuthenticationToken from springpython.security.web import FilterChainProxy, SessionStrategy,RegExpBasedFilterInvocationDefinitionMap, FilterInvocation class CP3FilterChainProxy(FilterChainProxy): def __init__(self, filterInvocationDefinitionSource=None): FilterChainProxy.__init__(self, filterInvocationDefinitionSource) self.logger = logging.getLogger("springpython.security.cherrypy3.Another") cherrypy.tools.securityFilterChain = cherrypy._cptools.HandlerTool(self) def __call__(self, environ=None, start_response=None): if cherrypy.request.handler is None: return False def cherrypy_handler(*args, **kwargs): return cherrypy.request.handler(*args, **kwargs) def func(args): return args[0]() self.application = (func, (cherrypy_handler,)) cherrypy.response.body = FilterChainProxy.__call__(self, cherrypy.request.wsgi_environ, None) return True class CP3SessionStrategy(SessionStrategy): def __init__(self): SessionStrategy.__init__(self) self.logger = logging.getLogger("springpython.security.cherrypy3.CP3SessionStrategy") def getHttpSession(self, environ): return cherrypy.session.get("session_id") def setHttpSession(self, key, value): if "session_id" not in cherrypy.session: cherrypy.session["session_id"] = {} cherrypy.session["session_id"][key] = value class CP3RedirectStrategy(object): def redirect(self, url): raise cherrypy.HTTPRedirect(url) springpython-1.2.0+ds/springpython/security/vote.py0000644000000000000000000003130111412453030021307 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging from springpython.security import AccessDeniedException logger = logging.getLogger("springpython.security.vote") class AccessDecisionVoter: """ Indicates a class is responsible for voting on authorization decisions. The coordination of voting (ie polling AccessDecisionVoters, tallying their responses, and making the final authorization decision) is performed by an AccessDecisionManager. """ ACCESS_ABSTAIN = 0 ACCESS_DENIED = 1 ACCESS_GRANTED = 2 access_dict = { ACCESS_ABSTAIN: "ACCESS_ABSTAIN", ACCESS_DENIED: "ACCESS_DENIED", ACCESS_GRANTED: "ACCESS_GRANTED" } def supports(self, attr): """ Indicates whether this AccessDecisionVoter is able to vote on the passed in object. """ raise NotImplementedError() def vote(self, authentication, object, config): """Indicates whether or not access is granted.""" raise NotImplementedError() class RoleVoter(AccessDecisionVoter): """ Votes if any ConfigAttribute.getAttribute() starts with a prefix indicating that it is a role. The default prefix string is ROLE_, but this may be overriden to any value. It may also be set to empty, which means that essentially any attribute will be voted on. As described further below, the effect of an empty prefix may not be quite desireable. Abstains from voting if no configuration attribute commences with the role prefix. Votes to grant access if there is an exact matching GrantedAuthority to a ConfigAttribute starting with the role prefix. Votes to deny access if there is no exact matching GrantedAuthority to a ConfigAttribute starting with the role prefix. An empty role prefix means that the voter will vote for every ConfigAttribute. When there are different categories of ConfigAttributes used, this will not be optimal since the voter will be voting for attributes which do not represent roles. However, this option may be of some use when using preexisting role names without a prefix, and no ability exists to prefix them with a role prefix on reading them in, such as provided for example in JdbcDaoImpl. All comparisons and prefixes are case sensitive. """ def __init__(self, role_prefix = "ROLE_"): self.role_prefix = role_prefix self.logger = logging.getLogger("springpython.security.vote.RoleVoter") def supports(self, attr): """This voter will support a list, or a string starting with the same characters as the set prefix. """ if isinstance(attr, list) or (attr is not None and attr.startswith(self.role_prefix)): return True else: return False def vote(self, authentication, invocation, config): """Grant access if any of the granted authorities matches any of the required roles. """ results = self.ACCESS_ABSTAIN for attribute in config: if self.supports(attribute): self.logger.debug("This %s role voter will vote whether user has %s" % (self.role_prefix, attribute)) results = self.ACCESS_DENIED for authority in authentication.granted_auths: if attribute == authority: self.logger.debug("This user has %s in %s. Vote for GRANTED!" % (attribute, authentication.granted_auths)) return self.ACCESS_GRANTED if results == self.ACCESS_ABSTAIN: self.logger.debug("This %s voter is abstaining from voting" % self.role_prefix) elif results == self.ACCESS_DENIED: self.logger.debug("This %s voter did NOT find the required credentials in %s. Vote for DENIED!" % (self.role_prefix, authentication.granted_auths)) return results def __str__(self): return "<'%s' role voter>" % self.role_prefix class AbstractAclVoter(AccessDecisionVoter): """ May/may not need this class """ pass class LabelBasedAclVoter(AbstractAclVoter): """ * This Acl voter will evaluate methods based on labels applied to incoming arguments. It will * only check methods that have been properly tagged in the MethodSecurityInterceptor with the * value stored in attr_indicating_labeled_op. If a method has been tagged, then * it examines each argument, and if the argument implements {@link LabeledData}, then it will * asses if the user's list of granted authorities matches.

*

* By default, if none of the arguments are labeled, then the access will be granted. This can * be overridden by setting allow_access_if_no_attr_labeled to false in the Spring * context file.

*

* In many situations, different values are linked together to define a common label, it is * necessary to define a map in the application context that links user-assigned label access * to domain object labels. This is done by setting up the label_dict in the application * context.

* * @author Greg Turnquist * @see org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor """ def __init__(self, label_dict = None, allow_access_if_no_attr_labeled = False, attr_indicating_labeled_op = ""): if label_dict is None: self.label_dict = {} else: self.label_dict = label_dict self.allow_access_if_no_attr_labeled = allow_access_if_no_attr_labeled self.attr_indicating_labeled_op = attr_indicating_labeled_op self.logger = logging.getLogger("springpython.security.vote.LabelBasedAclVoter") def supports(self, attr): if isinstance(attr, list) or (attr == self.attr_indicating_labeled_op): return True else: return False def vote(self, authentication, invocation, config): result = self.ACCESS_ABSTAIN; for attribute in config: if self.supports(attribute): result = self.ACCESS_DENIED; userLabels = [] for label in authentication.granted_auths: if label in self.label_dict: userLabels.extend(self.label_dict[label]) labeledArguments = [arg.getLabel() for arg in invocation.args if hasattr(arg, "getLabel")] matches = [arg for arg in labeledArguments if arg in userLabels] misses = [arg for arg in labeledArguments if arg not in userLabels] self.logger.debug("Arguments: %s Matches: %s Misses: %s User labels: %s" % (labeledArguments, matches, misses, userLabels)) if len(matches) > 0 and misses == []: self.logger.debug("Access is granted!") return self.ACCESS_GRANTED; elif labeledArguments == []: if self.allow_access_if_no_attr_labeled: self.logger.debug("Access is granted, since there are no attributes set!") return self.ACCESS_GRANTED; else: self.logger.debug("Access is denied, since there are no attributes set!") return self.ACCESS_DENIED; self.logger.debug("No matches, so returning %s" % self.access_dict[result]) return result; def __str__(self): return "<'%s' label-based ACL voter>" % self.label_dict class AccessDecisionManager: """" Makes a final access control (authorization) decision. """ def __init__(self, access_decision_voters = [], allow_if_all_abstain = False): self.access_decision_voters = access_decision_voters self.allow_if_all_abstain = allow_if_all_abstain def decide(self, authentication, object, config): """Resolves an access control decision for the passed parameters.""" raise NotImplementedError() def supports(self, attr): """ Indicates whether the AccessDecisionManager implementation is able to provide access control decisions for the indicated secured object type. """ raise NotImplementedError() class AffirmativeBased(AccessDecisionManager): """ Simple concrete implementation of AccessDecisionManager that grants access if any AccessDecisionVoter returns an affirmative response. """ def __init__(self, access_decision_voters = [], allow_if_all_abstain = False): AccessDecisionManager.__init__(self, access_decision_voters, allow_if_all_abstain) self.logger = logging.getLogger("springpython.security.vote.AffirmativeBased") def decide(self, authentication, invocation, config): """ This concrete implementation simply polls all configured AccessDecisionVoters and grants access if any AccessDecisionVoter voted affirmatively. """ for voter in self.access_decision_voters: if voter.supports(config) and \ voter.vote(authentication, invocation, config) == AccessDecisionVoter.ACCESS_GRANTED: self.logger.debug("Received affirmative vote from %s, granting access." % voter) return raise AccessDeniedException("Access is denied.") class ConsensusBased(AccessDecisionManager): """ Simple concrete implementation of AccessDecisionManager that uses a consensus-based approach. """ def __init__(self, access_decision_voters = [], allow_if_all_abstain = False): AccessDecisionManager.__init__(self, access_decision_voters, allow_if_all_abstain) self.allow_if_tied = True self.logger = logging.getLogger("springpython.security.vote.ConsensusBased") def decide(self, authentication, invocation, config): """ This concrete implementation simply polls all configured AccessDecisionVoters and upon completion determines the consensus of granted vs denied responses. """ granted_votes = [] denied_votes = [] for voter in self.access_decision_voters: if voter.supports(config): vote = voter.vote(authentication, invocation, config) if vote == AccessDecisionVoter.ACCESS_GRANTED: granted_votes.append(voter) if vote == AccessDecisionVoter.ACCESS_DENIED: denied_votes.append(voter) if len(granted_votes) > len(denied_votes): self.logger.debug("%s granted votes, %s denial votes, granting access." % (len(granted_votes), len(denied_votes))) return elif len(granted_votes) < len(denied_votes): self.logger.debug("%s granted votes, %s denial votes, denying access." % (len(granted_votes), len(denied_votes))) raise AccessDeniedException("Access is denied.") elif self.allow_if_tied: self.logger.debug("%s granted votes, %s denial votes, granting access." % (len(granted_votes), len(denied_votes))) return else: self.logger.debug("%s granted votes, %s denial votes, denying access." % (len(granted_votes), len(denied_votes))) raise AccessDeniedException("Access is denied.") class UnanimousBased(AccessDecisionManager): """ Simple concrete implementation of AccessDecisionManager that requires all voters to abstain or grant access. """ def __init__(self, access_decision_voters = [], allow_if_all_abstain = False): AccessDecisionManager.__init__(self, access_decision_voters, allow_if_all_abstain) self.logger = logging.getLogger("springpython.security.vote.UnanimousBased") def decide(self, authentication, invocation, config): """ This concrete implementation polls all configured AccessDecisionVoters for each ConfigAttribute and grants access if only grant votes were received. """ for voter in self.access_decision_voters: if voter.supports(config) and \ voter.vote(authentication, invocation, config) == AccessDecisionVoter.ACCESS_DENIED: self.logger.debug("Received denial vote from %s, denying access" % voter) raise AccessDeniedException("Access is denied.") springpython-1.2.0+ds/springpython/security/context/0000755000000000000000000000000011544154725021465 5ustar rootrootspringpython-1.2.0+ds/springpython/security/context/__init__.py0000644000000000000000000000617411464332043023576 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import threading from springpython.security.providers import Authentication # See GlobalSecurityContextHolderStrategy _globalContext = None class SecurityContext(object): def __init__(self, authentication = Authentication()): self.authentication = authentication def __str__(self): if self.authentication == None: return "Authentication is empty" else: return self.authentication.__str__() class SecurityContextHolderStrategy(object): """Strategy interface to allow defining ways to store security context.""" def clearContext(self): raise NotImplementedError() def getContext(self): raise NotImplementedError() def setContext(self, context): raise NotImplementedError() class ThreadLocalSecurityContextHolderStrategy(SecurityContextHolderStrategy): """Strategy to store the security context in a local thread. This allows multi-threaded apps to manage multiple contexts at the same time.""" def __init__(self): self.logger = logging.getLogger("springpython.security.context.ThreadLocalSecurityContextHolderStrategy") self.contextHolder = threading.local() self.logger.debug("Creating a new threadlocal security context.") self.clearContext() def clearContext(self): self.contextHolder.context = None def getContext(self): if not self.contextHolder.context: self.setContext(SecurityContext()) return self.contextHolder.context def setContext(self, context): if not context: raise SecurityException("Only non-None security context's are allowed") self.contextHolder.context = context class GlobalSecurityContextHolderStrategy(SecurityContextHolderStrategy): """Store one context in the entire python virtual machine. This typically suits a client-side application.""" def __init__(self): self.logger = logging.getLogger("springpython.security.context.GlobalSecurityContextHolderStrategy") self.clearContext() def clearContext(self): global _globalContext _globalContext = None def getContext(self): global _globalContext if not _globalContext: self.setContext(SecurityContext()) return _globalContext def setContext(self, context): global _globalContext if not context: raise SecurityException("Only non-None security context's are allowed") _globalContext = context springpython-1.2.0+ds/springpython/security/context/SecurityContextHolder.py0000644000000000000000000000423211464332043026342 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ from springpython.security import SecurityException from springpython.security.context import ThreadLocalSecurityContextHolderStrategy from springpython.security.context import GlobalSecurityContextHolderStrategy """ This represents a static object that holds the context of the current session. """ # Currently supported strategies MODE_THREADLOCAL = "THREADLOCAL" MODE_GLOBAL = "GLOBAL" # Local settings used to track strategy configuration settings = {"strategyName" : MODE_GLOBAL, "strategy" : None, "initialized" : False } def initialize(): global settings if settings["strategyName"] == MODE_THREADLOCAL: settings["strategy"] = ThreadLocalSecurityContextHolderStrategy() elif settings["strategyName"] == MODE_GLOBAL: settings["strategy"] = GlobalSecurityContextHolderStrategy() else: raise SecurityException("We don't support strategy type %s" % settings["strategyName"]) settings["initialized"] = True def setStrategy(newStrategyName): global settings settings["strategyName"] = newStrategyName initialize() def clearContext(): if not settings["initialized"]: initialize() settings["strategy"].clearContext() def getContext(): """Retrieve the context, based on the strategy.""" if not settings["initialized"]: initialize() return settings["strategy"].getContext() def setContext(context): """Store the context, based on the strategy.""" if not settings["initialized"]: initialize() settings["strategy"].setContext(context) springpython-1.2.0+ds/springpython/factory/0000755000000000000000000000000011544154725017601 5ustar rootrootspringpython-1.2.0+ds/springpython/factory/__init__.py0000644000000000000000000000500011412453030021666 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import sys class ObjectFactory(object): def create_object(self, constr, named_constr): raise NotImplementedError() class ReflectiveObjectFactory(ObjectFactory): def __init__(self, module_and_class): self.logger = logging.getLogger("springpython.factory.ReflectiveObjectFactory") self.module_and_class = module_and_class def create_object(self, constr, named_constr): self.logger.debug("Creating an instance of %s" % self.module_and_class) parts = self.module_and_class.split(".") module_name = ".".join(parts[:-1]) class_name = parts[-1] if module_name == "": return __import__(class_name)(*constr, **named_constr) else: __import__(module_name) cls = getattr(sys.modules[module_name], class_name) return cls(*constr, **named_constr) def __str__(self): return "ReflectiveObjectFactory(%s)" % self.module_and_class class PythonObjectFactory(ObjectFactory): def __init__(self, method, wrapper): self.logger = logging.getLogger("springpython.factory.PythonObjectFactory") self.method = method self.wrapper = wrapper def create_object(self, constr, named_constr): self.logger.debug("Creating an instance of %s" % self.method.func_name) # Setting wrapper's top_func can NOT be done earlier than this method call, # because it is tied to a wrapper decorator, which may not have yet been # generated. self.wrapper.func_globals["top_func"] = self.method.func_name # Because @object-based objects use direct code to specify arguments, and NOT # external configuration data, this factory doesn't care about the incoming arguments. return self.method() def __str__(self): return "PythonObjectFactory(%s)" % self.method springpython-1.2.0+ds/springpython/remoting/0000755000000000000000000000000011544154725017756 5ustar rootrootspringpython-1.2.0+ds/springpython/remoting/hessian/0000755000000000000000000000000011544154725021410 5ustar rootrootspringpython-1.2.0+ds/springpython/remoting/hessian/__init__.py0000644000000000000000000000327711412453030023513 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ from springpython.remoting.hessian.hessianlib import Hessian class HessianProxyFactory(object): """ This is wrapper around a Hessian client proxy. The idea is to inject this object with a Hessian service_url, which in turn generates a Hessian client proxy. After that, any method calls or attribute accessses will be forwarded to the Hessian client proxy. """ def __init__(self): self.__dict__["client_proxy"] = None def __setattr__(self, name, value): if name == "service_url": self.__dict__["service_url"] = value else: setattr(self.client_proxy, name, value) def __getattr__(self, name): if name == "service_url": return self.service_url elif name in ["post_process_before_initialization", "post_process_after_initialization"]: raise AttributeError, name else: if self.client_proxy is None: self.__dict__["client_proxy"] = Hessian(self.service_url) return getattr(self.client_proxy, name) springpython-1.2.0+ds/springpython/remoting/hessian/hessianlib.py0000644000000000000000000002705311412453030024073 0ustar rootroot# # A Hessian client interface for Python. The date and long types require # Python 2.2 or later. # # The Hessian proxy is used as follows: # # proxy = Hessian("http://hessian.caucho.com/test/basic") # # print proxy.hello() # # -------------------------------------------------------------------- # # The Apache Software License, Version 1.1 # # Copyright (c) 2001-2002 Caucho Technology, Inc. All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # 3. The end-user documentation included with the redistribution, if # any, must include the following acknowlegement: # "This product includes software developed by the # Caucho Technology (http://www.caucho.com/)." # Alternately, this acknowlegement may appear in the software itself, # if and wherever such third-party acknowlegements normally appear. # # 4. The names "Hessian", "Resin", and "Caucho" must not be used to # endorse or promote products derived from this software without prior # written permission. For written permission, please contact # info@caucho.com. # # 5. Products derived from this software may not be called "Resin" # nor may "Resin" appear in their names without prior written # permission of Caucho Technology. # # THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED # WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE # DISCLAIMED. IN NO EVENT SHALL CAUCHO TECHNOLOGY OR ITS CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, # OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT # OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, # WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE # OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN # IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. # -------------------------------------------------------------------- # # Credits: hessianlib.py was inspired and partially based on # xmlrpclib.py created by Fredrik Lundh at www.pythonware.org # import string, time import urllib from types import * from struct import unpack from struct import pack __version__ = "0.1" # -------------------------------------------------------------------- # Exceptions class Error: # base class for client errors pass class ProtocolError(Error): # Represents an HTTP protocol error def __init__(self, url, code, message, headers): self.url = url self.code = code self.message = message self.headers = headers def __repr__(self): return ( "" % (self.url, self.code, self.message) ) class Fault(Error): # Represents a fault from Hessian def __init__(self, code, message, **detail): self.code = code self.message = message def __repr__(self): return "" % (self.code, self.message) # -------------------------------------------------------------------- # Wrappers for Hessian data types non-standard in Python # # # Boolean -- use the True or False constants # class Boolean: def __init__(self, value = 0): self.value = (value != 0) def _hessian_write(self, out): if self.value: out.write('T') else: out.write('F') def __repr__(self): if self.value: return "" % id(self) else: return "" % id(self) def __int__(self): return self.value def __nonzero__(self): return self.value True, False = Boolean(1), Boolean(0) # # Date - wraps a time value in seconds # class Date: def __init__(self, value = 0): self.value = value def __repr__(self): return ("" % (time.asctime(time.localtime(self.value)), id(self))) def _hessian_write(self, out): out.write("d") out.write(pack(">q", self.value * 1000.0)) # # Binary - binary data # class Binary: def __init__(self, data=None): self.data = data def _hessian_write(self, out): out.write('B') out.write(pack('>H', len(self.data))) out.write(self.data) # -------------------------------------------------------------------- # Marshalling and unmarshalling code # # HessianWriter - writes Hessian data from Python objects # class HessianWriter: dispatch = {} def write_call(self, method, params): self.refs = {} self.ref = 0 self.__out = [] self.write = write = self.__out.append write("c\x01\x00m"); write(pack(">H", len(method))); write(method); for v in params: self.write_object(v) write("z"); result = string.join(self.__out, "") del self.__out, self.write, self.refs return result def write_object(self, value): try: f = self.dispatch[type(value)] except KeyError: raise TypeError, "cannot write %s objects" % type(value) else: f(self, value) def write_int(self, value): self.write('I') self.write(pack(">l", value)) dispatch[IntType] = write_int def write_long(self, value): self.write('L') self.write(pack(">q", value)) dispatch[LongType] = write_long def write_double(self, value): self.write('D') self.write(pack(">d", value)) dispatch[FloatType] = write_double def write_string(self, value): self.write('S') self.write(pack('>H', len(value))) self.write(value) dispatch[StringType] = write_string def write_reference(self, value): # check for and write circular references # returns 1 if the object should be written, i.e. not a reference i = id(value) if self.refs.has_key(i): self.write('R') self.write(pack(">L", self.refs[i])) return 0 else: self.refs[i] = self.ref self.ref = self.ref + 1 return 1 def write_list(self, value): if self.write_reference(value): self.write("Vt\x00\x00I"); self.write(pack('>l', len(value))) for v in value: self.__write(v) self.write('z') dispatch[TupleType] = write_list dispatch[ListType] = write_list def write_map(self, value): if self.write_reference(value): self.write("Mt\x00\x00") for k, v in value.items(): self.__write(k) self.__write(v) self.write("z") dispatch[DictType] = write_map def write_instance(self, value): # check for special wrappers if hasattr(value, "_hessian_write"): value._hessian_write(self) else: fields = value.__dict__ if self.write_reference(fields): self.write("Mt\x00\x00") for k, v in fields.items(): self.__write(k) self.__write(v) self.write("z") dispatch[InstanceType] = write_instance # # Parses the results from the server # class HessianParser: def __init__(self, f): self._f = f self._peek = -1 # self.read = f.read self._refs = [] def read(self, len): if self._peek >= 0: value = self._peek self._peek = -1 return value else: return self._f.read(len) def parse_reply(self): # parse header 'c' x01 x00 'v' ... 'z' read = self.read if read(1) != 'r': self.error() major = read(1) minor = read(1) value = self.parse_object() if read(1) == 'z': return value self.error() # actually a fault def parse_object(self): # parse an arbitrary object based on the type in the data return self.parse_object_code(self.read(1)) def parse_object_code(self, code): # parse an object when the code is known read = self.read if code == 'N': return None elif code == 'T': return True elif code == 'F': return False elif code == 'I': return unpack('>l', read(4))[0] elif code == 'L': return unpack('>q', read(8))[0] elif code == 'D': return unpack('>d', read(8))[0] elif code == 'd': ms = unpack('>q', read(8))[0] return Date(int(ms / 1000.0)) elif code == 'S' or code == 'X': return self.parse_string() elif code == 'B': return Binary(self.parse_string()) elif code == 'V': self.parse_type() # skip type self.parse_length() # skip length list = [] self._refs.append(list) ch = read(1) while ch != 'z': list.append(self.parse_object_code(ch)) ch = read(1) return list elif code == 'M': self.parse_type() # skip type map = {} self._refs.append(map) ch = read(1) while ch != 'z': key = self.parse_object_code(ch) value = self.parse_object() map[key] = value ch = read(1) return map elif code == 'R': return self._refs[unpack('>l', read(4))[0]] elif code == 'r': self.parse_type() # skip type url = self.parse_type() # reads the url return Hessian(url) else: raise "UnknownObjectCode %d" % code def parse_string(self): f = self._f len = unpack('>H', f.read(2))[0] return f.read(len) def parse_type(self): f = self._f code = self.read(1) if code != 't': self._peek = code return "" len = unpack('>H', f.read(2))[0] return f.read(len) def parse_length(self): f = self._f code = self.read(1); if code != 'l': self._peek = code return -1; len = unpack('>l', f.read(4)) return len def error(self): raise "FOO" # # Encapsulates the method to be called # class _Method: def __init__(self, invoker, method): self._invoker = invoker self._method = method def __call__(self, *args): return self._invoker(self._method, args) # -------------------------------------------------------------------- # Hessian is the main class. A Hessian proxy is created with the URL # and then called just as for a local method # # proxy = Hessian("http://www.caucho.com/hessian/test/basic") # print proxy.hello() # class Hessian: """Represents a remote object reachable by Hessian""" def __init__(self, url): # Creates a Hessian proxy object self._url = url # get the uri type, uri = urllib.splittype(url) if type != "http": raise IOError, "unsupported Hessian protocol" self._host, self._uri = urllib.splithost(uri) def __invoke(self, method, params): # call a method on the remote server request = HessianWriter().write_call(method, params) import httplib h = httplib.HTTP(self._host) h.putrequest("POST", self._uri) # required by HTTP/1.1 h.putheader("Host", self._host) h.putheader("User-Agent", "hessianlib.py/%s" % __version__) h.putheader("Content-Length", str(len(request))) h.endheaders() h.send(request) errcode, errmsg, headers = h.getreply() if errcode != 200: raise ProtocolError(self._url, errcode, errmsg, headers) return self.parse_response(h.getfile()) def parse_response(self, f): # read response from input file, and parse it parser = HessianParser(f) value = parser.parse_reply() f.close() return value def _hessian_write(self, out): # marshals the proxy itself out.write("rt\x00\x00S") out.write(pack(">H", len(self._url))) out.write(self._url) def __repr__(self): return "" % self._url __str__ = __repr__ def __getattr__(self, name): # encapsulate the method call return _Method(self.__invoke, name) # # Testing code. # if __name__ == "__main__": proxy = Hessian("http://hessian.caucho.com/test/test") try: print proxy.hello() except Error, v: print "ERROR", v springpython-1.2.0+ds/springpython/remoting/__init__.py0000644000000000000000000000121311412453030022045 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ springpython-1.2.0+ds/springpython/remoting/xmlrpc.py0000644000000000000000000001702111464332043021626 0ustar rootroot# -*- coding: utf-8 -*- # stdlib import httplib import logging import socket import ssl import sys import traceback from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler from xmlrpclib import ServerProxy, Error, Transport # Spring Python from springpython.remoting.http import CAValidatingHTTPS __all__ = ["VerificationException", "SSLServer", "SSLClient"] class VerificationException(Exception): """ Raised when the verification of a certificate's fields fails. """ # ############################################################################## # Server # ############################################################################## class RequestHandler(SimpleXMLRPCRequestHandler): rpc_paths = ("/", "/RPC2",) def setup(self): self.connection = self.request # for doPOST self.rfile = socket._fileobject(self.request, "rb", self.rbufsize) self.wfile = socket._fileobject(self.request, "wb", self.wbufsize) class SSLServer(object, SimpleXMLRPCServer): def __init__(self, host=None, port=None, keyfile=None, certfile=None, ca_certs=None, cert_reqs=ssl.CERT_NONE, ssl_version=ssl.PROTOCOL_TLSv1, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None, log_requests=True, **kwargs): SimpleXMLRPCServer.__init__(self, (host, port), requestHandler=RequestHandler) self.logger = logging.getLogger(self.__class__.__name__) self.keyfile = keyfile self.certfile = certfile self.ca_certs = ca_certs self.cert_reqs = cert_reqs self.ssl_version = ssl_version self.do_handshake_on_connect = do_handshake_on_connect self.suppress_ragged_eofs = suppress_ragged_eofs self.ciphers = ciphers # Looks awkward to use camelCase here but that's what SimpleXMLRPCRequestHandler # expects. self.logRequests = log_requests # 'verify_fields' is taken from kwargs to allow for adding more keywords # in future versions. self.verify_fields = kwargs.get("verify_fields") self.register_functions() def get_request(self): """ Overridden from SocketServer.TCPServer.get_request, wraps the socket in an SSL context. """ sock, from_addr = self.socket.accept() # 'ciphers' argument is new in 2.7 and we must support 2.6 so add it # to kwargs conditionally, depending on the Python version. kwargs = {"keyfile":self.keyfile, "certfile":self.certfile, "server_side":True, "cert_reqs":self.cert_reqs, "ssl_version":self.ssl_version, "ca_certs":self.ca_certs, "do_handshake_on_connect":self.do_handshake_on_connect, "suppress_ragged_eofs":self.suppress_ragged_eofs} if sys.version_info >= (2, 7): kwargs["ciphers"] = self.ciphers sock = ssl.wrap_socket(sock, **kwargs) if self.logger.isEnabledFor(logging.DEBUG): self.logger.debug("get_request cert='%s', from_addr='%s'" % (sock.getpeercert(), from_addr)) return sock, from_addr def verify_request(self, sock, from_addr): """ Overridden from SocketServer.TCPServer.verify_request, adds validation of the other side's certificate fields. """ try: if self.verify_fields: cert = sock.getpeercert() if not cert: msg = "Couldn't verify fields, peer didn't send the certificate, from_addr='%s'" % (from_addr,) raise VerificationException(msg) allow_peer, reason = self.verify_peer(cert, from_addr) if not allow_peer: self.logger.error(reason) sock.close() return False except Exception, e: # It was either an error on our side or the client didn't send the # certificate even though self.cert_reqs was CERT_OPTIONAL (it couldn't # have been CERT_REQUIRED because we wouldn't have got so far, the # session would've been terminated much earlier in ssl.wrap_socket call). # Regardless of the reason we cannot accept the client in that case. msg = "Verification error='%s', cert='%s', from_addr='%s'" % ( traceback.format_exc(e), sock.getpeercert(), from_addr) self.logger.error(msg) sock.close() return False return True def verify_peer(self, cert, from_addr): """ Verifies the other side's certificate. May be overridden in subclasses if the verification process needs to be customized. """ subject = cert.get("subject") if not subject: msg = "Peer certificate doesn't have the 'subject' field, cert='%s'" % cert raise VerificationException(msg) subject = dict(elem[0] for elem in subject) for verify_field in self.verify_fields: expected_value = self.verify_fields[verify_field] cert_value = subject.get(verify_field, None) if not cert_value: reason = "Peer didn't send the '%s' field, subject fields received '%s'" % ( verify_field, subject) return False, reason if expected_value != cert_value: reason = "Expected the subject field '%s' to have value '%s' instead of '%s', subject='%s'" % ( verify_field, expected_value, cert_value, subject) return False, reason return True, None def register_functions(self): raise NotImplementedError("Must be overridden by subclasses") # ############################################################################## # Client # ############################################################################## class SSLClientTransport(Transport): """ Handles an HTTPS transaction to an XML-RPC server. """ user_agent = "SSL XML-RPC Client (by http://springpython.webfactional.com)" def __init__(self, ca_certs=None, keyfile=None, certfile=None, cert_reqs=None, ssl_version=None, timeout=None, strict=None): self.ca_certs = ca_certs self.keyfile = keyfile self.certfile = certfile self.cert_reqs = cert_reqs self.ssl_version = ssl_version self.timeout = timeout self.strict = strict Transport.__init__(self) def make_connection(self, host): return CAValidatingHTTPS(host, strict=self.strict, ca_certs=self.ca_certs, keyfile=self.keyfile, certfile=self.certfile, cert_reqs=self.cert_reqs, ssl_version=self.ssl_version, timeout=self.timeout) class SSLClient(ServerProxy): def __init__(self, uri=None, ca_certs=None, keyfile=None, certfile=None, cert_reqs=ssl.CERT_REQUIRED, ssl_version=ssl.PROTOCOL_TLSv1, transport=None, encoding=None, verbose=0, allow_none=0, use_datetime=0, timeout=socket._GLOBAL_DEFAULT_TIMEOUT, strict=None): if not transport: _transport=SSLClientTransport(ca_certs, keyfile, certfile, cert_reqs, ssl_version, timeout, strict) else: _transport=transport(ca_certs, keyfile, certfile, cert_reqs, ssl_version, timeout, strict) ServerProxy.__init__(self, uri, _transport, encoding, verbose, allow_none, use_datetime) self.logger = logging.getLogger(self.__class__.__name__)springpython-1.2.0+ds/springpython/remoting/http.py0000644000000000000000000000363411464332043021305 0ustar rootroot# -*- coding: utf-8 -*- # stdlib import httplib import socket import ssl class CAValidatingHTTPSConnection(httplib.HTTPConnection): """ This class allows communication via SSL/TLS and takes Certificate Authorities into account. """ def __init__(self, host, port=None, ca_certs=None, keyfile=None, certfile=None, cert_reqs=None, strict=None, ssl_version=None, timeout=None): httplib.HTTPConnection.__init__(self, host, port, strict, timeout) self.ca_certs = ca_certs self.keyfile = keyfile self.certfile = certfile self.cert_reqs = cert_reqs self.ssl_version = ssl_version def connect(self): """ Connect to a host on a given (SSL/TLS) port. """ sock = socket.create_connection((self.host, self.port), self.timeout) if self._tunnel_host: self.sock = sock self._tunnel() self.sock = self.wrap_socket(sock) def wrap_socket(self, sock): """ Gets a socket object and wraps it into an SSL/TLS-aware one. May be overridden in subclasses if the wrapping process needs to be customized. """ return ssl.wrap_socket(sock, self.keyfile, self.certfile, ca_certs=self.ca_certs, cert_reqs=self.cert_reqs, ssl_version=self.ssl_version) class CAValidatingHTTPS(httplib.HTTP): """ A subclass of httplib.HTTP which is aware of Certificate Authorities used in SSL/TLS transactions. """ _connection_class = CAValidatingHTTPSConnection def __init__(self, host=None, port=None, strict=None, ca_certs=None, keyfile=None, certfile=None, cert_reqs=None, ssl_version=None, timeout=None): self._setup(self._connection_class(host, port, ca_certs, keyfile, certfile, cert_reqs, strict, ssl_version, timeout))springpython-1.2.0+ds/springpython/remoting/pyro/0000755000000000000000000000000011544154725020747 5ustar rootrootspringpython-1.2.0+ds/springpython/remoting/pyro/__init__.py0000644000000000000000000001673111464332043023060 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import Pyro.core from springpython.context import InitializingObject from springpython.remoting.pyro import PyroDaemonHolder class PyroServiceExporter(InitializingObject): """ This class will expose an object using Pyro. It requires that a daemon thread be up and running in order to receive requests and allow dispatching to the exposed object. """ def __init__(self, service = None, service_name = None, service_host = "localhost", service_port = 7766): self.logger = logging.getLogger("springpython.remoting.pyro.PyroServiceExporter") self.service = service self.service_name = service_name self.service_host = service_host self.service_port = service_port self._pyro_thread = None def __del__(self): """ When the service exporter goes out of scope and is garbage collected, the service must be deregistered. """ PyroDaemonHolder.deregister(self.service_name, self.service_host, self.service_port) def __setattr__(self, name, value): """ Only the explicitly listed attributes can be assigned values. Everything else is passed through to the actual service. """ if name in ["logger", "service", "service_name", "service_host", "service_port", "_pyro_thread"]: self.__dict__[name] = value else: object.__setattr__(self, name, value) def after_properties_set(self): if self.service is None: raise Exception("service must NOT be None") if self.service_name is None: raise Exception("service_name must NOT be None") if self.service_host is None: raise Exception("service_host must NOT be None") if self.service_port is None: raise Exception("service_port must NOT be None") self.logger.debug("Exporting %s as a Pyro service at %s:%s" % (self.service_name, self.service_host, self.service_port)) pyro_obj = Pyro.core.ObjBase() pyro_obj.delegateTo(self.service) PyroDaemonHolder.register(pyro_obj, self.service_name, self.service_host, self.service_port) class PyroProxyFactory(object): """ This is wrapper around a Pyro client proxy. The idea is to inject this object with a Pyro service_url, which in turn generates a Pyro client proxy. After that, any method calls or attribute accessses will be forwarded to the Pyro client proxy. """ def __init__(self): self.__dict__["client_proxy"] = None self.__dict__["service_url"] = None def __setattr__(self, name, value): if name == "service_url": self.__dict__["service_url"] = value else: setattr(self.client_proxy, name, value) def __getattr__(self, name): if name in ["service_url"]: return self.__dict__[name] elif name in ["post_process_before_initialization", "post_process_after_initialization"]: raise AttributeError, name else: if self.client_proxy is None: self.__dict__["client_proxy"] = Pyro.core.getProxyForURI(self.service_url) return getattr(self.client_proxy, name) class Pyro4ServiceExporter(InitializingObject): """ This class will expose an object using Pyro. It requires that a daemon thread be up and running in order to receive requests and allow dispatching to the exposed object. """ def __init__(self, service = None, service_name = None, service_host = "localhost", service_port = 7766): self.logger = logging.getLogger("springpython.remoting.pyro.Pyro4ServiceExporter") self.service = service self.service_name = service_name self.service_host = service_host self.service_port = service_port self._pyro_thread = None def __del__(self): """ When the service exporter goes out of scope and is garbage collected, the service must be deregistered. """ from springpython.remoting.pyro import Pyro4DaemonHolder Pyro4DaemonHolder.deregister(self.service_name, self.service_host, self.service_port) def __setattr__(self, name, value): """ Only the explicitly listed attributes can be assigned values. Everything else is passed through to the actual service. """ if name in ["logger", "service", "service_name", "service_host", "service_port", "_pyro_thread"]: self.__dict__[name] = value else: object.__setattr__(self, name, value) def after_properties_set(self): import Pyro4 from springpython.remoting.pyro import Pyro4DaemonHolder if self.service is None: raise Exception("service must NOT be None") if self.service_name is None: raise Exception("service_name must NOT be None") if self.service_host is None: raise Exception("service_host must NOT be None") if self.service_port is None: raise Exception("service_port must NOT be None") self.logger.debug("Exporting %s as a Pyro service at %s:%s" % (self.service_name, self.service_host, self.service_port)) wrapping_obj = PyroWrapperObj(self.service) Pyro4DaemonHolder.register(wrapping_obj, self.service_name, self.service_host, self.service_port) class PyroWrapperObj(object): def __init__(self, delegate): self.delegate = delegate def __getattr__(self, name): if name in ["__pyroInvoke", "__call__", "_pyroId", "_pyroDaemon", "delegate"]: return self.__dict__[name] else: return getattr(self.delegate, name) def __setattr__(self, name, value): if name in ["__pyroInvoke", "__call__", "_pyroId", "_pyroDaemon", "delegate"]: self.__dict__[name] = value else: setattr(self.delegate, name, value) class Pyro4ProxyFactory(object): """ This is wrapper around a Pyro client proxy. The idea is to inject this object with a Pyro service_url, which in turn generates a Pyro client proxy. After that, any method calls or attribute accessses will be forwarded to the Pyro client proxy. """ def __init__(self): self.__dict__["client_proxy"] = None self.__dict__["service_url"] = None def __setattr__(self, name, value): if name == "service_url": self.__dict__["service_url"] = value else: setattr(self.client_proxy, name, value) def __getattr__(self, name): import Pyro4 if name in ["service_url"]: return self.__dict__[name] elif name in ["post_process_before_initialization", "post_process_after_initialization"]: raise AttributeError, name else: if self.client_proxy is None: self.__dict__["client_proxy"] = Pyro4.Proxy(self.service_url) return getattr(self.client_proxy, name) springpython-1.2.0+ds/springpython/remoting/pyro/Pyro4DaemonHolder.py0000644000000000000000000001136711464332043024620 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import threading import time import Pyro4 from socket import getaddrinfo, gethostbyname pyro_threads = {} serviceList = {} logger = logging.getLogger("springpython.remoting.pyro.Pyro4DaemonHolder") def resolve(host, port): canonhost = gethostbyname(host) canonport = getaddrinfo(host, port)[0][4][1] return canonhost, canonport def register(pyro_obj, service_name, host, port): """ Register the Pyro4 object and its service name with the daemon. Also add the service to a dictionary of objects. This allows the PyroDaemonHolder to intelligently know when to start and stop the daemon thread. """ logger.debug("Registering %s at %s:%s with the Pyro4 server" % (service_name, host, port)) host, port = resolve(host, port) serviceList[(service_name, host, port)] = pyro_obj if (host, port) not in pyro_threads: logger.debug("Pyro4 thread needs to be started at %s:%s" % (host, port)) pyro_threads[(host, port)] = _Pyro4Thread(host, port) pyro_threads[(host, port)].start() if not hasattr(pyro_obj, "_pyroId"): uri = pyro_threads[(host, port)].pyro_daemon.register(pyro_obj, service_name) def deregister(service_name, host, port): """ Deregister the named service by removing it from the list of managed services and also disconnect from the daemon. """ logger.debug("Deregistering %s at %s:%s with the Pyro4 server" % (service_name, host, port)) host, port = resolve(host, port) if (host, port) in pyro_threads: pyro_threads[(host, port)].pyro_daemon.unregister(serviceList[(service_name, host, port)]) del(serviceList[(service_name, host, port)]) def get_address((service_name, host, port)): return (host, port) if len([True for x in serviceList.keys() if get_address(x) == (host, port)]) == 0: shutdown(host, port) def shutdown(daemon_host, daemon_port): """This provides a hook so an application can deliberately shutdown a daemon thread.""" logger.debug("Shutting down Pyro4 daemon at %s:%s" % (daemon_host, daemon_port)) daemon_host, daemon_port = resolve(daemon_host, daemon_port) try: pyro_threads[(daemon_host, daemon_port)].shutdown() time.sleep(1.0) del(pyro_threads[(daemon_host, daemon_port)]) except Exception, e: logger.debug("Failed to shutdown %s:%s => %s" % (daemon_host, daemon_port, e)) class _Pyro4Thread(threading.Thread): """ This is a thread that runs the Pyro4 daemon. It is instantiated automatically from within Pyro4ServiceExporter. """ def __init__(self, host, port): """ When this class is created, it also created a Pyro4 core daemon to manage. """ threading.Thread.__init__(self) self.host = host self.port = port self.logger = logging.getLogger("springpython.remoting.pyro.Pyro4DaemonHolder._Pyro4Thread") self.logger.debug("Creating Pyro4 daemon") self.pyro_daemon = Pyro4.Daemon(host=host, port=port) def run(self): """ When this thread starts up, it initializes the Pyro4 server and then puts the daemon into listen mode so it can process remote requests. """ self.logger.debug("Starting up Pyro4 server thread for %s:%s" % (self.host, self.port)) self.pyro_daemon.requestLoop() def shutdown(self): """ This is a hook in order to signal the thread that its time to shutdown the Pyro4 daemon. """ self.logger.debug("Signaling shutdown of Pyro4 server thread for %s:%s" % (self.host, self.port)) class ShutdownThread(threading.Thread): def __init__(self, pyro_daemon): threading.Thread.__init__(self) self.pyro_daemon = pyro_daemon self.logger = logging.getLogger("springpython.remoting.pyro.Pyro4DaemonHolder.ShutdownThread") def run(self): self.logger.debug("Sending shutdown signal...") self.pyro_daemon.shutdown() ShutdownThread(self.pyro_daemon).start() springpython-1.2.0+ds/springpython/remoting/pyro/PyroDaemonHolder.py0000644000000000000000000001037011464323256024533 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging import threading import Pyro.core, Pyro.naming from socket import getaddrinfo, gethostbyname pyro_threads = {} serviceList = {} logger = logging.getLogger("springpython.remoting.pyro.PyroDaemonHolder") def resolve(host, port): canonhost = gethostbyname(host) canonport = getaddrinfo(host, port)[0][4][1] return canonhost, canonport def register(pyro_obj, service_name, host, port): """ Register the pyro object and its service name with the daemon. Also add the service to a dictionary of objects. This allows the PyroDaemonHolder to intelligently know when to start and stop the daemon thread. """ logger.debug("Registering %s at %s:%s with the Pyro server" % (service_name, host, port)) host, port = resolve(host, port) serviceList[(service_name, host, port)] = pyro_obj if (host, port) not in pyro_threads: logger.debug("Pyro thread needs to be started at %s:%s" % (host, port)) pyro_threads[(host, port)] = _PyroThread(host, port) pyro_threads[(host, port)].start() pyro_threads[(host, port)].pyro_daemon.connect(pyro_obj, service_name) def deregister(service_name, host, port): """ Deregister the named service by removing it from the list of managed services and also disconnect from the daemon. """ logger.debug("Deregistering %s at %s:%s with the Pyro server" % (service_name, host, port)) host, port = resolve(host, port) pyro_threads[(host, port)].pyro_daemon.disconnect(serviceList[(service_name, host, port)]) del(serviceList[(service_name, host, port)]) def get_address((service_name, host, port)): return (host, port) if len([True for x in serviceList.keys() if get_address(x) == (host, port)]) == 0: logger.debug("Shutting down thread on %s:%s" % (host, port)) shutdown(host, port) def shutdown(daemon_host, daemon_port): """This provides a hook so an application can deliberately shutdown a daemon thread.""" logger.debug("Shutting down pyro daemon at %s:%s" % (daemon_host, daemon_port)) daemon_host, daemon_port = resolve(daemon_host, daemon_port) try: pyro_threads[(daemon_host, daemon_port)].shutdown() del(pyro_threads[(daemon_host, daemon_port)]) except: logger.debug("Failed to shutdown %s:%s" % (daemon_host, daemon_port)) class _PyroThread(threading.Thread): """ This is a thread that runs the Pyro daemon. It is instantiated automatically from within PyroServiceExporter. """ def __init__(self, host, port): """ When this class is created, it also created a Pyro core daemon to manage. """ threading.Thread.__init__(self) self.host = host self.port = port self.logger = logging.getLogger("springpython.remoting.pyro.PyroDaemonHolder._PyroThread") self.pyro_daemon = Pyro.core.Daemon(host=host, port=port) def run(self): """ When this thread starts up, it initializes the Pyro server and then puts the daemon into listen mode so it can process remote requests. """ self._running = True self.logger.debug("Starting up Pyro server thread for %s:%s" % (self.host, self.port)) Pyro.core.initServer() self.pyro_daemon.requestLoop(condition = lambda:self._running) def shutdown(self): """ This is a hook in order to signal the thread that its time to shutdown the Pyro daemon. """ self._running = False self.logger.debug("Signaling shutdown of Pyro server thread for %s:%s" % (self.host, self.port)) springpython-1.2.0+ds/springpython/container/0000755000000000000000000000000011544154725020114 5ustar rootrootspringpython-1.2.0+ds/springpython/container/__init__.py0000644000000000000000000001477111464332043022227 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import logging from springpython.context import scope class ObjectContainer(object): """ ObjectContainer is a container which uses multiple Config objects to read sources of object definitions. When a object is requested from this container, it may optionally pull the object from a scoped cache. If there is no stored copy of the object, it uses the scanned definition and its associated ObjectFactory to create an instance. It can then optionally store it in a scoped cache for future usage (e.g. singleton). Object definitions are stored in the container in a neutral format, decoupling the container entirely from the original source location. This means that XML, python code, and other formats may all contain definitions. By the time they reach this container, it doesn't matter what their original format was when a object instance is needed. NOTE: This explicitly means that one object in one source can refer to another object in another source OF ANY FORMAT as a property. """ def __init__(self, config = None): self.logger = logging.getLogger("springpython.container.ObjectContainer") if config is None: self.configs = [] elif isinstance(config, list): self.configs = config else: self.configs = [config] self.object_defs = {} for configuration in self.configs: self.logger.debug("=== Scanning configuration %s for object definitions ===" % configuration) for object_def in configuration.read_object_defs(): if object_def.id not in self.object_defs: self.logger.debug("%s object definition does not exist. Adding to list of definitions." % object_def.id) else: self.logger.debug("Overriding previous definition of %s" % object_def.id) self.object_defs[object_def.id] = object_def self.logger.debug("=== Done reading object definitions. ===") self.objects = {} def get_object(self, name, ignore_abstract=False): """ This function attempts to find the object in the singleton cache. If not found, delegates to _create_object in order to hunt for the definition, and request a object factory to generate one. """ try: object_def = self.object_defs[name] if object_def.abstract and not ignore_abstract: raise AbstractObjectException("Object [%s] is an abstract one." % name) return self.objects[name] except KeyError, e: self.logger.debug("Did NOT find object '%s' in the singleton storage." % name) try: object_def = self.object_defs[name] if object_def.abstract and not ignore_abstract: raise AbstractObjectException("Object [%s] is an abstract one." % name) comp = self._create_object(object_def) # Evaluate any scopes, and store appropriately. if self.object_defs[name].scope == scope.SINGLETON: self.objects[name] = comp self.logger.debug("Stored object '%s' in container's singleton storage" % name) elif self.object_defs[name].scope == scope.PROTOTYPE: pass else: raise InvalidObjectScope("Don't know how to handle scope %s" % self.object_defs[name].scope) return comp except KeyError, e: self.logger.error("Object '%s' has no definition!" % name) raise e def _get_constructors_pos(self, object_def): """ This function iterates over the positional constructors, and assembles their values into a list. In this situation, the order as read from the XML should be the order expected by the class definition. """ return tuple([constr.get_value(self) for constr in object_def.pos_constr if hasattr(constr, "get_value")]) def _get_constructors_kw(self, kwargs): """ This function iterates over the named constructors, and assembles their values into a list. In this situation, each argument is associated with a name, and due to unicode format provided by the XML parser, requires conversion into a new dictionary. """ return dict([(key, kwargs[key].get_value(self)) for key in kwargs if hasattr(kwargs[key], "get_value")]) def _create_object(self, object_def): """ If the object isn't stored in any scoped cache, and must instead be created, this method takes all the steps to read the object's definition, res it up, and store it in the appropriate scoped cache. """ self.logger.debug("Creating an instance of %s" % object_def) [constr.prefetch(self) for constr in object_def.pos_constr if hasattr(constr, "prefetch")] [constr.prefetch(self) for constr in object_def.named_constr.values() if hasattr(constr, "prefetch")] [prop.prefetch(self) for prop in object_def.props if hasattr(prop, "prefetch")] # Res up an instance of the object, with ONLY constructor-based properties set. obj = object_def.factory.create_object(self._get_constructors_pos(object_def), self._get_constructors_kw(object_def.named_constr)) # Fill in the other property values. [prop.set_value(obj, self) for prop in object_def.props if hasattr(prop, "set_value")] return obj class AbstractObjectException(Exception): """ Raised when the user's code tries to get an abstract object from the container. """ class InvalidObjectScope(Exception): pass springpython-1.2.0+ds/springpython/config/0000755000000000000000000000000011544154725017377 5ustar rootrootspringpython-1.2.0+ds/springpython/config/__init__.py0000644000000000000000000000136011464332043021500 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ from _config_base import * from _xml_config import * from _yaml_config import * from _python_config import * springpython-1.2.0+ds/springpython/config/_config_base.py0000644000000000000000000002635711464332043022354 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import re import types import inspect import logging from springpython.context import scope from decorator import decorator, partial from springpython.context import ApplicationContextAware from springpython.factory import PythonObjectFactory from springpython.factory import ReflectiveObjectFactory from springpython.container import InvalidObjectScope def get_string(value): """This function is used to parse text that could either be ASCII or unicode.""" try: return str(value) except UnicodeEncodeError: return unicode(value) class ObjectDef(object): """ ObjectDef is a format-neutral way of storing object definition information. It includes a handle for the actual ObjectFactory that should be used to utilize this information when creating an instance of a object. """ def __init__(self, id, props=None, factory=None, scope=scope.SINGLETON, lazy_init=False, abstract=False, parent=None): super(ObjectDef, self).__init__() self.id = id self.factory = factory if props is None: self.props = [] else: self.props = props self.scope = scope self.lazy_init = lazy_init self.abstract = abstract self.parent = parent self.pos_constr = [] self.named_constr = {} def __str__(self): return "id=%s props=%s scope=%s factory=%s" % (self.id, self.props, self.scope, self.factory) class ReferenceDef(object): """ This class represents a definition that is referencing another object. """ def __init__(self, name, ref): self.name = name self.ref = ref def prefetch(self, container): self.get_value(container) def get_value(self, container): return container.get_object(self.ref) def set_value(self, obj, container): setattr(obj, self.name, container.objects[self.ref]) def __str__(self): return "name=%s ref=%s" % (self.name, self.ref) class InnerObjectDef(object): """ This class represents an inner object. It is optional whether or not the object has its own name. """ def __init__(self, name, inner_comp): self.name = name self.inner_comp = inner_comp def prefetch(self, container): self.get_value(container) def get_value(self, container): return container.get_object(self.inner_comp.id) def set_value(self, obj, container): setattr(obj, self.name, self.get_value(container)) def __str__(self): return "name=%s inner_comp=%s" % (self.name, self.inner_comp) class ValueDef(object): """ This class represents a property that holds a value. The value can be simple value, or it can be a complex container which internally holds references, inner objects, or any other type. """ def __init__(self, name, value): self.name = name if value == "True": self.value = True elif value == "False": self.value= False else: self.value = value self.logger = logging.getLogger("springpython.config.ValueDef") def scan_value(self, container, value): if hasattr(value, "get_value"): return value.get_value(container) elif isinstance(value, tuple): new_list = [self.scan_value(container, item) for item in value] results = tuple(new_list) return results elif isinstance(value, list): new_list = [self.scan_value(container, item) for item in value] return new_list elif isinstance(value, set): results = set([self.scan_value(container, item) for item in value]) return results elif isinstance(value, frozenset): results = frozenset([self.scan_value(container, item) for item in value]) return results else: if value == "True": return True elif value == "False": return False else: return value def get_value(self, container): val = self._replace_refs_with_actuals(self.value, container) if val is None: return self.value else: return val def set_value(self, obj, container): setattr(obj, self.name, self.value) val = self._replace_refs_with_actuals(obj, container) def _replace_refs_with_actuals(self, obj, container): """Normal values do nothing for this step. However, sub-classes are defined for the various containers, like lists, set, dictionaries, etc., to handle iterating through and pre-fetching items.""" pass def __str__(self): return "name=%s value=%s" % (self.name, self.value) class DictDef(ValueDef): """Handles behavior for a dictionary-based value.""" def __init__(self, name, value): super(DictDef, self).__init__(name, value) def _replace_refs_with_actuals(self, obj, container): for key in self.value.keys(): if hasattr(self.value[key], "ref"): self.value[key] = container.get_object(self.value[key].ref) else: self.value[key] = self.scan_value(container, self.value[key]) class ListDef(ValueDef): """Handles behavior for a list-based value.""" def __init__(self, name, value): super(ListDef, self).__init__(name, value) self.logger = logging.getLogger("springpython.config.ListDef") def _replace_refs_with_actuals(self, obj, container): for i in range(0, len(self.value)): self.logger.debug("Checking out %s, wondering if I need to do any replacement..." % get_string(self.value[i])) if hasattr(self.value[i], "ref"): self.value[i] = container.get_object(self.value[i].ref) else: self.value[i] = self.scan_value(container, self.value[i]) class TupleDef(ValueDef): """Handles behavior for a tuple-based value.""" def __init__(self, name, value): super(TupleDef, self).__init__(name, value) def _replace_refs_with_actuals(self, obj, container): new_value = list(self.value) for i in range(0, len(new_value)): if hasattr(new_value[i], "ref"): new_value[i] = container.get_object(new_value[i].ref) else: new_value[i] = self.scan_value(container, new_value[i]) try: setattr(obj, self.name, tuple(new_value)) except AttributeError: pass return tuple(new_value) class SetDef(ValueDef): """Handles behavior for a set-based value.""" def __init__(self, name, value): super(SetDef, self).__init__(name, value) self.logger = logging.getLogger("springpython.config.SetDef") def _replace_refs_with_actuals(self, obj, container): self.logger.debug("Replacing refs with actuals...") self.logger.debug("set before changes = %s" % self.value) new_set = set() for item in self.value: if hasattr(item, "ref"): self.logger.debug("Item !!!%s!!! is a ref, trying to replace with actual object !!!%s!!!" % (item, item.ref)) #self.value.remove(item) #self.value.add(container.get_object(item.ref)) newly_fetched_value = container.get_object(item.ref) new_set.add(newly_fetched_value) self.logger.debug("Item !!!%s!!! was removed, and newly fetched value !!!%s!!! was added." % (item, newly_fetched_value)) #new_set.add(container.get_object(item.ref)) else: self.logger.debug("Item !!!%s!!! is NOT a ref, trying to replace with scanned value" % get_string(item)) #self.value.remove(item) #self.value.add(self.scan_value(container, item)) newly_scanned_value = self.scan_value(container, item) new_set.add(newly_scanned_value) self.logger.debug("Item !!!%s!!! was removed, and newly scanned value !!!%s!!! was added." % (item, newly_scanned_value)) #new_set.add(self.scan_value(container, item)) #self.value = new_set self.logger.debug("set after changes = %s" % new_set) #return self.value try: setattr(obj, self.name, new_set) except AttributeError: pass return new_set class FrozenSetDef(ValueDef): """Handles behavior for a frozen-set-based value.""" def __init__(self, name, value): super(FrozenSetDef, self).__init__(name, value) self.logger = logging.getLogger("springpython.config.FrozenSetDef") def _replace_refs_with_actuals(self, obj, container): self.logger.debug("Replacing refs with actuals...") self.logger.debug("set before changes = %s" % self.value) new_set = set() for item in self.value: if hasattr(item, "ref"): self.logger.debug("Item <<<%s>>> is a ref, trying to replace with actual object <<<%s>>>" % (item, item.ref)) #new_set.remove(item) #debug begin newly_fetched_value = container.get_object(item.ref) new_set.add(newly_fetched_value) self.logger.debug("Item <<<%s>>> was removed, and newly fetched value <<<%s>>> was added." % (item, newly_fetched_value)) #debug end #new_set.add(container.get_object(item.ref)) else: self.logger.debug("Item <<<%s>>> is NOT a ref, trying to replace with scanned value" % get_string(item)) #new_set.remove(item) #debug begin newly_scanned_value = self.scan_value(container, item) new_set.add(newly_scanned_value) self.logger.debug("Item <<<%s>>> was removed, and newly scanned value <<<%s>>> was added." % (item, newly_scanned_value)) #debug end #new_set.add(self.scan_value(container, item)) #self.logger.debug("Newly built set = %s" % new_set) #self.value = frozenset(new_set) new_frozen_set = frozenset(new_set) self.logger.debug("set after changes = %s" % new_frozen_set) #return self.value try: setattr(obj, self.name, new_frozen_set) except AttributeError: pass except TypeError: pass return new_frozen_set class Config(object): """ Config is an interface that defines how to read object definitions from an input source. """ def read_object_defs(self): """Abstract method definition - should return an array of Object objects""" raise NotImplementedError() springpython-1.2.0+ds/springpython/config/_python_config.py0000644000000000000000000001333011464332043022746 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ try: import cElementTree as etree except ImportError: try: import xml.etree.ElementTree as etree except ImportError: from elementtree import ElementTree as etree import re import types import inspect import logging from _config_base import * from springpython.context import scope from decorator import decorator, partial from springpython.context import ApplicationContextAware from springpython.factory import PythonObjectFactory from springpython.factory import ReflectiveObjectFactory from springpython.container import InvalidObjectScope class PythonConfig(Config, ApplicationContextAware): """ PythonConfig supports using pure python code to define objects. """ def __init__(self): self.logger = logging.getLogger("springpython.config.PythonConfig") super(PythonConfig, self).__init__() def read_object_defs(self): self.logger.debug("==============================================================") objects = [] self.logger.debug("Parsing %s" % self) for name, method in inspect.getmembers(self, inspect.ismethod): if name not in _pythonConfigMethods: try: wrapper = method.im_func.func_globals["_call_"] if wrapper.func_name.startswith("object"): c = ObjectDef(id=name, factory=PythonObjectFactory(method, wrapper), scope=wrapper.scope, lazy_init=wrapper.lazy_init, abstract=wrapper.abstract, parent=wrapper.parent) objects.append(c) except KeyError, e: pass self.logger.debug("==============================================================") return objects def set_app_context(self, app_context): super(PythonConfig, self).set_app_context(app_context) try: _object_context[(self,)]["container"] = app_context except KeyError, e: _object_context[(self,)] = {"container": app_context} _pythonConfigMethods = [name for (name, method) in inspect.getmembers(PythonConfig, inspect.ismethod)] _object_context = {} def _object_wrapper(f, scope, parent, log_func_name, *args, **kwargs): """ This function checks if the object already exists in the container. If so, it will retrieve its results. Otherwise, it calls the function. For prototype objects, the function is basically a pass through, because everytime a prototype function is called, there should be no caching of results. Using the @decorator library greatly simplifies the implementation of this. """ def _deco(f, scope, parent, log_func_name, *args, **kwargs): log = logging.getLogger("springpython.config.%s%s - %s%s" % (log_func_name, f, str(args), scope)) if f.func_name != top_func: log.debug("This is NOT the top-level object %s, deferring to container." % top_func) container = _object_context[args]["container"] log.debug("Container = %s" % container) if parent: parent_result = container.get_object(parent, ignore_abstract=True) log.debug("This IS the top-level object, calling %s(%s)" \ % (f.func_name, parent_result)) results = container.get_object(f.func_name)(parent_result) else: results = container.get_object(f.func_name) log.debug("Found %s inside the container" % results) return results else: if parent: container = _object_context[(args[0],)]["container"] parent_result = container.get_object(parent, ignore_abstract=True) log.debug("This IS the top-level object, calling %s(%s)" \ % (f.func_name, parent_result)) results = f(container, parent_result) else: log.debug("This IS the top-level object, calling %s()." % f.func_name) results = f(*args, **kwargs) log.debug("Found %s" % results) return results return _deco(f, scope, parent, log_func_name, *args, **kwargs) def Object(theScope=scope.SINGLETON, lazy_init=False, abstract=False, parent=None): """ This function is a wrapper around the function which returns the real decorator. It decides, based on scope and lazy-init, which decorator to return. """ if type(theScope) == types.FunctionType: return Object()(theScope) elif theScope == scope.SINGLETON: log_func_name = "objectSingleton" elif theScope == scope.PROTOTYPE: log_func_name = "objectPrototype" else: raise InvalidObjectScope("Don't know how to handle scope %s" % theScope) def object_wrapper(f, *args, **kwargs): return _object_wrapper(f, theScope, parent, log_func_name, *args, **kwargs) object_wrapper.lazy_init = lazy_init object_wrapper.abstract = abstract object_wrapper.parent = parent object_wrapper.scope = theScope return decorator(object_wrapper) springpython-1.2.0+ds/springpython/config/_yaml_config.py0000644000000000000000000004501011464332043022367 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import re import types import inspect import logging from _config_base import * from springpython.context import scope from decorator import decorator, partial from springpython.context import ApplicationContextAware from springpython.factory import PythonObjectFactory from springpython.factory import ReflectiveObjectFactory from springpython.container import InvalidObjectScope yaml_mappings = { "str":"types.StringType", "unicode":"types.UnicodeType", "int":"types.IntType", "long":"types.LongType", "float":"types.FloatType", "decimal":"decimal.Decimal", "bool":"types.BooleanType", "complex":"types.ComplexType", "list":"types.ListType", "tuple":"types.TupleType", "dict":"types.DictType", } class YamlConfig(Config): """ YamlConfig provides an alternative YAML-based version of objects. """ def __init__(self, config_location): if isinstance(config_location, list): self.config_location = config_location else: self.config_location = [config_location] self.logger = logging.getLogger("springpython.config.YamlConfig") # By making this an instance-based property (instead of function local), inner object # definitions can add themselves to the list in the midst of parsing an input. self.objects = [] def read_object_defs(self): import yaml self.logger.debug("==============================================================") # Reset, in case the file is re-read self.objects = [] for config in self.config_location: self.logger.debug("* Parsing %s" % config) stream = file(config) doc = yaml.load(stream) self.logger.debug(doc) # A dictionary of abstract objects, keyed by their IDs, used in # traversing the hierarchies of parents; built upfront here for # convenience. abstract_objects = {} for object in doc["objects"]: if "abstract" in object: abstract_objects[object["object"]] = object for object in doc["objects"]: if not "class" in object and not "parent" in object: self._map_custom_class(object, yaml_mappings) elif "parent" in object: # Children are added to self.objects during the children->abstract parents traversal. pos_constr = self._get_pos_constr(object) named_constr = self._get_named_constr(object) props = self._get_props(object) self._traverse_parents(object, object, pos_constr, named_constr, props, abstract_objects) continue self._print_obj(object) self.objects.append(self._convert_object(object)) self.logger.debug("==============================================================") self.logger.debug("objects = %s" % self.objects) return self.objects def _map_custom_class(self, obj, mappings): """ Enrich the object's attributes and make it look to the rest of YamlConfig as if the object had all of them right in the definition. """ for class_name in mappings: if class_name in obj: self.logger.debug("Found a matching type: %s -> %s" % (obj["object"], class_name)) obj["class"] = mappings[class_name] obj["constructor-args"] = [obj[class_name]] break else: self.logger.warning("No matching type found for object %s" % obj) def _traverse_parents(self, leaf, child, pos_constr, named_constr, props, abstract_objects): parent = abstract_objects[child["parent"]] # At this point we only build up the lists of parameters but we don't create # the object yet because the current parent object may still have its # own parent. # Positional constructors parent_pos_constrs = self._get_pos_constr(parent) # Make sure there are as many child positional parameters as there # are in the parent's list. len_pos_constr = len(pos_constr) len_parent_pos_constrs = len(parent_pos_constrs) if len_pos_constr < len_parent_pos_constrs: pos_constr.extend([None] * (len_parent_pos_constrs - len_pos_constr)) for idx, parent_pos_constr in enumerate(parent_pos_constrs): if not pos_constr[idx]: pos_constr[idx] = parent_pos_constr # Named constructors child_named_constrs = named_constr parent_named_constrs = self._get_named_constr(parent) for parent_named_constr in parent_named_constrs: if parent_named_constr not in child_named_constrs: named_constr[parent_named_constr] = parent_named_constrs[parent_named_constr] # Properties child_props = [prop.name for prop in props] parent_props = self._get_props(parent) for parent_prop in parent_props: if parent_prop.name not in child_props: props.append(parent_prop) if "parent" in parent: self._traverse_parents(leaf, parent, pos_constr, named_constr, props, abstract_objects) else: # Now we know we can create an object out of all the accumulated values. # The object's class is its topmost parent's class. class_ = parent["class"] id, factory, lazy_init, abstract, parent, scope_ = self._get_basic_object_data(leaf, class_) c = self._create_object(id, factory, lazy_init, abstract, parent, scope_, pos_constr, named_constr, props) self.objects.append(c) return parent def _get_pos_constr(self, object): """ Returns a list of all positional constructor arguments of an object. """ if "constructor-args" in object and isinstance(object["constructor-args"], list): return [self._convert_prop_def(object, constr, object["object"]) for constr in object["constructor-args"]] return [] def _get_named_constr(self, object): """ Returns a dictionary of all named constructor arguments of an object. """ if "constructor-args" in object and isinstance(object["constructor-args"], dict): return dict([(name, self._convert_prop_def(object, constr, object["object"])) for (name, constr) in object["constructor-args"].items()]) return {} def _get_props(self, object): """ Returns a list of all properties defined by an object. """ if "properties" in object: return [self._convert_prop_def(object, p, name) for (name, p) in object["properties"].items()] return [] def _create_object(self, id, factory, lazy_init, abstract, parent, scope, pos_constr, named_constr, props): """ A helper function which creates an object out of the supplied arguments. """ c = ObjectDef(id=id, factory=factory, lazy_init=lazy_init, abstract=abstract, parent=parent) c.scope = scope c.pos_constr = pos_constr c.named_constr = named_constr c.props = props self.logger.debug("object: props = %s" % c.props) self.logger.debug("object: There are %s props" % len(c.props)) return c def _get_basic_object_data(self, object, class_): """ A convenience method which creates basic object's data so that the code is not repeated. """ if "scope" in object: scope_ = scope.convert(object["scope"]) else: scope_ = scope.SINGLETON return(object["object"], ReflectiveObjectFactory(class_), object.get("lazy-init", False), object.get("abstract", False), object.get("parent"), scope_) def _convert_object(self, object, prefix=""): "This function generates a object definition, then converts scope and property elements." if prefix != "": if "object" in object and object["object"] is not None: object["object"] = prefix + "." + object["object"] else: object["object"] = prefix + "." id, factory, lazy_init, abstract, parent, scope_ = self._get_basic_object_data(object, object.get("class")) pos_constr = self._get_pos_constr(object) named_constr = self._get_named_constr(object) props = self._get_props(object) return self._create_object(id, factory, lazy_init, abstract, parent, scope_, pos_constr, named_constr, props) def _print_obj(self, obj, level=0): self.logger.debug("%sobject = %s" % ("\t"*level, obj["object"])) self.logger.debug("%sobject id = %s" % ("\t"*level, obj["object"])) self.logger.debug("%sclass = %s" % ("\t"*(level+1), obj["class"])) if "scope" in obj: self.logger.debug("%sscope = %s" % ("\t"*(level+1), obj["scope"])) else: self.logger.debug("%sscope = singleton (default)" % ("\t"*(level+1))) if "properties" in obj: self.logger.debug("%sproperties:" % ("\t"*(level+1))) for prop in obj["properties"].keys(): if "object" in obj["properties"][prop]: self.logger.debug("%s%s = ..." % ("\t"*(level+2), prop)) self._print_obj(obj["properties"][prop], level+3) else: self.logger.debug("%s%s = %s" % ("\t"*(level+2), prop, obj["properties"][prop])) self.logger.debug("") def _convert_ref(self, ref_node, name): self.logger.debug("ref: Parsing %s, %s" % (ref_node, name)) if "object" in ref_node: return ReferenceDef(name, ref_node["object"]) else: return ReferenceDef(name, ref_node) def _convert_value(self, value, id, name): results = [] if isinstance(value, dict): if "tuple" in value: self.logger.debug("value: Converting tuple") return self._convert_tuple(value["tuple"], id, name) elif "list" in value: self.logger.debug("value: Converting list") return self._convert_list(value["list"], id, name) elif "dict" in value: self.logger.debug("value: Converting dict") return self._convert_dict(value["dict"], id, name) elif "set" in value: self.logger.debug("value: Converting set") return self._convert_set(value["set"], id, name) elif "frozenset" in value: self.logger.debug("value: Converting frozenset") return self._convert_frozen_set(value["frozenset"], id, name) else: self.logger.debug("value: Plain ole value = %s" % value) return value return results def _convert_dict(self, dict_node, id, name): d = {} for (k, v) in dict_node.items(): if isinstance(v, dict): self.logger.debug("dict: You have a special type stored at %s" % k) if "ref" in v: self.logger.debug("dict/ref: k,v = %s,%s" % (k, v)) d[k] = self._convert_ref(v["ref"], "%s.dict['%s']" % (name, k)) self.logger.debug("dict: Stored %s => %s" % (k, d[k])) elif "tuple" in v: self.logger.debug("dict: Converting a tuple...") d[k] = self._convert_tuple(v["tuple"], id, "%s.dict['%s']" % (name, k)) else: self.logger.debug("dict: Don't know how to handle type %s" % v) else: self.logger.debug("dict: %s is NOT a dict, so going to convert as a value." % v) d[k] = self._convert_value(v, id, "%s.dict['%s']" % (name, k)) return DictDef(name, d) def _convert_props(self, props_node, name): dict = {} for prop in props_node.prop: dict[prop.key] = str(prop) return DictDef(name, dict) def _convert_list(self, list_node, id, name): list = [] for item in list_node: self.logger.debug("list: Adding %s to list..." % item) if isinstance(item, dict): if "ref" in item: list.append(self._convert_ref(item["ref"], "%s.list[%s]" % (name, len(list)))) elif "object" in item: list.append(self._convert_inner_object(item, id, "%s.list[%s]" % (name, len(list)))) elif len(set(["dict", "tuple", "set", "frozenset", "list"]) & set(item)) > 0: list.append(self._convert_value(item, id, "%s.list[%s]" % (name, len(list)))) else: self.logger.debug("list: Don't know how to handle %s" % item.keys()) else: list.append(item) return ListDef(name, list) def _convert_tuple(self, tuple_node, id, name): list = [] self.logger.debug("tuple: tuple_node = %s, id = %s, name = %s" % (tuple_node, id, name)) for item in tuple_node: if isinstance(item, dict): if "ref" in item: list.append(self._convert_ref(item["ref"], name + ".tuple")) elif "object" in item: list.append(self._convert_inner_object(item, id, "%s.tuple[%s]" % (name, len(list)))) elif len(set(["dict", "tuple", "set", "frozenset", "list"]) & set(item)) > 0: list.append(self._convert_value(item, id, "%s.tuple[%s]" % (name, len(list)))) else: self.logger.debug("tuple: Don't know how to handle %s" % item) else: list.append(item) return TupleDef(name, tuple(list)) def _convert_set(self, set_node, id, name): s = set() self.logger.debug("set: set_node = %s, id = %s, name = %s" % (set_node, id, name)) for item in set_node: if isinstance(item, dict): if "ref" in item: s.add(self._convert_ref(item["ref"], name + ".set")) elif "object" in item: s.add(self._convert_inner_object(item, id, "%s.set[%s]" % (name, len(s)))) elif len(set(["dict", "tuple", "set", "frozenset", "list"]) & set(item)) > 0: s.add(self._convert_value(item, id, "%s.set[%s]" % (name, len(s)))) else: self.logger.debug("set: Don't know how to handle %s" % item) else: s.add(item) return SetDef(name, s) def _convert_frozen_set(self, frozen_set_node, id, name): item = self._convert_set(frozen_set_node, id, name) self.logger.debug("frozenset: Just got back converted set %s" % item) self.logger.debug("frozenset: value is %s, which will be turned into %s" % (item.value, frozenset(item.value))) return FrozenSetDef(name, frozenset(item.value)) def _convert_inner_object(self, object_node, id, name): self.logger.debug("inner object: Converting %s" % object_node) inner_object_def = self._convert_object(object_node, prefix="%s.%s" % (id, name)) self.objects.append(inner_object_def) return InnerObjectDef(name, inner_object_def) def _convert_prop_def(self, comp, p, name): "This function translates object properties into useful collections of information for the container." self.logger.debug("prop_def: Trying to read property %s -> %s" % (name, p)) if isinstance(p, dict): if "ref" in p: self.logger.debug("prop_def: >>>>>>>>>>>>Call _convert_ref(%s, %s)" % (p["ref"], name)) return self._convert_ref(p["ref"], name) elif "tuple" in p: self.logger.debug("prop_def: Call _convert_tuple(%s,%s,%s)" % (p["tuple"], comp["object"], name)) return self._convert_tuple(p["tuple"], comp["object"], name) elif "set" in p: self.logger.debug("prop_def: Call _convert_set(%s,%s,%s)" % (p["set"], comp["object"], name)) return self._convert_set(p["set"], comp["object"], name) elif "frozenset" in p: self.logger.debug("prop_def: Call _convert_frozen_set(%s,%s,%s)" % (p["frozenset"], comp["object"], name)) return self._convert_frozen_set(p["frozenset"], comp["object"], name) elif "object" in p: self.logger.debug("prop_def: Call _convert_inner_object(%s,%s,%s)" % (p, comp["object"], name)) return self._convert_inner_object(p, comp["object"], name) else: #self.logger.debug("prop_def: Don't know how to handle %s" % p) return self._convert_dict(p, comp["object"], name) elif isinstance(p, list): return self._convert_list(p, comp["object"], name) elif isinstance(p, unicode): return ValueDef(name, unicode(p)) else: return ValueDef(name, str(p)) return None if hasattr(p, "ref"): return self._convert_ref(p.ref, name) elif hasattr(p, "value"): return ValueDef(name, str(p.value)) elif hasattr(p, "dict"): return self._convert_dict(p.dict, comp.id, name) elif hasattr(p, "props"): return self._convert_props(p.props, name) elif hasattr(p, "list"): return self._convert_list(p.list, comp.id, name) elif hasattr(p, "tuple"): return self._convert_tuple(p.tuple, comp.id, name) elif hasattr(p, "set"): return self._convert_set(p.set, comp.id, name) elif hasattr(p, "frozenset"): self.logger.debug("Converting frozenset") return self._convert_frozen_set(p.frozenset, comp.id, name) elif hasattr(p, "object"): return self._convert_inner_object(p.object, comp.id, name) springpython-1.2.0+ds/springpython/config/_xml_config.py0000644000000000000000000007136611464332043022242 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ try: import cElementTree as etree except ImportError: try: import xml.etree.ElementTree as etree except ImportError: from elementtree import ElementTree as etree import re import types import inspect import logging from _config_base import * from springpython.context import scope from springpython.context import ApplicationContextAware from springpython.factory import PythonObjectFactory from springpython.factory import ReflectiveObjectFactory from springpython.container import InvalidObjectScope xml_mappings = { "str":"types.StringType", "unicode":"types.UnicodeType", "int":"types.IntType", "long":"types.LongType", "float":"types.FloatType", "decimal":"decimal.Decimal", "bool":"types.BooleanType", "complex":"types.ComplexType", } class PyContainerConfig(Config): """ PyContainerConfig supports the legacy XML dialect (PyContainer) of reading object definitions. """ NS = "{http://www.springframework.org/springpython/schema/pycontainer-components}" def __init__(self, config_location): if isinstance(config_location, list): self.config_location = config_location else: self.config_location = [config_location] self.logger = logging.getLogger("springpython.config.PyContainerConfig") def read_object_defs(self): self.logger.debug("==============================================================") objects = [] for config in self.config_location: self.logger.debug("* Parsing %s" % config) components = etree.parse(config).getroot() objects.extend([self._convert_component(component) for component in components]) self.logger.debug("==============================================================") return objects def _convert_component(self, component): "This function generates a object definition, then converts scope and property elements." self.logger.debug("component: Processing %s" % component) c = ObjectDef(component.get("id"), factory=ReflectiveObjectFactory(component.get("class"))) if "scope" in component.attrib: c.scope = scope.convert(component.get("scope")) c.props = [self._convert_prop_def(p) for p in component.findall(self.NS+"property")] return c def _convert_prop_def(self, p): "This function translates object properties into useful dictionaries of information for the container." if "local" in p.attrib or p.find(self.NS+"local") is not None: if "local" in p.attrib: return ReferenceDef(p.get("name"), p.get("local")) else: return ReferenceDef(p.get("name"), p.find(self.NS+"local")) elif "list" in p.attrib or p.find(self.NS+"list") is not None: if "list" in p.attrib: return ListDef(p.name, [ReferenceDef(p.name + ".list", prop_list.local) for prop_list in p.list]) else: return ListDef(p.name, [ReferenceDef(p.name + ".list", prop_list.local) for prop_list in p.list]) else: self.logger.debug("py: name = %s code = %s" % (p.get("name"), p.text)) thing = eval(str(p.text).strip()) self.logger.debug("py: You have parsed %s" % thing) return ValueDef(p.get("name"), eval(str(p.text).strip())) class SpringJavaConfig(Config): """ SpringJavaConfig supports current Spring Java format of XML bean definitions. """ NS = "{http://www.springframework.org/schema/beans}" def __init__(self, config_location): if isinstance(config_location, list): self.config_location = config_location else: self.config_location = [config_location] self.logger = logging.getLogger("springpython.config.SpringJavaConfig") # By making this an instance-based property (instead of function local), inner object # definitions can add themselves to the list in the midst of parsing an input. self.objects = [] def read_object_defs(self): self.logger.debug("==============================================================") # Reset, in case the file is re-read self.objects = [] for config in self.config_location: self.logger.debug("* Parsing %s" % config) beans = etree.parse(config).getroot() self.objects.extend([self._convert_bean(bean) for bean in beans]) self.logger.debug("==============================================================") return self.objects def _convert_bean(self, bean, prefix=""): "This function generates a object definition, then converts scope and property elements." if prefix != "": if "id" in bean.attrib: bean.set("id", prefix + bean.get("id")) else: bean.set("id", prefix + "") c = ObjectDef(bean.get("id"), factory=ReflectiveObjectFactory(bean.get("class"))) if "scope" in bean.attrib: c.scope = scope.convert(bean.get("scope")) self.logger.debug("bean: %s" % bean) c.pos_constr = [self._convert_prop_def(bean, constr, bean.get("id") + ".constr") for constr in bean.findall(self.NS+"constructor-arg")] self.logger.debug("Constructors = %s" % c.pos_constr) c.props = [self._convert_prop_def(bean, p, p.get("name")) for p in bean.findall(self.NS+"property")] return c def _convert_prop_def(self, bean, p, name): "This function translates object constructors/properties into useful collections of information for the container." if "ref" in p.keys() or p.find(self.NS+"ref") is not None: if "ref" in p.keys(): return ReferenceDef(name, p.get("ref")) else: return ReferenceDef(name, p.find(self.NS+"ref").get("bean")) elif "value" in p.keys() or p.find(self.NS+"value") is not None: if "value" in p.keys(): return ValueDef(name, p.get("value")) else: return ValueDef(name, p.find(self.NS+"value").text) elif p.find(self.NS+"map") is not None: dict = {} for entry in p.find(self.NS+"map"): key = entry.find(self.NS+"key").find(self.NS+"value").text if entry.find(self.NS+"value") is not None: dict[key] = str(entry.find(self.NS+"value").text) elif entry.find(self.NS+"ref") is not None: dict[key] = ReferenceDef(key, entry.find(self.NS+"ref").get("bean")) else: self.logger.debug("Don't know how to handle %s" % entry) return DictDef(name, dict) elif p.find(self.NS+"props") is not None: dict = {} for prop in p.find(self.NS+"props"): dict[prop.get("key")] = str(prop.text) return DictDef(name, dict) elif p.find(self.NS+"list") is not None: list = [] for element in p.find(self.NS+"list"): if element.tag == self.NS+"value": list.append(element.text) elif element.tag == self.NS+"ref": list.append(ReferenceDef(name + ".list", element.get("bean"))) else: self.logger.debug("Don't know how to handle %s" % element.tag) return ListDef(name, list) elif p.find(self.NS+"set") is not None: s = set() for element in p.find(self.NS+"set"): if element.tag == self.NS+"value": s.add(element.text) elif element.tag == self.NS+"ref": s.add(ReferenceDef(name + ".set", element.get("bean"))) else: self.logger.debug("Don't know how to handle %s" % element.tag) return SetDef(name, s) elif p.find(self.NS+"bean"): inner_object_def = self._convert_bean(p.find(self.NS+"bean"), prefix=bean.get("id") + "." + name + ".") self.objects.append(inner_object_def) return InnerObjectDef(name, inner_object_def) class XMLConfig(Config): """ XMLConfig supports current Spring Python format of XML object definitions. """ NS = "{http://www.springframework.org/springpython/schema/objects}" NS_11 = "{http://www.springframework.org/springpython/schema/objects/1.1}" def __init__(self, config_location): if isinstance(config_location, list): self.config_location = config_location else: self.config_location = [config_location] self.logger = logging.getLogger("springpython.config.XMLConfig") # By making this an instance-based property (instead of function local), inner object # definitions can add themselves to the list in the midst of parsing an input. self.objects = [] def read_object_defs(self): self.logger.debug("==============================================================") # Reset, in case the file is re-read self.objects = [] for config in self.config_location: self.logger.debug("* Parsing %s" % config) # A flat list of objects, as found in the XML document. objects = etree.parse(config).getroot() # We need to handle both 1.0 and 1.1 XSD schemata *and* we may be # passed a list of config locations of different XSD versions so we # must find out here which one is used in the current config file # and pass the correct namespace down to other parts of XMLConfig. ns = objects.tag[:objects.tag.find("}") + 1] # A dictionary of abstract objects, keyed by their IDs, used in # traversing the hierarchies of parents; built upfront here for # convenience. abstract_objects = {} for obj in objects: if obj.get("abstract"): abstract_objects[obj.get("id")] = obj for obj in objects: if obj.get("class") is None and not obj.get("parent"): self._map_custom_class(obj, xml_mappings, ns) elif obj.get("parent"): # Children are added to self.objects during the children->abstract parents traversal. pos_constr = self._get_pos_constr(obj, ns) named_constr = self._get_named_constr(obj, ns) props = self._get_props(obj, ns) self._traverse_parents(obj, obj, ns, pos_constr, named_constr, props, abstract_objects) continue self.objects.append(self._convert_object(obj, ns=ns)) self.logger.debug("==============================================================") for object in self.objects: self.logger.debug("Parsed %s" % object) return self.objects def _map_custom_class(self, obj, mappings, ns): """ Fill in the missing attributes of Python objects and make it look to the rest of XMLConfig as if they already were in the XML config file. """ for class_name in mappings: tag_no_ns = obj.tag.replace(ns, "") if class_name == tag_no_ns: obj.set("class", mappings[class_name]) constructor_arg = etree.Element("%s%s" % (ns, "constructor-arg")) value = etree.Element("%s%s" % (ns, "value")) value.text = obj.text obj.append(constructor_arg) constructor_arg.append(value) obj.text = "" break else: self.logger.warning("No matching type found for object %s" % obj) def _traverse_parents(self, leaf, child, ns, pos_constr, named_constr, props, abstract_objects): parent = abstract_objects[child.get("parent")] # At this point we only build up the lists of parameters but we don't create # the object yet because the current parent object may still have its # own parent. # Positional constructors parent_pos_constrs = self._get_pos_constr(parent, ns) # Make sure there are as many child positional parameters as there # are in the parent's list. len_pos_constr = len(pos_constr) len_parent_pos_constrs = len(parent_pos_constrs) if len_pos_constr < len_parent_pos_constrs: pos_constr.extend([None] * (len_parent_pos_constrs - len_pos_constr)) for idx, parent_pos_constr in enumerate(parent_pos_constrs): if not pos_constr[idx]: pos_constr[idx] = parent_pos_constr # Named constructors child_named_constrs = named_constr parent_named_constrs = self._get_named_constr(parent, ns) for parent_named_constr in parent_named_constrs: if parent_named_constr not in child_named_constrs: named_constr[parent_named_constr] = parent_named_constrs[parent_named_constr] # Properties child_props = [prop.name for prop in props] parent_props = self._get_props(parent, ns) for parent_prop in parent_props: if parent_prop.name not in child_props: props.append(parent_prop) if parent.get("parent"): self._traverse_parents(leaf, parent, ns, pos_constr, named_constr, props, abstract_objects) else: # Now we know we can create an object out of all the accumulated values. # The object's class is its topmost parent's class. class_ = parent.get("class") id, factory, lazy_init, abstract, parent, scope_ = self._get_basic_object_data(leaf, class_) c = self._create_object(id, factory, lazy_init, abstract, parent, scope_, pos_constr, named_constr, props) self.objects.append(c) return parent def _get_pos_constr(self, object, ns): """ Returns a list of all positional constructor arguments of an object. """ return [self._convert_prop_def(object, constr, object.get("id") + ".constr", ns) for constr in object.findall(ns+"constructor-arg") if not "name" in constr.attrib] def _get_named_constr(self, object, ns): """ Returns a dictionary of all named constructor arguments of an object. """ return dict([(str(constr.get("name")), self._convert_prop_def(object, constr, object.get("id") + ".constr", ns)) for constr in object.findall(ns+"constructor-arg") if "name" in constr.attrib]) def _get_props(self, object, ns): """ Returns a list of all properties defined by an object. """ return [self._convert_prop_def(object, p, p.get("name"), ns) for p in object.findall(ns+"property")] def _create_object(self, id, factory, lazy_init, abstract, parent, scope, pos_constr, named_constr, props): """ A helper function which creates an object out of the supplied arguments. """ c = ObjectDef(id=id, factory=factory, lazy_init=lazy_init, abstract=abstract, parent=parent) c.scope = scope c.pos_constr = pos_constr c.named_constr = named_constr c.props = props self.logger.debug("object: props = %s" % c.props) self.logger.debug("object: There are %s props" % len(c.props)) return c def _get_basic_object_data(self, object, class_): """ A convenience method which creates basic object's data so that the code is not repeated. """ if "scope" in object.attrib: scope_ = scope.convert(object.get("scope")) else: scope_ = scope.SINGLETON return(object.get("id"), ReflectiveObjectFactory(class_), object.get("lazy-init", False), object.get("abstract", False), object.get("parent"), scope_) def _convert_object(self, object, prefix="", ns=None): """ This function collects all parameters required for an object creation and then calls a helper function which creates it. """ if prefix != "": if "id" in object.attrib: object.set("id", prefix + "." + object.get("id")) else: object.set("id", prefix + ".") id, factory, lazy_init, abstract, parent, scope_ = self._get_basic_object_data(object, object.get("class")) pos_constr = self._get_pos_constr(object, ns) named_constr = self._get_named_constr(object, ns) props = self._get_props(object, ns) return self._create_object(id, factory, lazy_init, abstract, parent, scope_, pos_constr, named_constr, props) def _convert_ref(self, ref_node, name): if hasattr(ref_node, "attrib"): results = ReferenceDef(name, ref_node.get("object")) self.logger.debug("ref: Returning %s" % results) return results else: results = ReferenceDef(name, ref_node) self.logger.debug("ref: Returning %s" % results) return results def _convert_value(self, value, id, name, ns): if value.text is not None and value.text.strip() != "": self.logger.debug("value: Converting a direct value <%s>" % value.text) return value.text else: if value.tag == ns+"value": self.logger.debug("value: Converting a value's children %s" % value.getchildren()[0]) results = self._convert_value(value.getchildren()[0], id, name, ns) self.logger.debug("value: results = %s" % str(results)) return results elif value.tag == ns+"tuple": self.logger.debug("value: Converting a tuple") return self._convert_tuple(value, id, name, ns).value elif value.tag == ns+"list": self.logger.debug("value: Converting a list") return self._convert_list(value, id, name, ns).value elif value.tag == ns+"dict": self.logger.debug("value: Converting a dict") return self._convert_dict(value, id, name, ns).value elif value.tag == ns+"set": self.logger.debug("value: Converting a set") return self._convert_set(value, id, name, ns).value elif value.tag == ns+"frozenset": self.logger.debug("value: Converting a frozenset") return self._convert_frozen_set(value, id, name, ns).value else: self.logger.debug("value: %s.%s Don't know how to handle %s" % (id, name, value.tag)) def _convert_dict(self, dict_node, id, name, ns): dict = {} for entry in dict_node.findall(ns+"entry"): self.logger.debug("dict: entry = %s" % entry) key = entry.find(ns+"key").find(ns+"value").text self.logger.debug("dict: key = %s" % key) if entry.find(ns+"value") is not None: dict[key] = self._convert_value(entry.find(ns+"value"), id, "%s.dict['%s']" % (name, key), ns) elif entry.find(ns+"ref") is not None: dict[key] = self._convert_ref(entry.find(ns+"ref"), "%s.dict['%s']" % (name, key)) elif entry.find(ns+"object") is not None: self.logger.debug("dict: Parsing an inner object definition...") dict[key] = self._convert_inner_object(entry.find(ns+"object"), id, "%s.dict['%s']" % (name, key), ns) else: for token in ["dict", "tuple", "set", "frozenset", "list"]: if entry.find(ns+token) is not None: dict[key] = self._convert_value(entry.find(ns+token), id, "%s.dict['%s']" % (name, key), ns) break if key not in dict: self.logger.debug("dict: Don't know how to handle %s" % entry.tag) self.logger.debug("Dictionary is now %s" % dict) return DictDef(name, dict) def _convert_props(self, props_node, name, ns): dict = {} self.logger.debug("props: Looking at %s" % props_node) for prop in props_node: dict[prop.get("key")] = str(prop.text) self.logger.debug("props: Dictionary is now %s" % dict) return DictDef(name, dict) def _convert_list(self, list_node, id, name, ns): list = [] self.logger.debug("list: Parsing %s" % list_node) for element in list_node: if element.tag == ns+"value": list.append(get_string(element.text)) elif element.tag == ns+"ref": list.append(self._convert_ref(element, "%s.list[%s]" % (name, len(list)))) elif element.tag == ns+"object": self.logger.debug("list: Parsing an inner object definition...") list.append(self._convert_inner_object(element, id, "%s.list[%s]" % (name, len(list)), ns)) elif element.tag in [ns+token for token in ["dict", "tuple", "set", "frozenset", "list"]]: self.logger.debug("This list has child elements of type %s." % element.tag) list.append(self._convert_value(element, id, "%s.list[%s]" % (name, len(list)), ns)) self.logger.debug("List is now %s" % list) else: self.logger.debug("list: Don't know how to handle %s" % element.tag) self.logger.debug("List is now %s" % list) return ListDef(name, list) def _convert_tuple(self, tuple_node, id, name, ns): list = [] self.logger.debug("tuple: Parsing %s" % tuple_node) for element in tuple_node: self.logger.debug("tuple: Looking at %s" % element) if element.tag == ns+"value": self.logger.debug("tuple: Appending %s" % element.text) list.append(get_string(element.text)) elif element.tag == ns+"ref": list.append(self._convert_ref(element, "%s.tuple(%s}" % (name, len(list)))) elif element.tag == ns+"object": self.logger.debug("tuple: Parsing an inner object definition...") list.append(self._convert_inner_object(element, id, "%s.tuple(%s)" % (name, len(list)), ns)) elif element.tag in [ns+token for token in ["dict", "tuple", "set", "frozenset", "list"]]: self.logger.debug("tuple: This tuple has child elements of type %s." % element.tag) list.append(self._convert_value(element, id, "%s.tuple(%s)" % (name, len(list)), ns)) self.logger.debug("tuple: List is now %s" % list) else: self.logger.debug("tuple: Don't know how to handle %s" % element.tag) self.logger.debug("Tuple is now %s" % str(tuple(list))) return TupleDef(name, tuple(list)) def _convert_set(self, set_node, id, name, ns): s = set() self.logger.debug("set: Parsing %s" % set_node) for element in set_node: self.logger.debug("Looking at element %s" % element) if element.tag == ns+"value": s.add(get_string(element.text)) elif element.tag == ns+"ref": s.add(self._convert_ref(element, name + ".set")) elif element.tag == ns+"object": self.logger.debug("set: Parsing an inner object definition...") s.add(self._convert_inner_object(element, id, "%s.set(%s)" % (name, len(s)), ns)) elif element.tag in [ns+token for token in ["dict", "tuple", "set", "frozenset", "list"]]: self.logger.debug("set: This set has child elements of type %s." % element.tag) s.add(self._convert_value(element, id, "%s.set(%s)" % (name,len(s)), ns)) else: self.logger.debug("set: Don't know how to handle %s" % element.tag) self.logger.debug("Set is now %s" % s) return SetDef(name, s) def _convert_frozen_set(self, frozen_set_node, id, name, ns): item = self._convert_set(frozen_set_node, id, name, ns) self.logger.debug("frozenset: Frozen set is now %s" % frozenset(item.value)) return FrozenSetDef(name, frozenset(item.value)) def _convert_inner_object(self, object_node, id, name, ns): inner_object_def = self._convert_object(object_node, prefix="%s.%s" % (id, name), ns=ns) self.logger.debug("innerobj: Innerobject is now %s" % inner_object_def) self.objects.append(inner_object_def) return InnerObjectDef(name, inner_object_def) def _convert_prop_def(self, comp, p, name, ns): "This function translates object properties into useful collections of information for the container." #self.logger.debug("Is %s.%s a ref? %s" % (comp.get("id"), p.get("name"), p.find(ns+"ref") is not None or "ref" in p.attrib)) #self.logger.debug("Is %s.%s a value? %s" % (comp.get("id"), p.get("name"), p.find(ns+"value") is not None or "value" in p.attrib)) #self.logger.debug("Is %s.%s an inner object? %s" % (comp.get("id"), p.get("name"), p.find(ns+"object") is not None or "object" in p.attrib)) #self.logger.debug("Is %s.%s a dict? %s" % (comp.get("id"), p.get("name"), p.find(ns+"dict") is not None or "dict" in p.attrib)) #self.logger.debug("Is %s.%s a list? %s" % (comp.get("id"), p.get("name"), p.find(ns+"list") is not None or "list" in p.attrib)) #self.logger.debug("Is %s.%s a tuple? %s" % (comp.get("id"), p.get("name"), p.find(ns+"tuple") is not None or "tuple" in p.attrib)) #self.logger.debug("Is %s.%s a set? %s" % (comp.get("id"), p.get("name"), p.find(ns+"set") is not None or "set" in p.attrib)) #self.logger.debug("Is %s.%s a frozenset? %s" % (comp.get("id"), p.get("name"), p.find(ns+"frozenset") is not None or "frozenset" in p.attrib)) #self.logger.debug("") if "ref" in p.attrib or p.find(ns+"ref") is not None: if "ref" in p.attrib: return self._convert_ref(p.get("ref"), name) else: return self._convert_ref(p.find(ns+"ref"), name) elif "value" in p.attrib or p.find(ns+"value") is not None: if "value" in p.attrib: return ValueDef(name, get_string(p.get("value"))) else: return ValueDef(name, get_string(p.find(ns+"value").text)) elif "dict" in p.attrib or p.find(ns+"dict") is not None: if "dict" in p.attrib: return self._convert_dict(p.get("dict"), comp.get("id"), name, ns) else: return self._convert_dict(p.find(ns+"dict"), comp.get("id"), name, ns) elif "props" in p.attrib or p.find(ns+"props") is not None: if "props" in p.attrib: return self._convert_props(p.get("props"), name, ns) else: return self._convert_props(p.find(ns+"props"), name, ns) elif "list" in p.attrib or p.find(ns+"list") is not None: if "list" in p.attrib: return self._convert_list(p.get("list"), comp.get("id"), name, ns) else: return self._convert_list(p.find(ns+"list"), comp.get("id"), name, ns) elif "tuple" in p.attrib or p.find(ns+"tuple") is not None: if "tuple" in p.attrib: return self._convert_tuple(p.get("tuple"), comp.get("id"), name, ns) else: return self._convert_tuple(p.find(ns+"tuple"), comp.get("id"), name, ns) elif "set" in p.attrib or p.find(ns+"set") is not None: if "set" in p.attrib: return self._convert_set(p.get("set"), comp.get("id"), name, ns) else: return self._convert_set(p.find(ns+"set"), comp.get("id"), name, ns) elif "frozenset" in p.attrib or p.find(ns+"frozenset") is not None: if "frozenset" in p.attrib: return self._convert_frozen_set(p.get("frozenset"), comp.get("id"), name, ns) else: return self._convert_frozen_set(p.find(ns+"frozenset"), comp.get("id"), name, ns) elif "object" in p.attrib or p.find(ns+"object") is not None: if "object" in p.attrib: return self._convert_inner_object(p.get("object"), comp.get("id"), name, ns) else: return self._convert_inner_object(p.find(ns+"object"), comp.get("id"), name, ns) springpython-1.2.0+ds/springpython/config/decorator.py0000644000000000000000000002324011412453030021715 0ustar rootroot########################## LICENCE ############################### ## ## Copyright (c) 2005, Michele Simionato ## All rights reserved. ## ## Redistributions of source code must retain the above copyright ## notice, this list of conditions and the following disclaimer. ## Redistributions in bytecode form must reproduce the above copyright ## notice, this list of conditions and the following disclaimer in ## the documentation and/or other materials provided with the ## distribution. ## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT ## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR ## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT ## HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, ## INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, ## BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS ## OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ## ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR ## TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE ## USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH ## DAMAGE. """ Decorator module, see http://pypi.python.org/pypi/decorator for the documentation. """ __all__ = ["decorator", "FunctionMaker", "partial", "deprecated", "getinfo", "new_wrapper"] import os, sys, re, inspect, warnings try: from functools import partial except ImportError: # for Python version < 2.5 class partial(object): "A simple replacement of functools.partial" def __init__(self, func, *args, **kw): self.func = func self.args = args self.keywords = kw def __call__(self, *otherargs, **otherkw): kw = self.keywords.copy() kw.update(otherkw) return self.func(*(self.args + otherargs), **kw) DEF = re.compile('\s*def\s*([_\w][_\w\d]*)\s*\(') # basic functionality class FunctionMaker(object): """ An object with the ability to create functions with a given signature. It has attributes name, doc, module, signature, defaults, dict and methods update and make. """ def __init__(self, func=None, name=None, signature=None, defaults=None, doc=None, module=None, funcdict=None): if func: # func can also be a class or a callable, but not an instance method self.name = func.__name__ if self.name == '': # small hack for lambda functions self.name = '_lambda_' self.doc = func.__doc__ self.module = func.__module__ if inspect.isfunction(func): self.signature = inspect.formatargspec( formatvalue=lambda val: "", *inspect.getargspec(func))[1:-1] self.defaults = func.func_defaults self.dict = func.__dict__.copy() if name: self.name = name if signature is not None: self.signature = signature if defaults: self.defaults = defaults if doc: self.doc = doc if module: self.module = module if funcdict: self.dict = funcdict # check existence required attributes assert hasattr(self, 'name') if not hasattr(self, 'signature'): raise TypeError('You are decorating a non function: %s' % func) def update(self, func, **kw): "Update the signature of func with the data in self" func.__name__ = self.name func.__doc__ = getattr(self, 'doc', None) func.__dict__ = getattr(self, 'dict', {}) func.func_defaults = getattr(self, 'defaults', ()) callermodule = sys._getframe(3).f_globals.get('__name__', '?') func.__module__ = getattr(self, 'module', callermodule) func.__dict__.update(kw) def make(self, src_templ, evaldict=None, addsource=False, **attrs): "Make a new function from a given template and update the signature" src = src_templ % vars(self) # expand name and signature evaldict = evaldict or {} mo = DEF.match(src) if mo is None: raise SyntaxError('not a valid function template\n%s' % src) name = mo.group(1) # extract the function name reserved_names = set([name] + [ arg.strip(' *') for arg in self.signature.split(',')]) for n, v in evaldict.iteritems(): if n in reserved_names: raise NameError('%s is overridden in\n%s' % (n, src)) if not src.endswith('\n'): # add a newline just for safety src += '\n' try: code = compile(src, '', 'single') exec code in evaldict except: print >> sys.stderr, 'Error in generated code:' print >> sys.stderr, src raise func = evaldict[name] if addsource: attrs['__source__'] = src self.update(func, **attrs) return func @classmethod def create(cls, obj, body, evaldict, defaults=None, addsource=True,**attrs): """ Create a function from the strings name, signature and body. evaldict is the evaluation dictionary. If addsource is true an attribute __source__ is added to the result. The attributes attrs are added, if any. """ if isinstance(obj, str): # "name(signature)" name, rest = obj.strip().split('(', 1) signature = rest[:-1] func = None else: # a function name = None signature = None func = obj fun = cls(func, name, signature, defaults) ibody = '\n'.join(' ' + line for line in body.splitlines()) return fun.make('def %(name)s(%(signature)s):\n' + ibody, evaldict, addsource, **attrs) def decorator(caller, func=None): """ decorator(caller) converts a caller function into a decorator; decorator(caller, func) decorates a function using a caller. """ if func is not None: # returns a decorated function return FunctionMaker.create( func, "return _call_(_func_, %(signature)s)", dict(_call_=caller, _func_=func), undecorated=func) else: # returns a decorator if isinstance(caller, partial): return partial(decorator, caller) # otherwise assume caller is a function f = inspect.getargspec(caller)[0][0] # first arg return FunctionMaker.create( '%s(%s)' % (caller.__name__, f), 'return decorator(_call_, %s)' % f, dict(_call_=caller, decorator=decorator), undecorated=caller) ###################### deprecated functionality ######################### @decorator def deprecated(func, *args, **kw): "A decorator for deprecated functions" warnings.warn( ('Calling the deprecated function %r\n' 'Downgrade to decorator 2.3 if you want to use this functionality') % func.__name__, DeprecationWarning, stacklevel=3) return func(*args, **kw) @deprecated def getinfo(func): """ Returns an info dictionary containing: - name (the name of the function : str) - argnames (the names of the arguments : list) - defaults (the values of the default arguments : tuple) - signature (the signature : str) - doc (the docstring : str) - module (the module name : str) - dict (the function __dict__ : str) >>> def f(self, x=1, y=2, *args, **kw): pass >>> info = getinfo(f) >>> info["name"] 'f' >>> info["argnames"] ['self', 'x', 'y', 'args', 'kw'] >>> info["defaults"] (1, 2) >>> info["signature"] 'self, x, y, *args, **kw' """ assert inspect.ismethod(func) or inspect.isfunction(func) regargs, varargs, varkwargs, defaults = inspect.getargspec(func) argnames = list(regargs) if varargs: argnames.append(varargs) if varkwargs: argnames.append(varkwargs) signature = inspect.formatargspec(regargs, varargs, varkwargs, defaults, formatvalue=lambda value: "")[1:-1] return dict(name=func.__name__, argnames=argnames, signature=signature, defaults = func.func_defaults, doc=func.__doc__, module=func.__module__, dict=func.__dict__, globals=func.func_globals, closure=func.func_closure) @deprecated def update_wrapper(wrapper, model, infodict=None): "A replacement for functools.update_wrapper" infodict = infodict or getinfo(model) wrapper.__name__ = infodict['name'] wrapper.__doc__ = infodict['doc'] wrapper.__module__ = infodict['module'] wrapper.__dict__.update(infodict['dict']) wrapper.func_defaults = infodict['defaults'] wrapper.undecorated = model return wrapper @deprecated def new_wrapper(wrapper, model): """ An improvement over functools.update_wrapper. The wrapper is a generic callable object. It works by generating a copy of the wrapper with the right signature and by updating the copy, not the original. Moreovoer, 'model' can be a dictionary with keys 'name', 'doc', 'module', 'dict', 'defaults'. """ if isinstance(model, dict): infodict = model else: # assume model is a function infodict = getinfo(model) assert not '_wrapper_' in infodict["argnames"], ( '"_wrapper_" is a reserved argument name!') src = "lambda %(signature)s: _wrapper_(%(signature)s)" % infodict funcopy = eval(src, dict(_wrapper_=wrapper)) return update_wrapper(funcopy, model, infodict) springpython-1.2.0+ds/springpython/COPYRIGHT0000644000000000000000000002042511364062063017421 0ustar rootroot Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. ***************************************************************************************** Spring Python is inspired by the Spring framework (http://www.springframework.org) which is released under the Apache Foundation License. Spring Python does NOT require redistribution of Spring's software modules, nor any of its dependencies (binary or source code). However, you are free to download their source code to understand the basis of this product. The Spring Python's Security module is inspired by Acegi Security Java-based framework (http://www.acegisecurity.org), which is released under the Apache Foundation License. Spring Python's Security module does NOT require redistribution of Acegi's components, nor any of its dependencies (binary or source code). However, you are free to download their source code to understand the basis of this product. ***************************************************************************************** Spring Python has incorporated the "decorator" library written by Michele Simionato into springpython.context in order to implement a decorator based IoC container solution. See http://www.phyast.pitt.edu/~micheles/python/documentation.html for more information about downloads of this library. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in bytecode form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ***************************************************************************************** Spring Python includes software developed by Fourthought, Inc. (http://www.fourthought.com). License and copyright info for 4Suite software License and copyright info for 4Suite software Mike Olson Fourthought, Inc. Uche Ogbuji Fourthought, Inc. License and copyright info for 4Suite software 1.1 2002-01-21 MO Initial version in post-0.11.1 codebase 1.4 2005-03-03 MB Converted source to Simplified DocBook XML 1.0 4Suite copyright license 4Suite software copyright The copyright on 4Suite as a whole is owned by Fourthought, Inc. (USA). Copyright on the components of 4Suite is indicated in the source code; most files have their own notice of copyright and ownership, and a CVS datestamp to clarify the actual date of authorship or last revision/publication. For purposes of usage and redistribution, the following Apache-based license applies. The 4Suite License, Version 1.1 Copyright (c) 2000 Fourthought, Inc.. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Fourthought, Inc. (http://www.fourthought.com)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. The names "4Suite", "4Suite Server" and "Fourthought" must not be used to endorse or promote products derived from this software without prior written permission. For written permission, please contact info@fourthought.com. Products derived from this software may not be called "4Suite", nor may "4Suite" appear in their name, without prior written permission of Fourthought, Inc. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FOURTHOGHT, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================== This license is based on the Apache Software License, Version 1.1, Copyright (c) 2000 The Apache Software Foundation. All rights reserved. ***************************************************************************************** Spring Python includes software developed by Uche Ogbuji (http://uche.ogbuji.net). Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. The end-user documentation included with the redistribution, if any, must include the following acknowledgment: "This product includes software developed by Uche Ogbuji (http://uche.ogbuji.net)." Alternately, this acknowledgment may appear in the software itself, if and wherever such third-party acknowledgments normally appear. 4. The names "Amara" and "Uche Ogbuji" must not be used to endorse or promote products derived from this software without prior written permission of Uche Ogbuji (uche@ogbuji.net). 5. Products derived from this software may not be called "Amara", nor may "Amara" appear in their name, without prior written permission of Uche Ogbuji (uche.ogbuji@gmail.com). THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL FOURTHOGHT, INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ==================================================================== This license is based on the Apache Software License, Version 1.1, Copyright (c) 2000 The Apache Software Foundation. All rights reserved. springpython-1.2.0+ds/springpython/images/0000755000000000000000000000000011544154725017377 5ustar rootrootspringpython-1.2.0+ds/springpython/images/spring_python_white.png0000644000000000000000000003674511364062063024220 0ustar rootrootPNG  IHDR ZbKGD pHYs  ~tIME 7e3P IDATxy|Tw}l$DPZ * Z+nE- V+*nV-qE!.@d  {2ɬws~A#y5ܙsssssU4iҤ jI&-iҤo)Hf0m UUQE(!P/Ȗ (Jl@ )H*~ ja* Emk8oE@P^Uĩj,1T @"ވ SDDAAU|.U!PV[T EX,PtkX" ovz}g;eTj\J`Ֆ{,OKBX؊4KX4&#dDPDeFiJaz%!",mRBaxP1<"]({>lSP?@a| \\St$F埫huv=<2`lz@X1T7{J.9AY} >xm4K`S(?L-ܻV!hjL,OPX0:}~ BЉ4^r,9=z$ ֟K^牻D :3;73ƔSpœvr ' < GTN"kfDaN4O?x!]a%shT'oBMIeyxYnlz#Ba]f㮹M>*PPڄ qD"Lծ/"j*P9x@~ܮ\ .)*,L48l4E 8\栱 }!z_/^t%$URw ?;~c5?ĀN>%6yQ],3A"ig08P\_~ijp.]JD[T\L0i=v,r5QJid:dlӺʪ4An=^{$9H )2 Kb߯:.3ӧn&Oٹs?4-_QHB3nf_w~a  ի׺Y;3{T*;RIb81gq|QN9 iLtŠ%Y"3]m~Y>iϩN℩g es[YORUdQ ;zyowB=P}ŻE'MANx~,% эcDzh7:/?xcc=E`Ĉ,_{w뮻;w… #d[e Ȏ ̌([%;kmb6ؗmI8mriXTFqV_6w/@er_.Է%'N߽@шpdIf60LtI0P4ۧY oekpsxŏXN*BlFAEAljzE_XH>z}iHs=L2tPVXyС԰m۶oT)),Uߕcv&=~U-XX8A' Zȭs^,Aiyx>ant'VcѝxӱEM$ޘIӺ*){wyVf}-]N?H*.q$ 40@,PW @Uy9g>7L;wn^vAO?tnG}D$ED(.u*t: 14|ᩧ;5ԘE"Zѷ:2x+ ؙ&n}nBһ6̘l-a/gw-Aָ5u4'@|,-I?/!nZHE VՖyfDmsW]p_=8Q<ӧϏ\z, [04x$Uۥcghvꄼ]F< N2K$hӻd8Mo^{Pmθޛsǘ`*% (0fL#fc=jٞ_I3pJa6 %A\I`i)]"HL4͛hEicǎmƓO>8fC5C#h 5%@k& cMӏMZ*`c?btMF;v\=ޢ;\5\J&m0ޅLWKJS 9y-[]ރ@BC [tx~7F1k6oPVZʄѣ1rdZ,~,Ym .8O8X,8Q^I] -躹wnU6I,@$YԉĪQCG{?(_r%GL<S n1N1clY__KVd!99lHd6dNqo~{TTLMHuؓد.Z)S*/3mhrBF;֖2cڴ.X@yyy[l=gsΥI&1a$}ey9h×Y^VFyy9kKK mͧaUii[]ǎ2.X@U4BJJJvۿ\fϚ;.|Ͷf͚j4&Mp}iN$Rغ 1 ≒f?r"/@=V)ыXNZ2]4U&賣/m;=qw-26d`8IcW/;UIb)THER|`i3f#+hu{xI\vWYl?it[[P/)iwxI,*.-ϴ3v+k…rIrxI/,l˳vs5f/,l'MRJvs6cni{:͛755I)1b^s=ס?rRuy!'|CŊ yM7;L\.p8d>}䥗^*?ݿLN8Q:ϞyyGH0d޽ܹswIB ےBrW%*1y %{CSGfgq\+'-"/zj4aG2T^93- g2\Kr{CUIy֢uu> ^" .?vSO=UX-uĉ{{3إ+c]^~}ShɤeJ4$U +$"QIc9.<ыeJwHH$9iV4:,CVL)DR|-g1L=r}/袽ܹV͗tMrڴiR1 O9唽/JEQG~w~%}Ȑ!K.n_ܶmRqƵ{PA’}(HA :dKr%<Ng<%dBW_w45l f)iږ 2Lp""F)Q-CzH>{V+YI_ wQ$Unh3g3ܹ3g}6555F͝;3<3!Z},t!e˃j(HGW!w%ԯADeWPZT&u>_$$)@AJ2RQ Ŕ~9#vf8sOBZ׷?@nzG=bҿ6stps})7 ̞3Qÿ54ڶ6FJH/өSm}8jQ\lˇ;-Y3ye[≲o@yfN1e[zlKΨ`g-wކ63'xɱ~Pbcq'1@*]Wiu5^ł!I:XaH ZBIde]tfpQë垻 hDIlOb2fhf̘A&s粪NeѲeG}4G}4| o6[lalٳmO>>~0N4-/NzsePS)ƒY S/9sΌ V#)wXj;kؙUTQMnԠgQQEs+5yQ2StqѭۡTCĵ0D)F\~5"nnݩҟCZȽ4D׸,*̧h7qՄ -ni~QLBL(Oⲿ"ʔb9m"0r2~W=><ȶ?N9֭[,7zXQQѶ=vX8.l@H'kj*pEr1 0ْisXR3@TwuLC4RHi I:?XޙlS|E9tb#;xFmy( *>N>.bfOvsjV FBFڎee,?.0wXEXt)@⽆OO@[6W^ᡇШFG=\s58ζP4c?q5UP4l5Ȩ_[~uw_K!TaŨɜГAϏWh {khWc!2Т$&tDJ;'A8})ǁ;Rt/5⾹%ſ/fu"6x$AB;b;j^(1i>rWVZJ2hr&M ̟զ]^+PX|9˗.m[[ZVײUZނHN"bUW1zXϜ1rn~+Gskinn뮻8w겧a޽{s-C^z}PG}tߴ3dr'a#ؼY{'8c $~G&w%\Gf*^@"Kѓ9nbY ;2r蹡;>is=;a#l;$FʊSxY@S"qs~}:o͝O`upHFL/'rڌms#h:ߠ5 (+ё{ jgS]ͮEm>8}cƌ-$zOZ0uԩwo_e}EJ)uv_VVv@⚚咀;ɓr=Wv7n-7-[nwzRUU s9-`jڵΒ~_>{ofΜ[%KvK>}Rd,.c".L& " !P  ٳy \kWfIDAT6cQ1rd[\ ?jT[bmi) kOd,]B@]ZTꨝ6cW}7pNg'pwqÆ ;`u4iO?4;v //C\ve_"Jqꩧrwлw5IZ{˳ʕ+yx뭷عs'aзo_F͔)S(((ͣ zz@VnguQy|zDYGp5sڝ!&ECNqyC\y쎇p|^M\w4:)sΟB⸝zKc]M.vJ|"SU.gxqǚO{ Xt)K+ &6)0p{\Ջf&_CEInnGexO"qV+k0uP8,L&YE TIJUlpq&@*M'ekOdK/ФE $*tۍ2xx35M֬@SsO0~t`y?3fPee̝;[}gϞy睗V5焁f<0m&c7t e=GnX[d'uܖ@aF3Q L1dKIԡɩI*UTfS AĒqgFϛ4< ƅ3}Lj+f]Uj1bHT<\,`_:ۺp{j*̙Ìiɏ'ql/p߲|+^Q㮤`f7E$ދYY(I!hvYH9"ĝFK`(j3Ҍ"Rq6a"2tua|; DFahh */')&1m/cO$i{ŋ1bDZ IMjRXt)&iJ3_%gMé\'vgcfW YX*!#b**p2IthAAj9Dx$qX_8F&,{d) KԷ7| L(3grYm7}*؏_<ͯ;{6EEEYqn&LdԨQ~+ ,#,ē-[?+~͵8ut ^}򻹔g)P SF⒔IBw:kqed3ȋPS~$"4TW=#(LN|2&NkYyl~&]L w3팣w 2.X@cc#:4 3;4“I]Ā,;E_^9횏͵a!P.y%?CcEz10\)*O$iIѭX,zv|e%lbQШ'GO%TeW 8?Qct·|f-M;B􎭖67ҤQD42ERѱ,.4!Cww.3ca3YͶ>YR|:m+>kpv+`񫯒 ;V!#1,F#4R|/6pX[я7($Ҥ9xض$\@A@"q%$*a~i8`*ԒX)a):j`t4MC6&IZ&.HS?hSLꡱl 7}ofCv36Nf4eE6X65SE  =}ӤOOBTRT4)4$te*N,MAJ0!)HUCb`$9!p!73N_ef8 e`),l)k(kfwR x2gZ$ҤO4­hDA A)ͅR P!fزsνlC˿%N%lGP*(EJsE.'OZ\F8~q;gvf+~A~=&():IM~]~oLF"&B.Rdʆ&X lo^⎇gir|qF<3OU8lCTK`4K1 w*K>[y_KA cU/o ?BIm8ցfZI h g!r/g(0"&"*PDGW^G_r-0$՘F MC!UlUEa+*nD-`G2B]sN7 Iѧ?rcTCD&v5MZI4e1P,$6jr46\DIN5_ŭlD[Mx*>£St12-8ID!r!_`UIUw :5}Ӥ?T['@JؤVD]F &uJu 'SuRɱ<)rB9 ޘ %H¨wWd[>|…PSN6:I~4V}ELO#NEX99pEemڧP.q=Ll&QWۋC"7EuUѧG1PZG&jfɌy\-1q%hCHCnnI( RBJPuF#=NG}<%P<. Ԭ};^n=Ygszr1 o؄g3|0TI =I!m]ENa7PMpܞ W08b&~w"&bX*v_㍚.╒@n4ez"8\y̚ϗcR76Q_eCh8q_Ehf- !%=|;T:4h & c* F<_BexR&?OF&IN94xè!4iO")I`XUFҫ;6{#QCB83CInnFd$o-<R) ĎO\J3`=)$XP@őRYpdQekxDR53v =vSPܙ$Ҥ9A ͖hΐ#QdEda_]zl0N4['ZtA|s7cm'5䌑?c+8b̑Te8ΊwfqS5tcs?"hJ5}Z3IUÌ$k6;TcFHSsIs FA tNxMY*w !EF D *I:okQBChuj=PѕtD4T  tt Xb֯,e^!zqH}F ; :)+0Uz6K9r?eS |M4ĉ')/J"FL6ud5:Xl\[CFM;ʹ.97diw *IIs+ZfHl?g =2F^*L~?@Z@qd"nGD*BK60ȰT2VHS{rl1f Sɠ{co=*z,Ư 7T3veD5eуCO]Cn4'$Ҥ90qx04֖fkEيJL8 DDy61U5;Ɋ; .3,@M"mR Lŏ^R`3ٟD'oΧ%a M:&P݁rN;5K]6-aO{i'av, 00k| n^{!cFRhQ6n&ER߉/7uI/f>W ^IȮgos1mV-lA:wXG oGx\\pG7Ҥ9hv){sgq%#ep s\[Y 6⌸16!U9dcuT7mǑR$2׸w-s‚oHqn8z *@jb5 1'+OU`5ӋI<͖{}7aX$ S5I$"䝕or|/</vlOH־e`AΝ~D(NB ׅsjdCF'I|4k{c#C@}–MX RoO"M]{ RHX%p* "@q*&,`jti1Bdf۹dz18>g>fZh ]$a Յ;.%glㄧ%H r9PeNvB2354= 4II軚{NۥImH*!h0P)yWW R' ,tc D#m$IbMŧk+(wI)7|~0 jğG&M/!P4 ej*--QsMVUZ׌4e wY_@2 Oyso. Ʊ HϢ$p)D(HIaPU)dEm}5E J˃{dksFQU+컩j؂ajD$ou.5+xRf sKN#a2'&!GE1 ]*RHhҢ ((hD84g s&b՚%Mulp3[?ۊC z+.dte4)%1'cG EmyRh]I?J!ZT ѱֲIdDSz&G0p)16IͦPcʕXV+_{bUqg9fK$&fh* AeH)>]!^".֨c8SSBi43g̙s>{8/#Iyݰ_Z5;@ծд=s< Y 6hn]~;^  bxSV6=Z K,~8ŋAHIgS go MkՎ (jkC`ݝTq&* ? O/PN¡ő!L,YJ'IS!H)QB"1vyMд:vܾ!DmPZ7Ґ@DH njLPn'i5gQ"Qj͌D+]49 MtHh6"IENDB`springpython-1.2.0+ds/springpython/images/coily.jpg0000644000000000000000000001762611364062063021225 0ustar rootrootJFIFExifMM*C  !"$"$Cb" 9!"1AQa2q#B3Rc4!1AQaq"23R ?#:14i;/\<vڲ.;՘SvI#zq 4(!%G8N4ΪF߾u7bG Tq5urNWnςCZ[,1榾$ܥ)R]@%D\A֫zPZ$ ~muyo^SqfITHC*u$]($7E77hcީ,qpISڒ0KZ]p I~4 QF*ThѣJ4hҥ^GzXʯg}C[kRTSI`%e:W  g틩M*A)YiZI+W &%cuYUa1T~)hHTSÜ EVtPIܣ;eiD4jUaqqRWi RTΞRG, +Q)2YZ&x0 5,7ѴUiZ.P-;** s>*@;WI3CMwu̝ :9ͽ#C!z:xx`$ ycG c d rǝ7?KgMA !y ћ BE8`0|{Gyȵ$j_,2U1Eؗyw>ւT҄Q_|#;{PLh ˟)04O j)^gX5*kƔc+u<Ԫ$+ $x*{ GGLU†oYOYK2x$?!GDk@3[4hөhѣJp/;xgt,#BM6X7Wޕu2V\j5S]Y> c!v:}ʹzG-$̫"$~l}Q}V[uYdg+U gPT*;:<@z7vb-AGL{giGi`X[p; ksZ_ƕ eDX]FSR[}b5VHr\3c,:]+)\qĺ(?:[D?T;r)c2І`JE˘(ŲđԨ x-=0N(F`?ckWrhm4Y]v4ae,\$cMm58ho.`$F ȱp$}]e7/C\$g#E8.DŽt38jEoX/ CtqՍIRqhXԻImKetRLSuPqR̤W=M䡘IRW{'r}CJBnRP'<#Xʤۋm[PRE 8pIӨpI ;ڪ`Xյmߝc{S~AZjuEm KN̠u9uʰSOzOsku4V<{t9#7&$8sr(dIbIb`A^ e]e =+>4ꞍT߮Gcyuiq䧭 Q BȹrOlJy`xp\~I;|M:W;⬸:vU t `gF XF60r:Y-m,,QT*y@2y *1km=dOn߭HP"ĎG8I$֦#jYwβZڻN֋MVf f9f۷'ɟM;s)—8*N`u\G CT z`d`9?#ս,[R.V mGVC6sTI j+mIm~~յt۶/4 vFc`1⠜St-ݾJ]DFXOYe(MJ|ι֡- {k:CrTD5(@ѩ{!FB;,Rrn_;DŽux5N֊tRrL'H]S=E:SU 0ԁx&: |A$=~;nC2.*ILyM^RRg^q֝4hժ5Q&okbV dQ)h>q:;%wY' ^ZHI (e*g̶=yxpph^I+D)|#X?:cId+d(IdTᗬf"lk:m-kr$;ƍ1Abܮ ]<$fH)& ddzQP=/qmWUmN!HjCdR{H /'N:[ W*innsHeԻg_(JO'I˩43) I.@f\JdW'ҹX<S4{U|]OO*Xuw9GV [OǽFǡ )딄MIb ߾3t=ûMnڕЋrr,RV"\@(`oؕՋ7sV?Tx1ǔU) WSEiJ+u,tģs>ܓg`nyJax*U]zVOf,9TDC$I$qP J@  wc46٫RD`نv{ʏbW zə!.GC?Ɨ{s]I⦧RѳJVq:iwn3 øn]@P Ğ(4 i}>4>Wben~eGnm, xfyB}%<[pEO"ZSoT5G S $i%s7@N|LOxilNfup54 UytX;jI3VmJh(Fp#?&l{{bnZ*jx{j݌g޲8ڝ|L>L&UTٷ D+C2R$Qa8*?YuNݯ?z]гFrAͫo]'n:#5߇ojhEYM3m6xq8X/RGGYE54HUxK7yGI!F~{&]gUwPUH̜ev$1\kh^J|J+kHq< 'ꨍH~ZIHE?ۗ/Ͳ5Q7IleJZe0TTxq>;l-OG <ԓ}2~mE:o7&TStj8,r+sS(?=})ի}I_mp04ԖJPrY]i' P%hՄj\r@`23bϻEMznQm>$VP)h d8u:`r]Qmehk%Br㐏\}4hl_}Txyss׽T/ 8\?m8yCoecDӶG`OAĬ@We0I*#;UQI3O<?$먮)79cbV,H 6rHps=)z;zpSwxa9p:qgi /L[(PN+",kŁQd zlC;fox>Q%K"23Ew㫽j8UheR+.c!C|p$kӝpxCi\"H`)Xg[f-)9:WgOѣF*ͻ4iJ{ǵmESQ$U7З8o"8pGJr/#n&u%e.HTYզ{m[.4rKOU Xt="e&ڬ =4JpsPG9{"0uӍfqk=3mq֢aIg(_qqm-; i$dѺd0tg~#>Nx>0ldiȈQNpHt F I9.ڞEe$LI'UQ4~D};qQ\,TʁG,ULd{$>մՕH-bcI$,BDgr? ]z/:Ɍ[ GEBLAzY˅#<~ԭl)|/[6R\ϧOޕfsj]'7N**@$Fb`{Dvw(nPEQWA4\*t*P0X(Pp>l=6= Qm{]Ue(n?U-X$ČJF^CC2#`GT՛EA9:& RLnοtwߦVO/ҕjbAP>{ deSxbT-uڨic@dY(.*uY6vۗ*s%dԼY 蠷S-2y$I=HŲ`HQ)$^6HZ0;ѥ,{wt-EE-3SK||2*aޙK@$DYԵ,׬Fp}mr\w HZYU۾4j V);TW@sX H@I5 !,gwl%u,KS{-Oj#gH-@,Đ$jx *ks5EUC4̻3}z$ I+j+U{5n)c+2B)$8$*5oR뱕yM5Qh@XRX2?o?5^'!(>qμKv㬊nHn EOG'4mOV@zb#$]Omh6_lj.(H87,qxf9ɾA{܍d%@Gs=b?}_i$DVѻVV]4jZF4W2jmV,UTUq43 ua?Oio !TnmYlI=)\Ve*&2ׇTYA_y!c*,'q#|oZ4kuq-=Lьny8R?GA[GI⭮P;7\Owd,O,Wtض6Ks2R'䷢\ej(TQ?T^ 9rSnۖ 5g3/ K ῕W!y1!@μ捵LOTh^W9FpprE?L 'tkhCZmnJ#-%QB> O0tlpJ_<CJL{L}8Ս嚪eث(-L5?.erH|d03>PG-U-zTd \8c鰠0^Ym! on i0:WF>ѣF*Uspringpython-1.2.0+ds/springpython/aop/0000755000000000000000000000000011544154725016711 5ustar rootrootspringpython-1.2.0+ds/springpython/aop/__init__.py0000644000000000000000000002624011464332043021016 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import copy import logging import re import types from springpython.aop import utils class Pointcut(object): """Interface defining where to apply an aspect.""" def class_filter(self): raise NotImplementedError() def method_matcher(self): raise NotImplementedError() class MethodMatcher(object): """Interface defining how to apply aspects based on methods.""" def matches_method_and_target(self, method, targetClass, args): raise NotImplementedError() class MethodInterceptor(object): """Interface defining "around" advice.""" def invoke(self, invocation): raise NotImplementedError() class MethodInvocation(object): """Encapsulation of invoking a method on a proxied service. It iterates throgh the list of interceptors by using a generator.""" def __init__(self, instance, method_name, args, kwargs, interceptors): self.instance = instance self.method_name = method_name self.args = args self.kwargs = kwargs self.intercept_stack = copy.copy(interceptors) self.intercept_stack.append(FinalInterceptor()) self.logger = logging.getLogger("springpython.aop.MethodInvocation") def getInterceptor(self): """This is a generator to proceed through the stack of interceptors. By using generator convention, code may proceed in a nested fashion, versus a for-loop which would act in a chained fashion.""" for interceptor in self.intercept_stack: yield interceptor def proceed(self): """This is the method every interceptor should call in order to continue down the chain of interceptors.""" interceptor = self.iterator.next() self.logger.debug("Calling %s.%s(%s, %s)" % (interceptor.__class__.__name__, self.method_name, self.args, self.kwargs)) return interceptor.invoke(self) def __getattr__(self, name): """This only deals with method invocations. Attributes are dealt with by the AopProxy, and don't every reach this block of code.""" self.iterator = self.getInterceptor() self.method_name = name return self def __call__ (self, *args, **kwargs): """This method converts this from being a stored object into a callable class. This is effectively like a metaclass that dispatches calls to proceed through a stack of interceptors.""" self.args = args self.kwargs = kwargs return self.proceed() def dump_interceptors(self, level = logging.INFO): """DEBUG: Method used to dump the stack of interceptors in order of execution.""" for interceptor in self.intercept_stack: self.logger.log(level, "Interceptor stack: %s" % interceptor.__class__.__name__) class RegexpMethodPointcutAdvisor(Pointcut, MethodMatcher, MethodInterceptor): """ This is a combination PointCut/MethodMatcher/MethodInterceptor. It allows associating one or more defined advices with a set of regular expression patterns. """ def __init__(self, advice = None, patterns = None): Pointcut.__init__(self) MethodMatcher.__init__(self) self.advice = advice if not patterns: self.patterns = [] else: self.patterns = patterns self.logger = logging.getLogger("springpython.aop.RegexpMethodPointcut") def init_patterns(self): """Precompile the regular expression pattern matcher list.""" self.compiled_patterns = {} for pattern in self.patterns: self.compiled_patterns[pattern] = re.compile(pattern) def matches_method_and_target(self, method, target_class, args): """Iterate through all patterns, checking for a match. Calls the pattern matcher against "class.method_name".""" for pointcut_pattern in self.patterns: if (self.matches_pattern(target_class + "." + method, pointcut_pattern)): return True return False def matches_pattern(self, method_name, pointcut_pattern): """Uses a pre-built dictionary of regular expression patterns to check for a matcch.""" if self.compiled_patterns[pointcut_pattern].match(method_name): matched = True else: matched = False self.logger.debug("Candidate is [%s]; pattern is [%s]; matched=%s" % (method_name, pointcut_pattern, matched)) return matched def invoke(self, invocation): """Compares "class.method" against regular expression pattern and if it passes, it will pass through to the chain of interceptors. Otherwise, bypass interceptors and invoke class method directly.""" className = invocation.instance.__class__.__name__ if self.matches_method_and_target(invocation.method_name, className, invocation.args): # This constant is not class level, because it is a hack solution, and should only be used # used here, and not known outside the scope of this block of code. --G.Turnquist (9/22/2008) ASSUME_THIS_ADVISOR_WAS_FIRST = 1 invocation.intercept_stack[ASSUME_THIS_ADVISOR_WAS_FIRST:ASSUME_THIS_ADVISOR_WAS_FIRST] = self.advice self.logger.debug("We have a match, passing through to the advice.") invocation.dump_interceptors(logging.DEBUG) return invocation.proceed() else: self.logger.debug("No match, bypassing advice, going straight to targetClass.") return getattr(invocation.instance, invocation.method_name)(*invocation.args, **invocation.kwargs) def __setattr__(self, name, value): """If "advice", make sure it is a list. Anything else, pass through to simple assignment. Also, if "patterns", initialize the regular expression parsers. """ if name == "advice" and type(value) != list: self.__dict__[name] = [value] else: self.__dict__[name] = value if name == "patterns": self.init_patterns() class FinalInterceptor(MethodInterceptor): """ Final interceptor is always at the bottom of interceptor stack. It executes the actual target method on the instance. """ def __init__(self): MethodInterceptor.__init__(self) self.logger = logging.getLogger("springpython.aop.FinalInterceptor") def invoke(self, invocation): return getattr(invocation.instance, invocation.method_name)(*invocation.args, **invocation.kwargs) class AopProxy(object): """AopProxy acts like the target object by dispatching all method calls to the target through a MethodInvocation. The MethodInvocation object actually deals with potential "around" advice, referred to as interceptors. Attribute lookups are not intercepted, but instead fetched from the actual target object.""" def __init__(self, target, interceptors): self.target = target if type(interceptors) == list: self.interceptors = interceptors else: self.interceptors = [interceptors] self.logger = logging.getLogger("springpython.aop.AopProxy") def __getattr__(self, name): """If any of the parameters are local objects, they are immediately retrieved. Callables cause the dispatch method to be return, which forwards callables through the interceptor stack. Target attributes are retrieved directly from the target object.""" if name in ["target", "interceptors", "method_name"]: return self.__dict__[name] else: attr = getattr(self.target, name) if not callable(attr): return attr def dispatch(*args, **kwargs): """This method is returned to the caller emulating the function call being sent to the target object. This services as a proxying agent for the target object.""" invocation = MethodInvocation(self.target, name, args, kwargs, self.interceptors) ############################################################################## # NOTE: # getattr(invocation, name) doesn't work here, because __str__ will print # the MethodInvocation's string, instead of trigger the interceptor stack. ############################################################################## return invocation.__getattr__(name)(*args, **kwargs) return dispatch class ProxyFactory(object): """This object helps to build AopProxy objects programmatically. It allows configuring advice and target objects. Then it will produce an AopProxy when needed. To use similar behavior in an IoC environment, see ProxyFactoryObject.""" def __init__(self, target = None, interceptors = None): self.logger = logging.getLogger("springpython.aop.ProxyFactory") self.target = target if not interceptors: self.interceptors = [] elif type(interceptors) == list: self.interceptors = interceptors else: self.interceptors = [interceptors] def getProxy(self): """Generate an AopProxy given the current target and list of interceptors. Any changes to the factory after proxy creation do NOT propagate to the proxies.""" return AopProxy(self.target, self.interceptors) def __setattr__(self, name, value): if name == "target" and type(value) == types.StringType: value = utils.getClass(value)() elif name == "interceptors" and not isinstance(value, list): value = [value] self.__dict__[name] = value class ProxyFactoryObject(ProxyFactory, AopProxy): """This class acts as both a ProxyFactory to build and an AopProxy. It makes itself look like the target object. Any changes to the target and list of interceptors is immediately seen when using this as a proxy.""" def __init__(self, target = None, interceptors = None): ProxyFactory.__init__(self, target, interceptors) self.logger = logging.getLogger("springpython.aop.ProxyFactoryObject") def __str__(self): return self.__getattr__("__str__")() class PerformanceMonitorInterceptor(MethodInterceptor): def __init__(self, prefix = None, level = logging.DEBUG): self.prefix = prefix self.level = level self.logger = logging.getLogger("springpython.aop") def invoke(self, invocation): self.logger.log(self.level, "%s BEGIN" % (self.prefix)) timing.start() results = invocation.proceed() timing.finish() self.logger.log(self.level, "%s END => %s" % (self.prefix, timing.milli()/1000.0)) return results springpython-1.2.0+ds/springpython/aop/utils.py0000644000000000000000000000212411464332043020412 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import sys def getModuleAndClassName (classpath): '''Splits classpath to modulepath and classname.''' parts = classpath.split(".") className = parts.pop() moduleName = ".".join(parts) return moduleName, className def getClass (classpath): '''Returns an instance of a class.''' moduleName, className = getModuleAndClassName(classpath) # Split the class path __import__(moduleName) klass = getattr(sys.modules[moduleName], className) return klassspringpython-1.2.0+ds/springpython/context/0000755000000000000000000000000011544154725017616 5ustar rootrootspringpython-1.2.0+ds/springpython/context/__init__.py0000644000000000000000000001470411464332043021725 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import atexit import logging from traceback import format_exc from springpython.container import ObjectContainer class ApplicationContext(ObjectContainer): """ ApplicationContext IS a ObjectContainer. It also has the ability to define the lifecycle of objects. """ def __init__(self, config = None): super(ApplicationContext, self).__init__(config) atexit.register(self.shutdown_hook) self.logger = logging.getLogger("springpython.context.ApplicationContext") self.classnames_to_avoid = set(["PyroProxyFactory", "ProxyFactoryObject", "Pyro4ProxyFactory", "Pyro4FactoryObject"]) for object_def in self.object_defs.values(): self._apply(object_def) for configuration in self.configs: self._apply(configuration) for object_def in self.object_defs.values(): if not object_def.lazy_init and object_def.id not in self.objects: self.logger.debug("Eagerly fetching %s" % object_def.id) self.get_object(object_def.id, ignore_abstract=True) post_processors = [object for object in self.objects.values() if isinstance(object, ObjectPostProcessor)] for obj_name, obj in self.objects.iteritems(): if not isinstance(obj, ObjectPostProcessor): for post_processor in post_processors: self.objects[obj_name] = post_processor.post_process_before_initialization(obj, obj_name) for object in self.objects.values(): self._apply(object) for obj_name, obj in self.objects.iteritems(): if not isinstance(obj, ObjectPostProcessor): for post_processor in post_processors: self.objects[obj_name] = post_processor.post_process_after_initialization(obj, obj_name) def _apply(self, obj): if not (obj.__class__.__name__ in self.classnames_to_avoid): if hasattr(obj, "after_properties_set"): obj.after_properties_set() #if hasattr(obj, "post_process_after_initialization"): # obj.post_process_after_initialization(self) if hasattr(obj, "set_app_context"): obj.set_app_context(self) def get_objects_by_type(self, type_, include_type=True): """ Returns all objects which are instances of a given type. If include_type is False then only instances of the type's subclasses will be returned. """ result = {} for obj_name, obj in self.objects.iteritems(): if isinstance(obj, type_): if include_type == False and type(obj) is type_: continue result[obj_name] = obj return result def shutdown_hook(self): self.logger.debug("Invoking the destroy_method on registered objects") for obj_name, obj in self.objects.iteritems(): if isinstance(obj, DisposableObject): try: if hasattr(obj, "destroy_method"): destroy_method_name = getattr(obj, "destroy_method") else: destroy_method_name = "destroy" destroy_method = getattr(obj, destroy_method_name) except Exception, e: self.logger.error("Could not destroy object '%s', exception '%s'" % (obj_name, format_exc())) else: if callable(destroy_method): try: self.logger.debug("About to destroy object '%s'" % obj_name) destroy_method() self.logger.debug("Successfully destroyed object '%s'" % obj_name) except Exception, e: self.logger.error("Could not destroy object '%s', exception '%s'" % (obj_name, format_exc())) else: self.logger.error("Could not destroy object '%s', " \ "the 'destroy_method' attribute it defines is not callable, " \ "its type is '%r', value is '%r'" % (obj_name, type(destroy_method), destroy_method)) self.logger.debug("Successfully invoked the destroy_method on registered objects") class InitializingObject(object): """This allows definition of a method which is invoked by the container after an object has had all properties set.""" def after_properties_set(self): pass class ObjectPostProcessor(object): def post_process_before_initialization(self, obj, obj_name): return obj def post_process_after_initialization(self, obj, obj_name): return obj class ApplicationContextAware(object): def __init__(self): self.app_context = None def set_app_context(self, app_context): self.app_context = app_context class ObjectNameAutoProxyCreator(ApplicationContextAware, ObjectPostProcessor): """ This object will iterate over a list of objects, and automatically apply a list of advisors to every callable method. This is useful when default advice needs to be applied widely with minimal configuration. """ def __init__(self, objectNames = [], interceptorNames = []): super(ObjectNameAutoProxyCreator, self).__init__() self.objectNames = objectNames self.interceptorNames = interceptorNames class DisposableObject(object): """ This allows definition of a method which is invoked when the container's shutting down to release the resources held by an object. """ def destroy(self): raise NotImplementedError("Should be overridden by subclasses") springpython-1.2.0+ds/springpython/context/scope.py0000644000000000000000000000177411412453030021273 0ustar rootroot""" Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ PROTOTYPE = "scope.PROTOTYPE" SINGLETON = "scope.SINGLETON" def convert(scope_str): "This function converts the string-version of scope into the internal, enumerated version." if scope_str == "prototype": return PROTOTYPE elif scope_str == "singleton": return SINGLETON else: raise Exception("Can not handle scope %s" % s) springpython-1.2.0+ds/PKG-INFO0000644000000000000000000000167211526401330014454 0ustar rootrootMetadata-Version: 1.0 Name: springpython Version: 1.2.0.FINAL Summary: Spring Python Home-page: http://springpython.webfactional.com Author: Greg L. Turnquist Author-email: greg.turnquist at springsource dot com License: Apache Software License (http://www.apache.org/licenses/LICENSE-2.0) Download-URL: http://s3.amazonaws.com/dist.springframework.org/release/EXTPY/springpython-1.2.0.FINAL.tar.gz Description: Spring Python is an offshoot of the Java-based SpringFramework, targeted for Python. Spring provides many useful features, and I wanted those same features available when working with Python. Platform: Python >= 2.6 Classifier: License :: OSI Approved :: Apache Software License Classifier: Intended Audience :: Developers Classifier: Development Status :: 5 - Production/Stable Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Programming Language :: Python Classifier: Operating System :: OS Independent springpython-1.2.0+ds/springpython.egg-info/0000755000000000000000000000000011526401330017607 5ustar rootrootspringpython-1.2.0+ds/springpython.egg-info/SOURCES.txt0000644000000000000000000000360511526401330021477 0ustar rootrootMANIFEST.in README.txt setup.py plugins/coily springpython/COPYRIGHT springpython/LICENSE.txt springpython/__init__.py springpython/util.py springpython.egg-info/PKG-INFO springpython.egg-info/SOURCES.txt springpython.egg-info/dependency_links.txt springpython.egg-info/top_level.txt springpython/aop/__init__.py springpython/aop/utils.py springpython/config/__init__.py springpython/config/_config_base.py springpython/config/_python_config.py springpython/config/_xml_config.py springpython/config/_yaml_config.py springpython/config/decorator.py springpython/container/__init__.py springpython/context/__init__.py springpython/context/scope.py springpython/database/__init__.py springpython/database/core.py springpython/database/factory.py springpython/database/transaction.py springpython/factory/__init__.py springpython/images/coily.jpg springpython/images/spring_python_white.png springpython/jms/__init__.py springpython/jms/core.py springpython/jms/factory.py springpython/jms/listener.py springpython/remoting/__init__.py springpython/remoting/http.py springpython/remoting/xmlrpc.py springpython/remoting/hessian/__init__.py springpython/remoting/hessian/hessianlib.py springpython/remoting/pyro/Pyro4DaemonHolder.py springpython/remoting/pyro/PyroDaemonHolder.py springpython/remoting/pyro/__init__.py springpython/security/__init__.py springpython/security/cherrypy3.py springpython/security/intercept.py springpython/security/vote.py springpython/security/web.py springpython/security/context/SecurityContextHolder.py springpython/security/context/__init__.py springpython/security/providers/Ldap.py springpython/security/providers/_Ldap_cpython.py springpython/security/providers/_Ldap_jython.py springpython/security/providers/__init__.py springpython/security/providers/dao.py springpython/security/providers/encoding.py springpython/security/userdetails/__init__.py springpython/security/userdetails/dao.pyspringpython-1.2.0+ds/springpython.egg-info/PKG-INFO0000644000000000000000000000167211526401330020712 0ustar rootrootMetadata-Version: 1.0 Name: springpython Version: 1.2.0.FINAL Summary: Spring Python Home-page: http://springpython.webfactional.com Author: Greg L. Turnquist Author-email: greg.turnquist at springsource dot com License: Apache Software License (http://www.apache.org/licenses/LICENSE-2.0) Download-URL: http://s3.amazonaws.com/dist.springframework.org/release/EXTPY/springpython-1.2.0.FINAL.tar.gz Description: Spring Python is an offshoot of the Java-based SpringFramework, targeted for Python. Spring provides many useful features, and I wanted those same features available when working with Python. Platform: Python >= 2.6 Classifier: License :: OSI Approved :: Apache Software License Classifier: Intended Audience :: Developers Classifier: Development Status :: 5 - Production/Stable Classifier: Topic :: Software Development :: Libraries :: Python Modules Classifier: Programming Language :: Python Classifier: Operating System :: OS Independent springpython-1.2.0+ds/springpython.egg-info/dependency_links.txt0000644000000000000000000000000111526401330023655 0ustar rootroot springpython-1.2.0+ds/springpython.egg-info/top_level.txt0000644000000000000000000000001511526401330022335 0ustar rootrootspringpython springpython-1.2.0+ds/plugins/0000755000000000000000000000000011544154725015047 5ustar rootrootspringpython-1.2.0+ds/plugins/coily0000755000000000000000000002356411526401326016116 0ustar rootroot#!/usr/bin/env python """ Copyright 2006-2009 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ __version__ = "1.2.0.FINAL" import os import re import sys import tarfile import time import getopt import shutil import urllib from datetime import datetime ############################################################################ # Get external properties and load into a dictionary. NOTE: These properties # files mimic Java props files. ############################################################################ p = {} p["plugindir"] = os.path.expanduser("~") + "/.springpython" sys.path.append(p["plugindir"]) if not os.path.exists(p["plugindir"]): os.makedirs(p["plugindir"]) ############################################################################ # Statically defined functions. ############################################################################ def plugin_path(name): return p["plugindir"] + "/%s" % name def list_installed_plugins(): print "Looking for plugins in %s..." % p["plugindir"] if not os.path.exists(p["plugindir"]): os.makedirs(p["plugindir"]) print "Currently installed plugins:" for plugin in os.listdir(p["plugindir"]): try: if hasattr(p[plugin], "create"): print "\t--" + plugin + " [name]"+ "\t"*(3-len(plugin)/8) + p[plugin].__description__ else: print "\t--" + plugin + "\t"*(4-len(plugin)/8) + p[plugin].__description__ except KeyError: print "\t%s is not valid plugin" % plugin except AttributeError: print "\t%s is not a valid plugin" % plugin def list_available_plugins(): modules = get_modules_from_s3() print "S3 from SpringSource has..." for key in modules: for rev in modules[key]: if __version__ in rev[1]: print "\t%s\t%s" % (key, rev[1]) def install_plugin(name): """Go through a series of options to look for Spring Python modules to download.""" if not os.path.exists(plugin_path(name)): print "Installing plugin %s to %s" % (name, p["plugindir"]) if fetch_from_s3(name): print "Found %s online. Installing." % name return if fetch_locally(name): print "Found %s locally. Installing." % name return print "Couldn't find %s anywhere!" % name else: print "%s is already installed. We do NOT support automated upgrades." % name def get_modules_from_s3(): """Read the S3 site for information about existing modules.""" info = "" for bucket in ["milestone", "release"]: url = "http://s3browse.springsource.com/browse/dist.springframework.org/%s/EXTPY" % bucket info += urllib.urlopen(url).read() #print info #return [] #choicesR = re.compile('(.*?).*?(.*?).*?(.*?)', re.DOTALL) choicesR = re.compile(r'(.*?)', re.DOTALL) match = choicesR.findall(info) converted = [] for item in match: #print "Checking out %s" % str(item) if "springpython-plugin" not in item[0]: continue if item[0].endswith(".sha1"): continue #print "Parsing %s" % item[0] converted.append(item) #t = time.strptime(item[3], "%Y-%m-%d %H:%M") #d = datetime(year=t.tm_year, month=t.tm_mon, day=t.tm_mday, hour=t.tm_hour, minute=t.tm_min) #converted.append(("http://s3.amazonaws.com/" + item[0], item[1], item[2]+"K", item[3], d)) modules = {} baseR = re.compile("springpython-plugin-([a-zA-Z-]*)[-.]([0-9a-zA-Z]+.*).tar.gz") for item in converted: try: modules[baseR.match(item[1]).group(1)].append(item) except KeyError: modules[baseR.match(item[1]).group(1)] = [item] return modules def fetch_from_s3(name): """Download a selected item""" print "Can we find %s online? " % name selected = None try: s3 = get_modules_from_s3() print "Looking at %s" % s3 print "Ah ha! I can see %s" % str(s3[name]) for item in s3[name]: print "Is %s the version we want?" % str(item[1]) if __version__ in item[1]: selected = item except KeyError, e: print "KeyError! %s" % e pass if selected is None: print "Couldn't find %s online!" % name return None else: print "Fetching %s as %s" % (selected[0], selected[1]) urllib.urlretrieve(selected[0], selected[1]) t = tarfile.open(selected[1], "r:gz") top = t.getmembers()[0] if os.path.exists(name): shutil.rmtree(name) for member in t.getmembers(): t.extract(member) fetch_locally(name) shutil.rmtree(name) os.remove(selected[1]) return True def fetch_locally(name): """Scan the local directory for Spring Python modules to install.""" try: shutil.copytree(name, plugin_path(name)) return True except OSError: print "Couldn't find %s locally!" % name return None def uninstall_plugin(name): if os.path.exists(plugin_path(name)): print "Uninstalling plugin %s from %s" % (name, p["plugindir"]) shutil.rmtree(plugin_path(name)) else: print "Plugin %s is NOT installed." % name ############################################################################ # Print out a listing of existing commands, including hooks to installed # plugins. ############################################################################ def usage(): """This tool helps you create Spring Python apps, install plug-ins, and much more.""" print print "Usage: coily [command]" print print "\t--help\t\t\t\tprint this help message" print "\t--list-installed-plugins\tlist currently installed plugins" print "\t--list-available-plugins\tlist plugins available for download" print "\t--install-plugin [name]\t\tinstall coily plugin" print "\t--uninstall-plugin [name]\tuninstall coily plugin" print "\t--reinstall-plugin [name]\treinstall coily plugin" for plugin in p["plugins"]: try: if hasattr(p[plugin], "create"): print "\t--" + plugin + " [name]"+ "\t"*(3-len(plugin)/8) + p[plugin].__description__ else: print "\t--" + plugin + "\t"*(4-len(plugin)/8) + p[plugin].__description__ except AttributeError, e: print "\t%s/%s is not a valid plugin => %s" % (p["plugindir"], plugin, e) print print "Coily v%s - the command-line management tool for Spring Python, http://springpython.webfactional.com" % __version__ print "===============================================================================" print "Copyright 2006-2009 SpringSource (http://springsource.com), All Rights Reserved" print "Licensed under the Apache License, Version 2.0" print ############################################################################ # Read the command-line, and assemble commands. Any invalid command, print # usage info, and EXIT. ############################################################################ p["plugins"] = [] import_errors = False for plugin in os.listdir(p["plugindir"]): try: p[plugin] = __import__(plugin) p["plugins"].append(plugin) except ImportError: #print "\tWARNING: %s is not a python package, making it an invalid plugin." % plugin #import_errors = True pass if import_errors: print try: cmds = ["help", "list-installed-plugins", "list-available-plugins", "install-plugin=", "uninstall-plugin=", "reinstall-plugin="] cmds.extend([plugin + "=" for plugin in p["plugins"] if hasattr(p[plugin], "create")]) cmds.extend([plugin for plugin in p["plugins"] if not hasattr(p[plugin], "create")]) optlist, args = getopt.getopt(sys.argv[1:], "h", cmds) except getopt.GetoptError: # print help information and exit: print "Invalid command found in %s" % sys.argv usage() sys.exit(2) # Check for help requests, which cause all other options to be ignored. Help can offer version info, which is # why it comes as the second check for option in optlist: if option[0] in ("--help", "-h"): usage() sys.exit(1) ############################################################################ # Main commands. Skim the options, and run each command as its found. # Commands are run in the order found ON THE COMMAND LINE. ############################################################################ # Parse the arguments, in order for option in optlist: if option[0] in ("--list-installed-plugins"): list_installed_plugins() if option[0] in ("--list-available-plugins"): list_available_plugins() if option[0] in ("--install-plugin"): install_plugin(option[1]) if option[0] in ("--uninstall-plugin"): uninstall_plugin(option[1]) if option[0] in ("--reinstall-plugin"): uninstall_plugin(option[1]) install_plugin(option[1]) cmd = option[0][2:] # Command name with "--" stripped out if cmd in p["plugins"]: try: p[cmd].create(p["plugindir"] + "/" + cmd, option[1]) except AttributeError, e: print e p[cmd].apply(p["plugindir"] + "/" + cmd) springpython-1.2.0+ds/setup.py0000644000000000000000000000531611526401326015075 0ustar rootroot#!/usr/bin/env python """ Copyright 2006-2008 SpringSource (http://springsource.com), All Rights Reserved Licensed 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. """ import re import sys try: from distribute.core import setup except ImportError: from setuptools import setup if sys.version_info < (2, 6): print "Spring Python only supports Python 2.6 and higher" sys.exit(1) setup(name='springpython', version='1.2.0.FINAL', description='Spring Python', long_description='Spring Python is an offshoot of the Java-based SpringFramework, targeted for Python. Spring provides many useful features, and I wanted those same features available when working with Python.', author='Greg L. Turnquist', author_email='greg.turnquist at springsource dot com', url='http://springpython.webfactional.com', platforms = ["Python >= 2.6"], license='Apache Software License (http://www.apache.org/licenses/LICENSE-2.0)', scripts=['plugins/coily'], packages=['springpython', 'springpython.aop', 'springpython.jms', 'springpython.config', 'springpython.container', 'springpython.context', 'springpython.database', 'springpython.factory', 'springpython.remoting', 'springpython.remoting.hessian', 'springpython.remoting.pyro', 'springpython.security', 'springpython.security.context', 'springpython.security.providers', 'springpython.security.userdetails' ], package_data={'springpython': ["README", "COPYRIGHT", "LICENSE.txt"]}, download_url="http://s3.amazonaws.com/dist.springframework.org/release/EXTPY/springpython-1.2.0.FINAL.tar.gz", classifiers=["License :: OSI Approved :: Apache Software License", "Intended Audience :: Developers", "Development Status :: 5 - Production/Stable", "Topic :: Software Development :: Libraries :: Python Modules", "Programming Language :: Python", "Operating System :: OS Independent" ] ) springpython-1.2.0+ds/README.txt0000644000000000000000000000030711364062063015055 0ustar rootrootTo install this package, just run: python setup.py install This may require admin privileges. An alternative is to specify a different location and make sure that location is in your PYTHON_PATH. springpython-1.2.0+ds/MANIFEST.in0000644000000000000000000000007311364062063015115 0ustar rootrootrecursive-include springpython COPYRIGHT *.txt *.png *.jpg springpython-1.2.0+ds/setup.cfg0000644000000000000000000000007311526401330015172 0ustar rootroot[egg_info] tag_build = tag_date = 0 tag_svn_revision = 0