OSID 2.0 Design Patterns
OSID Type Registry
Tom Coppeto
OnTapSolutions
27 April 2005

I'm tired of creating Types. I know I said I don't like using them, but the classes still need to be created regardless of whether or not they are used in an Implementation. Some Types, such as loggingPriorityType, are unavoidable in the current OSID Specification.

Types are funny in that they do not represent an actual object They contain a form of an identifier that classifies an object, behavior or collection. The Type itself is an abstract class left to be defined in a subclass. This results in Implementations carrying its own Type classes that the Application needs to access. This creates another linkage between the Application and the Implementation. Not only does there need to be agreement on the identifier aspect of the Type, but in practice the interoperability depends on the package name of the Type class itself.


    logger.appendLogWithTypes(message, 
                              edu.mit.osidimpl.logging.defaultLoggingFormatType,
			      edu.mit.osidimpl.logging.criticalLoggingPriorityType);

Listing 1: example use of Types using classes

The class representing the Type is irrelevant. If Applications need to pass a Type through the Interface, they should do so by specifying the identification components and not create dependencies upon Implementation class files. We just need a handy way to do this.


    logger.appendLogWithTypes(message, 
                              getType(logging format identifier,
			      getType(critical priority identifier);

Listing 2: example use of Types using an identifier
Shorthand

The Type is identified by a domain, authority and a key. It is this triplet of Strings that makes the type unique. For convenience I combine the Strings into a format I can easily parse, stash away in databases, or just make the code a bit cleaner.

domain/key@authority

Since I used this format for both Properties and the OsidContext, I'll use it here as well.

Type Registry

The Type Registry accomplishes three tasks. It instantiates Types upon request given the identifying components, it centralizes all the Type definitions known to an Implementation Suite in one place, and it separates the display string function from the identification function.

The Type Registry contains a HashMap of the Types that have been made known to it and provides a number of supporting methods. First lets look at the Type. Identification and display are different functions and programmers should not have to worry about making their identifiers pretty should one appear in a menu. Always being strict about sticking closely with the Specification, the Type cannot change but there's no rule that we can't add some useful information along side of it.

memberdescriptionexample
Stringidthe shorthand identifier for this Typeoid/1.3.6.1.4.1.22316.1.1.1.2.1.8.0@osid.org
Stringdesignationa one word label for the Type intended to be used for internal purposes, such as debuggingcriticalLoggingFormatType
Stringnamea proper name for this TypeCritical Priority
Stringlabela shorter label for this Type where it might be more appropriate in some UI elementscritical
Typetypethe actual Type

The above structure is contained in a class called TypeContainer. The OsidTypeRegistry contains the HashMap of TypeContainers.


    public abstract class OsidTypeRegistry
        implements java.io.Serializable {

        private static java.util.HashMap types = new java.util.HashMap();


        protected OsidTypeRegistry();

        public static org.osid.shared.Type getType(String id);

        public static String getIdForType(org.osid.shared.Type type);

        public static String getDesignationForType(org.osid.shared.Type type);

        public static String getNameForType(org.osid.shared.Type type);

        public static String getLabelForType(org.osid.shared.Type type);

        public static String typeToString(org.osid.shared.Type type);

        protected synchronized static void loadTypeInfo(String authority, String file);
    }

Listing 3: the OsidTypeRegistry methods

The OsidTypeRegistry is a static class that holds all of the Types that have been registered through the loadTypeInfo() method accessible to classes which subclass it. Each subclass loads a set of Type identifiers. For sanity, this set should all be under the same authority in the same domain. These subclasses also make constants available for convenient access. The example here uses object identifiers, but any domain may be used.


    public class OsidTypeOIDRegistry
        extends OsidTypeRegistry {

        private static String DOMAIN      = "OID";
        private static String AUTHORITY   = "osid.org";
        private static String FILE        = "OsidTypeOID.dat";

        public static String TRACE_LOGGING_PRIORITY_TYPE    = "oid/1.3.6.1.4.1.22316.1.1.1.2.1.2.0@osid.org";

        public static String DEBUG_LOGGING_PRIORITY_TYPE    = "oid/1.3.6.1.4.1.22316.1.1.1.2.1.3.0@osid.org";

        public static String INFO_LOGGING_PRIORITY_TYPE     = "oid/1.3.6.1.4.1.22316.1.1.1.2.1.4.0@osid.org";
    
        public static String NOTICE_LOGGING_PRIORITY_TYPE   = "oid/1.3.6.1.4.1.22316.1.1.1.2.1.5.0@osid.org";

        public static String WARNING_LOGGING_PRIORITY_TYPE  = "oid/1.3.6.1.4.1.22316.1.1.1.2.1.6.0@osid.org";

        public static String ERROR_LOGGING_PRIORITY_TYPE    = "oid/1.3.6.1.4.1.22316.1.1.1.2.1.7.0@osid.org";

        public static String CRITICAL_LOGGING_PRIORITY_TYPE = "oid/1.3.6.1.4.1.22316.1.1.1.2.1.8.0@osid.org";

        public static String FATAL_LOGGING_PRIORITY_TYPE    = "oid/1.3.6.1.4.1.22316.1.1.1.2.1.9.0@osid.org";

        static {
            loadTypeInfo(AUTHORITY, FILE);
        }
    }

Listing 4: An OsidTypeRegistry

For ease of maintenance, most of the meta information surrounding the Type is kept in a configuration file. This file maps the Type identifier to the display strings and description.


    oid/1.3.6.1.4.122316.1.1.1.2.1.2.0@osid.org:traceLoggingPriorityType:Trace Logging Priority:trace:The priority Type used by the Logging OSID to indicate a trace level message.
    oid/1.3.6.1.4.122316.1.1.1.2.1.3.0@osid.org:debugLoggingPriorityType:Debug Logging Priority:debug:The priority Type used by the Logging OSID to indicate a debug level message.
    oid/1.3.6.1.4.122316.1.1.1.2.1.4.0@osid.org:infoLoggingPriorityType:Informational Logging Priority:informational:The priority Type used by the Logging OSID to indicate an informational level message.
    oid/1.3.6.1.4.122316.1.1.1.2.1.5.0@osid.org:noticeLoggingPriorityType:Noitice Logging Priority:notice:The priority Type used by the Logging OSID to indicate a notice level message.
    oid/1.3.6.1.4.122316.1.1.1.2.1.6.0@osid.org:warningLoggingPriorityType:Warning Logging Priotity:warning:The priority Type used by the Logging OSID to indicate a warning level message.
    oid/1.3.6.1.4.122316.1.1.1.2.1.7.0@osid.org:errorLoggingPriorityType:Error Logging Priority:error:The priority Type used by the Logging OSID to indicate an error level message.
    oid/1.3.6.1.4.122316.1.1.1.2.1.8.0@osid.org:criticalLoggingPriorityType:Critical Logging Priority:critcal:The priority Type used by the Logging OSID to indicate a critical level message.
    oid/1.3.6.1.4.122316.1.1.1.2.1.9.0@osid.org:fatalLoggingPriorityType:Fatal LoggingPriority:fatal:The priority Type used by the Logging OSID to indicate a datal level message.

Listing 5: OsidTypeRegistry data file

The OsidTypeRegistry subclass and the data file together create a module of Type definitions that can be accessed and shared. On the Application side, Types can be generated on the fly through the identifier.


    logger.appendLogWithTypes(message, 
                              OsidTypeRegistry.getType(OsidTypeOIDRegistry.CRITICAL_LOGGING_PRIORITY_TYPE),
                              OsidTypeRegistry.getType("oid/1.3.6.1.4.122316.1.1.1.2.1.1.0@osid.org"));

Listing 6: Logging example that creates Types on the fly

The OsidTypeRegistry brings together the Type definitions that are scattered through an Implementation Suite and provides a framework for sharing them. By making Type creation indirect through a registry class, you can avoid coding of the Type's class to create more insulation between Applications and Implementations.

Code Listings