GNU Enterprise Application Server v2 Proposed Architecture

Introduction
 - GEAS (GNU Enterprise Application Server) is the middleware of the
 GNU Enterprise project
 - it acts as an Object Data Management System (ODMS), with its
 prime goal to pull the application logic from the front end UI client and
 back end database, thus providing a 'middle' layer
  Purpose
   - in 2-tier systems the application logic lies either in the front
   end or in the database (via events (triggers) or stored procedures)
   - main purpose of the application server is to pull application
   logic out of both front end and back end serving as a middle layer
   that abstracts the logic (called business rules) from the user
   interface as well as from the database back-end
  Goals
   - Apart from fulfilling the main purpose as described, we have
   defined several additional, ethical as well as technical, goals
   - Following list by priority:
   - Freedom: GEAS must be GPL and built with truly Free tools
   - Stability: GEAS must be built with a high degree of stability in
   mind as it will be used in mission-critical business applications
   - Security: GEAS must be reasonably secure so as to not compromise
   a business's data
   - Maintainability: The GEAS code base must be maintainable by the
   GEAS development team and clear enough to allow interested
   programmers to adapt to their needs, fix bugs, or even take over
   maintenance of part or all of GEAS
   - Configurability: GEAS must support configuration at runtime
   (dynamically) and a central location for configuration information
   it must be easily configured without programming skills, be
   configured without downtime, and in separate "layers" for various
   levels of specification
   - Performance: GEAS must perform reasonably well with large numbers
   of users and/or large quantities of data
   - Database Independence: GEAS must support a number of Database
   systems as the backend
   - Portability: GEAS must run on multiple Operating Systems and
   architectures.
   - Communication Independence: GEAS must be able to utilize various
   communication protocols to communicate with the front end (CORBA,
   SOAP, XMLRPC, HTTP, IRC, etc.)
   - Language Independence: GEAS must be able to support and handle
   business methods written in different programming languages
  Overview
   - UML arhcitecture diagram with some explanation
GNUe Data Interface (GEDI)
 - might be confused with EDI (Electronic Data Interchange)
