More Flexible Sakai Configuration

Using sakai-configuration.xml to configure Sakai

1. Background

In Sakai 2.5.x and earlier versions, the only standard way to configure the Sakai framework and application suite is through a set of optional properties files placed in the installation's "sakai.home" (and possibly "sakai.security") areas:

  • ${sakai.home}sakai.properties - For most changes to text properties
  • ${sakai.home}local.properties - Meant for text properties that are specific to a single machine on a cluster
  • ${sakai.security}security.properties - Meant for text properties that require tighter access permissions than those of "sakai.home"
  • ${sakai.home}placeholder.properties - Deprecated file, still checked to support legacy installations

Other integration and customization needs could only be met by discarding the binary distribution, editing the source code, and re-building.

2. sakai-configuration.xml

As of 2.6, Sakai will also check your "sakai.home" area for a Spring bean definition file named "sakai-configuration.xml". This file will be read after all Sakai component definitions have been loaded, but before the system starts. It can be used for the following purposes (in ascending level of risk):

  • To customize the list of properties files
  • To handle configurations that are too complex for a single string property
  • To override or disable standard service implementations

2.1. Examples of customizing beans

2.1.1. Customizing the list of properties files

Let's say your institution maintains three server clusters, one for development, one for QA, and one for production. The three deployments use different database tables, and each machine in a cluster needs to set its own "serverId" property, but otherwise it's important that they share the same configuration. The following sakai-configuration.xml file might serve your needs well:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

    <!-- Change the list of local properties files. -->
    <bean id="org.sakaiproject.component.SakaiProperties"
      parent="org.sakaiproject.component.DefaultSakaiProperties">
        <property name="locations">
            <list merge="true">
                <value>file:${sakai.home}standard.properties</value>
                <value>file:${sakai.home}database.properties</value>
                <value>file:${sakai.home}server.properties</value>
            </list>
        </property>
    </bean>

</beans>

For more background, see SAK-8315 - Getting issue details... STATUS .

2.1.2. Customizing gradebook values

Let's say you just wanted to change the grading scales in edu-services. The default bean listed in the components.xml isn't in shared and has some properties not worth overriding anyway, but you can override the importantvalues. The below just overrides to only have A's and B's. You can add as many GradingScaleDefinition beans as you like just like in the original file. This sakai-configuration.xml file might serve your needs well:

Note  SAK-29740 - Getting issue details... STATUS  changed the defaultBottomPercents. You either need to set DefaultBottomPercents as a map or use the defaultBottomPercentsAsList as in the example.

 

<map>
 <entry key="P" value="75.0" />
 <entry key="NP" value="0.0" />
</map>

 

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">
       <!-- Simple configuration handler. -->
        <bean id="org_sakaiproject_service_gradebook_GradebookScalesConfiguration"
                class="org.sakaiproject.service.gradebook.shared.GradebookConfiguration"
                init-method="init">
                <!-- Set up default system-wide grading scales. -->
                <property name="gradebookFrameworkService" ref="org.sakaiproject.service.gradebook.GradebookFrameworkService"/>
                <property name="availableGradingScales">
                        <list>
                                <bean class="org.sakaiproject.service.gradebook.shared.GradingScaleDefinition">
                                        <property name="uid" value="LetterGradePlusMinusMapping"/>
                                        <property name="name" value="Letter Grades with +/-"/>
                                        <property name="grades">
                                                <list>
                                                        <value>A</value>
                                                        <value>A-</value>
                                                        <value>B+</value>
                                                        <value>B</value>
                                                </list>
                                        </property>
                                        <property name="defaultBottomPercentsAsList">
                                                <list>
                                                        <value>95.0</value>
                                                        <value>90.0</value>
                                                        <value>87.0</value>
                                                        <value>83.0</value>
                                                </list>
                                        </property>
                <property name="defaultGradingScale" value="LetterGradePlusMinusMapping"/>
       </bean>

</beans>

2.1.3. Switching from DBCP to the tomcat pool

In Sakai 10 support was added to make it easier to switch the BaseDataSource bean over to the tomcat one (from the default DBCP one). You just need to override this bean in this file.  KNL-1000 - Getting issue details... STATUS

<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans" 
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd"> 
    <!-- Shared DataSource for all pooled database connections --> 
    <!-- Either make this javax.sql.dbcp.BaseDataSource or javax.sql.tomcat.BaseDataSource --> 
        <bean id="javax.sql.BaseDataSource" parent="javax.sql.tomcat.BaseDataSource"></bean> 
</beans>

2.2. Complex configuration

Some sorts of configuration are too complex to be handled by overriding a text property. One example is defining how LDAP attributes or enrollment statuses should map to Sakai user roles.