GNUe Methods Adapter (GEMA)
GNUe Object Repository (GEOR)
GNUe Object Access Translator (GOAT)
Security Adapter (GNUe Access Control Server - GACS)
Remote Protocol Adapter (GNURPC)
Batch Schema Compiler (ODL compiler and/or ODLML parser)
SQL Schema Generator
Transaction Processor
Synchronization Engine
Workflow Server
ODMG Python Binding (needs paraphrasing badly ;) )
  Introduction
    - here we define the python binding for the ODMG Object Model, 
    ODL, and OQL
    - ???
  Language Design Principles
    - ODMG python binding based upon 2 principles: should bind to
    python in a natural way which is consistent with principles of the
    language, and should support language inertoperability consistent
    with ODL specification and semantics
    - these principles have several implications that are evident in
    the design of the binding

      1. there is a unified type system that is shared by python and
         the ODMS. this type system is ODL mapped into python by the
         python binding

      2. the binding respects python syntax, meaning the python
         language will not have to be modified to accommodate this
         binding. ODL concepts will be represented using normal python
         coding conventions

      3. binding respects the fact that python is dynamically
         typed. arbitrary python objects may be stored persistently,
         including ODL-specified objects that will obey the ODL typing
         semantics

      4. binding respects the dynamic memory management semantics of
         python. objects will become persistent when they are referred
         by other persistent objects in the database and will be
         removed when they are no longer reachable in this manner
         (this is referred to as transitive persistence)

  Language Binding
    - ODMG binding for python is based on OMG python IDL binding
    - as ODL is a superset of IDL, the IDL binding defines a large
    part of the mapping required by this document
    - section/chapter provides informal descriptions of the IDL
    binding topics and more formally defines the python binding for
    the ODL extensions, including relationships, literals and
    collections
    - ODMG python binding can be automated by an ODL compiler that
    processes ODL declarations and generates a graph of 'meta
    objects', which model the schema of the database (ODMS)
    - the meta objects provide the type information that allows the
    python binding to support the required ODL type semantics
    - complete set of such meta objects defines the entire schema of
    the database would serve much in the same capacity as an OMG
    Interface Repository
    - includes python binding for meta object interfaces defined in
    ch. 2
    - in such a  repository, the meta objects that represent the schema
    of the database may be programatically accesses and modified by
    python applications (clients, etc.), through their standard 
    interfaces
    - one such application, a 'binding generator', may be used to
    generate python class and method skeletons from the meta objects
    - this binding generator would resolve the type-class mapping
    choices that are inherent in the ODMG python binding
    - the info in the meta objects is also sufficient to 'regenerate'
    the ODL declarations for the portions of the schema that they
    represent
    - the relationships between these components are illustrated in
    Figure 1 (include geas-schema-compiler.png)
    - a conforming implementation must support the python output of
    this binding process; it need not provide automated tools
  
  Mapping the ODMG Object Model Into Python
    - although python has a powerful data model that is close to the one
    presented in ch. 2, it remains necessary to precisely describe how
    the concepts of the ODMG Object Model map into concrete python
    constructions

    Object and Literal
      - an ODMG object type maps into a python object type
      - since python has no distinct notion of literal objects, both
      ODMG objects and ODMG literals may be implemented by the same
      python classes (is this really true???)

    Relationship
      - this concept is not directly supported by Python and must be
      implemented by Python methods that support a standard protocol
      - the relationship itself is typically implemented either as an
      object reference (one-to-one relation) or as an appropriate
      'Collection' subclass (one-to-many, many-to-many relations)
      embedded as an instance variable of the object
      - rules for defining sets of accessor methods are presented that
      allow all relationships to be managed uniformly

    Names
      - objects may be named using methods of the Database interface
      defined in the Python OML
      - the root objects of a database are the named objects; root
      objects and any objects reachable from them are persistent

    Extents
      - extents are not supported by this binding, instead users may
      use the database naming protocol to explicitly register and access
      named 'Collections'

    Keys
      - key declarations are not supported by this binding
      - instead, users may use the database naming protocol to
      explicitly register and access named Dictionaries

    Implementation
      - everything in python is implemented as an object
      - the python language does not support the independent
      definition of interface from implementation, all classes/object
      types include both interface and implementation

    Collections
      - collection objects described in section 2.3.6 specify
      collection behavior, which may be implemented using many different
      collection representations such as hash tables, trees, chained
      lists, and so on
      - the python binding provides provides collection interfaces as
      described in chapter 2 as well as emulating python sequence and
      mapping types by implementing the special methods, providing
      various mathematical operation methods, and the various dictionary
      methods and mutable sequence methods (like the one in python's
      list object) for a more complete implementation (could expand
      here a lot further, but I am tired so it's going to have to
      wait)
      - provide summary of collection interfaces?!?

  Python ODL
  
    OMG IDL Binding Overview
      - since python/ODL binding is based upon the OMG python binding,
      we include here some descriptions of the important aspects of the
      IDL binding that are needed in order to better understand the ODL
      binding that follows

      Using Scoped Names (IDL Namespace)
       - python implements a module concept that is similar to the IDL
       scoping mechanisms, except that it does not allow for nested
       modules
       - in addition Python requires each object to be implemented in a
       module; globally visible objects are not supported
       - because of these constraints, scoped names are translated into Python using the 
       following rules:

         * an IDL module mapped into a Python module, modules containing modules are 
           mapped to packages (i.e., directories with an __init__ module containing all 
           definitions excluding the nested modules). An implementation can
           chose to map top-level definitions to modules in an implementation defined
           package, to allow concurrent installations of different ODMG runtime 
           libraries. In that case, the implementation must provide
           additional modules so that top-level modules can be used without 
           importing them from a package.

         * for all other scopes, a Python class is introduced that
           contains all the definitions inside this scope.

         * other global definitions (except modules) appear in a module
           whose name is  implementation dependent, implementations are
           encouraged to use the name of the IDL file when defining the
           name of that module.

       - For example,

         Module M
         {
           struct E
           {
             long L;
           };
           module M
           {
             interface I
             {
               void import(in string what);
             };
           };
         };

         const string NameServer="NameServer";

       would introduce a module M.py, which contains the following
       definitions:

         # since M is a package, this appears in M/__init__.py
         class E:  
           pass # structs are discussed later

         # module M/N.py
           class I:
             def _import(self, what):
               pass # interfaces are discussed later

       the string NameServer would be defined in another module
       because the name of that module is not defined and
       using global definitions for modules is discouraged

       to avoid conflicts, IDL names that are also Python identifiers are
       prefixed with an underscore ('_'), for a list of keywords, see Table
       1-1

         Table 1-1 Python keywords
         ------------------------------------
         and    assert  break class continue
         def    del     elif  else  except
         exec   finally for   from  global
         if     import  in    is    lambda
         not    or      pass  print raise
         return try     while

      Types
        Simple Types
          - because python does not require type information for
          operation declarations, it is not necessary to introduce
          standardized types names, unlike C or C++ mappings
          - instead, the mapping of types to dynamic values is specified
          here
          - for most of the simple types, it is obvious how values of
          these types can be created
          - for the other types, the interface for constructing  values
          is also defined
          - the mappings for the basic types are shown in table 1-2

           Table 1-2 Basic Data Type Mappings
           ---------------------------------------------------------
           OMG IDL             Python
           ---------------------------------------------------------
           octet               Integer (<type 'int'>)
           short               Integer
           long                Integer
           unsigned short      Integer
           unsigned long       Long integer (<type 'long int'>)
           long long           Long integer (<type 'long int'>)
           unsigned long long  Long interger
           float               Floating Point Number (<type 'float'>)
           double              Floating Point Number
           long double         CORBA.long_double
           boolean             Interger
           char                string of length 1
           wchar               Wide string length of 1
         Compound Types
           Array
             - an appropriate Python sequence type supporting the
             'Array' interface along with the appropriate python
           sequence methods (e.g., __getitem__)

           Enumeration
             - an enumeration is mapped into a number of constant objects
             in the name space where the enumeration is defined
             - an application may only test for equivalence of two
             enumeration values, and not assume that they behave like
             numbers
         
             - For example, the definition:

              module M
              {
                enum color {red, green, blue};
 
                interface O
                {
                  enum Farbe {rot, gruen, blau};
                };
              };
   
             introduces the objects:
 
             import M
             M.red, M.green, M.blue, M.O.rot, M.O.gruen, M.O.blau

           Structures
             - an IDL struct definition is mapped into a Python class or
             type
             - for each field in the struct, there is a corresponding
             attribute in the class with the same name as the field
             - the constructor of the class expects the field values, from
             left to right

             For example, the IDL definition:

               struct segment {long left_limit, long right_limit};

               could be used on Python statements

               s=segment(-3,7)
               print s.left_limit, s.right_limit

           Unions
             - Union types are mapped to classes with two attributes,
             first being the descriminant _d, the second the associated
             value _v
             - for each branch, there is an additional attribute,
             which can only be accessed if the branch has been set,
             there are three possibilities:

               - if the discriminant was explicitly listed in a case
                 statement, the value is of the branch associated
                 with the case
               - if the discriminant is not explicitly listed and
                 there is a default case label, the value is of the
                 branch associated with the case label
               - if the discriminant is not listed, and there is no
                 default, the value is None

             - constructor of the class expects the discriminator and
             the value as arguments
             - alternatively, the union can also be constructed by
             passing a keyword argument, with the field name of the
             union as the key, if more than one discriminator is
             associated with a field, the discriminator must be
             set explicitly

             Example:
 
             union MyUnion switch(long)
             {
               case 1: string s;
               default: long x;
             };

             can be accessed as

             u = MyUnion(17, 42)
             print u.x
             u = MyUnion(s = 'string')
             print u._d, u._v

      Exceptions
        - when error detected, exception is raised using standard Python
        exception mechanism, which consists of deriving from
        exceptions.Exception, the following standard exception types are
        defined, some are raised from specific ODMG interfaces and are
        thus subclasses of OMDGError, others may be raised in the
        course of using persistent objects and are thus subclasses of
        ODMGRuntimeError, and others are related to query processing
        and are just subclasses of QueryError, which in turn is a
        subclass of ODMGError:

	class ODMGError (exceptions.Exception):

          Base exception class for ODMG specific interfaces

        class ODMGRuntimeError (exceptions.Exception):

          Base class for exceptions raised in the course of using
          peristent object

        class QueryError (ODMGError):

          Base class for exceptions rasied during query processing

        class ClassNotPersistenceCapableError (ODMGRuntimeError):

          Raised when the implementation cannot make the object
          persistent because of the type of object

        class DatabaseClosedError (ODMGRuntimeError):

          Raised when attempting to call an operation for which a
          database is not open but is required to be open

        class DatabaseIsReadOnlyError (ODMGRuntimeError):

          Raised when attempting to call a method that modifies a
          database that is open read-only

        class DatabaseNotFoundError (ODMGError):

          Raised when attempting to open a database that does not exist

        class DatabaseOpenError (ODMGError):

          Raised when attempting to open a database that is already
          opened

        class LockNotGrantedError (ODMGRuntimeError):

          Raised if a lock could not be granted. (Note that time-outs
          and deadlock detection are implementation defined)

        class NotImplementedError (ODMGRuntimeError):

          Raised when an implementation does not implement an operation
          or when the underlying implementation does not support an
          operation

        class ObjectDeletedError (ODMGRuntimeError):

          Raised when accessing an object that was deleted

        class ObjectNameNotFoundError (ODMGError):

          Raised when attempting to get a named object whose name is not
          found

        class ObjectNameNotUniqueError (ODMGError):

          Raised when attempting to bind a name to an object when the
          name is already bound to an existing object

        class ObjectNotPersistentError (ODMGRuntimeError):

          Raised when deleting an object that is not persistent

        class QueryInvalidError (QueryError):

          Raised if the query is not a valid OQL query and thus does not
          compile

        class QueryParameterCountInvalidError (QueryError):

          Raised when the number of bound parameters for a query does
          not match the number of placeholders

       class QueryParamterTypeInvalidError (QueryError):

         Raised when the type of a parameter for a query is not
         compatible with the expected parameter type

       class TransactionAbortedError (ODMGRuntimeError):
 
         Raised when the database system has asynchronously terminated
         the user's transaction due to deadlock, resource failure, and
         so on. In such cases the user's data is reset just as if the
         user had called Transaction.abort.

       class TransactionInProgressError (ODMGRuntimeError):

         Raised when attempting to call a method within a transaction
         that must be called when no transaction is in progress

       class TransactionNotInProgressError (ODMGRuntimeError):

         Raised when attempting to perform outside of a transaction an
         operation that must be called when there is a transaction in
         progress

      Constants
        - IDL constant definition maps to Python variable initialized
        with the value of the constant

      Operations
        - nil object is represented by 'None'.
        - if operation expects parameters of the IDL Object type, and
        Python object representing an object reference might be passed
        as an actual argument
        - if an operation expects a parameter of an abstract interface,
        either an object implementing that interface, or a value
        supporting this interface may be passed as an actual argument
        - the semantics of abstract values then define whether the
        argument is passed by value or by reference
        - operations of an interface map to methods available on the
        object references
        - parameters with a parameter attribute of 'in' or 'inout' are
        passed from left to right to the method, skipping 'out'
        parameters
        - return value of a method depends on the number of 'out'
        parameters and the return type
        - if the operation returns a value, this value forms the first
        'result value'
        - all 'inout' or 'out' parameters form consecutive 'result
        values'
        - the method result depends then on the number of 'result values':
          * if there is no 'result value', the method returns 'None'
          * if there is exactly one 'result value', it is returned as a single 
            value
          * if there is more then one 'result value', all of them are packed
          into a tuple, and this tuple is returned

          Assuming the IDL definition:
            
            interface I
            {
              oneway void stop();
              bool more_data();
              void get_data(out string name, out long age);
            };

          a client would write
         
            names={}
            while my_I.more_data():
              name,age = my_I.get_data()
              names[name]=age
            my_I.stop()

        - if an interface defines an 'attribute name', the attribute is mapped
        into an opertaion _get_name, as defined

        - if the attribute is not 'readonly' there is an additional operation
        _set_name, as defined in the 'OMG IDL Syntax and Semantics' chapter,
        "Attribute Declaration" section, of the 'Common Object Request Broker:
        Architecture and Specification' document

    Python ODL Binding Extensions
      - section describes binding of ODMG ODL to Python
      - ODL provides a description of the database schema as a set of
      interfaces, including their attributes, relationships, and
      operations
      - Python implementations consist of a set of object classes and
      their instances
      - language binding provides a mapping between these domains
      
      Interfaces and Classes
        - in ODL, interfaces used to represent the 'abstract behavior'
        of and object, and classes used to model the 'abstract state'
        of objects
        ***** to be determined *****
        - add ODLName method that returns the name of the ODL
        interface or class that is bound to the object in the schema
        repository? e.g., aDate.ODLName() returns string '::Date',
        which is name of the odl interface
        - we may not need this because python has a namespace and I
        only see the Smalltalk binding doing this because it does not
        support the concept of namespaces, afaict
        ***** to be determined *****
        - an ODL interface maps to a Python class that raises
        MethodNotImplementedError for the operations of the interface
        class, all other conventions follow the IDL binding for interfaces
        - an ODL class maps to a Python class

      Attribute Declarations
        - attribute declarations used to define pairs of accessor
        operations that get and set attribute values
        - Generally, there would be a one-to-one correspondence
        between attributes defined within an ODL class and instance
        variables defined within the corresponding Python class,
        although this is not required
        - ODL attributes define abstract state of their object when
        they appear within class definitions
        - when attributes appear within interface definitions, as in
        IDL they are merely a convenience mechanism for introducing get
        and set accessing operations
        - e.g. attribute Enum Rank {full, associate, assistant} rank;

          yields Python methods:

          def _get_rank(self):

          def _set_rank(self, aProfessorRank):
	  
      Relationship Declarations
        - relationships define sets of accessor operations for adding
        and removing associations between objects
        - as with attributes relationships are a part of an object's
        abstract state
        - Python biding for relationships results in public methods to
        "form" and "drop" members from the relationship, plus public
        methods on the relationship target classes to provide access and
        private methods to manage the required referential integrity
        constraints 
        - we begin the relationship binding by applying the chapter 2
        mapping rule from ODL relationships to equivalent IDL
        constructions and then illustrate with a complete example:

        Single-Valued Relationships

          relationship X Y inverse Z;

        we expand first to the IDL attribute and operations:

          attribute X Y;
          void form_Y(in X target) raises(IntegrityError);
          void drop_Y(in X target) raises(IntegrityError);

        which result in the following Python methods:

          def Y(self):

          def form_Y(self, target):

          def drop_Y(self, target):

          def __Y(self):

        For example, from Chapter 3:

          interface Course
          {
            ....
            relationship Professor is_taught_by 
                inverse Professor::teaches;
            ...
          }
        
        Expands into the following IDL attributes and operations:

          attribute Professor is_taught_by;

          void form_is_taught_by(in Professor aProfessor)
                   raises(IntegrityError);

          void drop_is_taught_by(in Professor aProfessor)
                   raises(IntegrityError);

        yields Python methods (on the class Course):

          def form_is_taught_by(self, aProfessor):

          def drop_is_taught_by(self, aProfessor):

          def is_taught_by(self):

          def __is_taught_by(self):

        Multivalued Relationships
          - for a multivalued relationship such as:

            relationship set<X> Y inverse Z;

        we expand first to the IDL attribute and operations:

          readonly attribute set<X> Y;

          void form_Y(in X target) raises(IntegrityError);

          void drop_Y(in X target) raises(IntegrityError);

          void add_Y(in X target) raises(IntegrityError);

          void remove_Y(in X target) raises(IntegrityError);

        which results in the following Python methods:

          def Y(self):

          def form_Y(self, target):

          def drop_Y(self, target):

          def __add_Y(self, target):

          def __remove_Y(self, target):

        For example:
   
          interface Professor
          {
            ...
            relationship Set<Course> teaches
                inverse Course::is_taught_by;
            ...
          }

        Expand into the following IDL and attributes and operations:

          readonly attribute set<Course> teaches;

          void form_teaches(in Course aCourse) raises(IntegrityError);

          void drop_teaches(in Course aCourse) raises(IntegrityError);

          void add_teaches(in Course aCourse) raises(IntegrityError);

          void remove_teaches(in Course aCourse) raises(IntegrityError);

        which results in the following Python methods:

          def teaches(self):

          def form_teaches(self, aCourse):

          def drop_teaches(self, aCourse):

          def __add_teaches(self, aCourse):

          def __remove_teaches(self, aCourse):

      Collections
        - ch. 2 introduced several new kinds of collections that
        extend the IDL sequence
        - the following shows the Python interfaces/classes that this
        binding defines for each of the collection interfaces