If a service's API includes an externalizable configuration object, sakai-configuration.xml can be used to override that configuration's default settings.

For example, let's say your institution uses the standard Course-Management-based site role provider, but needs to customize the default role mappings to match the enrollment data available from your student information systems. For the Sakai 2.6 CourseManagementGroupProvider, here's a sample customization:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:util="http://www.springframework.org/schema/util"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
    http://www.springframework.org/schema/util
    http://www.springframework.org/schema/util/spring-util-2.0.xsd">

    <!-- Customize mapping from Course Management data to Sakai roles -->
    <util:map id="org.sakaiproject.coursemanagement.GroupProviderConfiguration">
        <entry key="siteRoleResolutionOrder">
            <list>
                <value>Instructor</value>
                <value>Student</value>
            </list>
        </entry>
        <entry key="officialInstructorToSiteRole" value="Instructor"/>
        <entry key="enrollmentStatusToSiteRole">
            <map>
                <entry key="E" value="Student"/>
                <entry key="W" value="Student"/>
            </map>
        </entry>
        <entry key="sectionRoleToSiteRole">
            <map>
                <entry key="teacher" value="Instructor"/>
                <entry key="learner" value="Student"/>
            </map>
        </entry>
    </util:map>

</beans>

CAUTION

This is a new capability in Sakai 2.6, and few service APIs have changed to take advantage of it. In many cases, if you mistakenly try to use this feature to override a service-internal object, Sakai will simply fail due to classloader issues. In some cases, however, you might succeed in overriding an object that's not part of a service's supported API, which would leave your installation open to unexpected breakage in later releases of Sakai.

Note that a similar caution applies to sakai.properties lines which override bean properties using the "@" syntax:

internalUseOnly@org.sakaiproject.some.ClassNotInterface=kablooey

For more background, see SAK-12237 - Getting issue details... STATUS .

2.3. Disabling standard service implementations

A Sakai binary release deploys many components, including some that were designed with installation-specific overrides in mind, and possibly also including some not-so-customizable services that you find you need to replace or disable.

To better support planned overrides, over time the product suite has begun using standard Spring techniques such as lazy loading, proxies, and injection-by-reference. As a result, in Sakai 2.6 you're able to enable your own user directory provider, your own course management service, and your own group-authorization provider through a Sakai properties file rather than having to comment out or delete released source code.

Unplanned overrides are a different story. Should you find yourself in such a position, it may be possible to disable or replace a standardly deployed Sakai component without taking on the extra maintenance cost of an edited source tree. Because sakai-properties.xml takes effect after all component definitions have been read, if the file includes a bean alias or overrides a bean definition, the original component definition will effectively be lost.

For example, if you're developing a reimplementation of Sakai's Section Awareness Service, you might want to swap a test system back and forth between your experimental prototype and the official code. This sakai-configuration.xml might work for you:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-2.0.xsd">

    <!-- Override standard service implementation -->
    <alias name="edu.myschool.ExperimentalSectionAwareness"
        alias="org.sakaiproject.section.api.SectionAwareness"/>

</beans>

WARNING

Unless otherwise documented, this technique should be treated as highly dangerous. It's possible that non-lazily-loaded beans will expect to find the exact service you've written over, or that you've accidentally broken other internal dependencies. Even if the override appears to work for a particular version of Sakai, there's no guarantee that it will continue to work with the next minor maintenance release.

To put it another way, this approach eliminates the cost of maintaining and building source code for a local branch, but it will not eliminate the QA cost associated with local branches. Even though you haven't modified the Java or XML files included in a Sakai release, you must view this as an unsupported modification of Sakai's runtime behavior.

3. Bean Overrides

If you wish to create new beans inside a component then you can define additional these additional beans in a set of files defined in "sakai.home"/override/{component-name}.xml when the component manager is starting up it will look in there and load an additional beans defined in this file. This allows you to define new spring beans.

If you want to configure Sakai to use LDAP you can copy /providers/component/src/webapp/WEB-INF/jldap-beans.xml to "sakai.home"/override/sakai-component-pack.xml and edit the file for point at your local LDAP server and anything else you need to set.

4. Alternatives and future directions

  • Dynamic configuration approaches - Some subset of Sakai properties can safely be changed on a running system. Also, some installations might prefer to avoid dealing directly with a text file when configuring Sakai properties. Tony Atkins of U. Highlands and Islands has created a Configuration Viewer and Editor application, and Thomas Amsler of U. California, Davis, has created a Dynamic Configuration Service which can be administered with JMX.
  • Centralized control of deployed components - Support for easier enabling and disabling of Sakai services and applications has long been requested. As the Sakai project suite continues to expand, the need only becomes more imperative. It's likely that the Component Manager will be redesigned for the next major version of Sakai, with this among the requirements.