450 likes | 627 Views
Sakai Enterprise Integration and Tool Enhancements at Georgia Tech. Dan McCallum Developer, Unicon, Inc. Overview Source Control Banner Integration User Identity Email Whitelisting Other Enhancements. Overview. Project Outline.
E N D
Sakai Enterprise Integration and Tool Enhancements at Georgia Tech Dan McCallum Developer, Unicon, Inc
Overview • Source Control • Banner Integration • User Identity • Email Whitelisting • Other Enhancements
Project Outline • Implementation planning session in Atlanta Dec 11 – 13, 2006. • Development Feb – Mid-April, 2007. • TSquare (https://t-square.gatech.edu/) pilot launched May 11, 2007. • Georgia Tech OIT and CETL provided SA, DBA, QA, SME resources • Unicon and Aeroplane Software provided development, SA, QA
Development Targets • Content migration from WebCT (see http://bugs.sakaiproject.org/confluence/display/CONF07/Migration+Tool+for+WebCT+4.1) • Automated Sakai worksite and enrollment provisioning from Banner (approx 5000 course sections for summer) • Unique user identity management requirements • Other Sakai enhancements/extensions: • Worksite email whitelist publication • TSquare Skin • Assignment auto-submission • SIS-related UI extensions • Improved support for cross-service database transactions
Source Code Overlay SVN community trunk A B B checkout local export A B
The Theory • Avoid complex vendor drops (burned in the past, esp 2.1 -> 2.2) • Expected to make few modify community code • Wanted to be able to easily identify modified community code
The Reality • Just doesn’t scale • By the end of phase 1, just about every update would fail or at least it seemed that way • Difficult to merge community maintenance releases • Local mods always win • Updates don’t include latest community code • Have to remember to check in “baseline” code prior to committing local mods • Annoying, continual svn:ignore updates
SCM – Phase 2 • Migrated to a conventional vendor drop approach tracking 2.4.x maintenance branch • Manageable so long as drops are frequent and/or changesets are small.
Vendor Code Drop SVN vendor merge trunk current A A B B dropX A B checkout local A B
GaTech Course Models – Independent • Banner CourseSection == Sakai worksite • 75% of Banner courses
Independent Course Handling Worksite X Crn: 11111 Term: 200701 Sakai Banner Worksite Y Crn: 22222 Term: 200701 CourseSection X Crn: 11111 Term: 200701 Worksite Z Crn: 33333 Term: 200701 CourseSection Y Crn: 22222 Term: 200701 CourseSection Z Crn: 11111 Term: 200701 JMS Handle Course Create XML Messages Send Course Create XML Messages
GaTech Course Models - Linked • Courses have some dependency relationship known to the SIS • E.g. a lecture with several “linked” labs • Grades typically reported under one course within a linked set • 10% of Banner courses • Linkage data not conveyed in LDIS messages
Linked Course Handling Worksite X Crn: 11111 Term: 200701 Sakai Banner Section Y Crn: 22222 CourseSection X Crn: 11111 Term: 200701 links Section Z Crn: 33333 CourseSection Y Crn: 22222 Term: 200701 CourseSection Z Crn: 11111 Term: 200701 JMS Send Course Create XML Messages Handle Course Create XML Messages
GaTech Course Models – Cross-Listed/Combined • Multiple Banner CourseSections treated as a single entity, except for grading • Two variations: • Cross-listed, i.e. “equivalencies” • Combined, i.e. ad-hoc peer course relationships • 5%+ of Banner Courses
Cross Listed Course Handling Banner Worksite XX Crn: XLSXX200701 Term: 200701 Sakai Crosslist Group XX ID: XLSXX200701 CourseSection X Crn: 11111 Term: 200701 Section X Crn: 11111 CourseSection Y Crn: 22222 Term: 200701 CourseSection Z Crn: 11111 Term: 200701 Section Y Crn: 22222 Section Z Crn: 33333 JMS Send Course Create XML Messages Handle Course Create XML Messages
Candidate Enhancements • Composite cross-listed worksite titles • Sakai-to-Banner gradebook push • Performance improvements • CM-backed implementation • Automated linked CourseSection handling • Sakai dashboarding, deep-linking from portal • Automated worksite aliasing
GaTech AuthN • Two basic problems: • Users with multiple login IDs need access to a single Sakai account • Identifier type ambiguity in UserDirectoryService and UserDirectoryProvider method arguments • Custom UserDirectoryProvider • Sets UserEdit.eid to a GUID read from LDAP • Heuristics for guessing at lookup key (i.e. user ID) types • Extends refactored JLDAPDirectorProvider in contrib (more flexible attribute mapping, pluggable connection mgmt, parameter scrubbing, etc etc etc) • Would be nice to see first-class distinction between authentication, integration and display user IDs
GaTech AuthZ • Users’ access to Sakai controlled by LDAP “entitlements” attributes • Entitlements enforced even if user authenticated via CAS • GaTechUserDirectoryService.authorizeUserLogin(User) • Custom AuthenticationManager post-processes user authentication
GaTech AuthN/AuthZ : LoginTool <<AuthenticationManager>> : GaTechUserAuthnComp : AuthenticationManager <<UserDirectoryService>> : GaTechUserDirectoryService <<UserDirectoryProvider>> : GaTechUserDirectoryProvider doPost() authenticate(:Evidence) authenticate(:Evidence) authenticate(eid,pass) getUserByEid(eid) getUserId(eid) isGtDirectoryId(eid) user : UserEdit false new() getUserByAccountId(user,eid) [LDAP search] setEid(directoryId) [set other attrs] authenticateUser(eid,user,pass) [LDAP bind] true user : : Authentication authorizeUserLogin(user) authorizeUserLogin(userLoginId) [LDAP search] true : Authentication
GaTech User Attributes • GaTechUserDirectoryService.getUserByImsId(String) • New finder method for Banner services • Does UserDirectoryService interface need more generic search capability? • Confidential user attributes • Masked by default in LDAP • Sakai tools responsible for “unmasking”, e.g. to disambiguate students in the Gradebook • Minor mapping issues • Wiki stores EIDs in short db columns – had to trim values provided by LDAP • User mail-to email address not identical to lookup addresses • UserMembership Tool • Doesn’t scale for large, remote user bases • Postponed tool deployment
Exporting Worksite Email Addrs • Need to publish addresses at which Sakai worksites receive email • Implemented as passive services with an Axis wrapper • Web services polled periodically by GaTech mail exchanger
SiteMailQueryService /** * An interface defining a way to ask Sakai for all of the email * addresses that belong to its sites. */ publicinterface SiteMailQueryService { /** * * @return a Collection of objects that represent the email addresses of all the sites in Sakai * * @see SiteMailAddresses */ Collection<SiteMailAddresses> getCurrentSiteMailAddresses(); }
GaTechSiteMailQueryServiceFacade publicinterface GaTechSiteMailQueryServiceFacade { /** * * @param sessionId the id for a currently valid Sakai * session, used as an authorization token * @return an array of all the email addresses assigned to * all the sites in Sakai * @throws AxisFault */ public String[] getCurrentSiteMailAddresses(String sessionId) throws AxisFault; }
Brand Application • Continuity of the Georgia Tech brand to Sakai
Brand Application • Elegant navigation
Brand Application • Cross browser consistency
Service Decorators • Gradebook and Assignments • An alternative to extending or directly modifying concrete service implementations • Results in more portable extensions – across institutions and across Sakai versions • For example, schools already running a custom AssignmentService impl could reuse the Georgia Tech feature add without modifying either their code or BasicAutoSubmitAssignmentService • Still need to modify components.xml, which usually involves a rebuild of that service
Assignment Auto-Submission • Draft assignments automatically submitted as a side-effect of querying AssignmentService for Submissions • Auto-submission boundary determined by “Accept Until” Assignment attribute (i.e. Assignment.closeTime). • Optional – instructors can choose canonical or auto-submit mode when placing tool • Another service wrapping exercise
Wrapping AssignmentService publicabstractclass DecoratableAssignmentService implements AssignmentService { protected AssignmentService assignmentService; publicvoid setAssignmentService(AssignmentService assignmentService) { this.assignmentService = assignmentService; } public AssignmentEdit addAssignment(String context) throws PermissionException { return assignmentService.addAssignment(context); } public AssignmentContentEdit addAssignmentContent(String context) throws PermissionException { return assignmentService.addAssignmentContent(context); } // etc etc etc… } Injected delegate pass-through pass-through
Wrapping AssignmentService publicclass BasicAutoSubmitAssignmentService extends DecoratableAssignmentService implements AutoSubmitAssignmentService{ privateboolean shouldAutoSubmitByDefault; public AssignmentSubmission getSubmission(String submissionId) throws IdUnusedException, PermissionException { if (shouldAutoSubmit()) { AssignmentSubmission submission = assignmentService.getSubmission(submissionId); if (submission == null) { returnnull; } Assignment assignment = submission.getAssignment(); // is the assignment close time after now, // or is the assignment already submitted? if (assignment.getCloseTime().after(TimeService.newTime()) || submission.getSubmitted()) { return submission; } else { submit(submission, assignment.getCloseTime()); return assignmentService.getSubmission(submissionId); } } else { returnsuper.getSubmission(submissionId); } } publicboolean shouldAutoSubmit() { try { Properties toolProps = ToolManager.getCurrentPlacement().getConfig(); return Boolean.parseBoolean(toolProps.getProperty("autoSubmit")); // the NullPointer exception means the service was called without // executing within a tool context, so we fall back to the default. } catch (NullPointerException npe) { return shouldAutoSubmitByDefault; } } // ... Etc etc etc … } Auto-submit logic Placement-scoped Auto-submit behavior
Wrapping AssignmentService <beans> <bean id="org.sakaiproject.assignment.impl.AssignmentService" class="org.sakaiproject.assignment.impl.DbAssignmentService" init-method="init" destroy-method="destroy" singleton="true"> <property name="memoryService"> <ref bean="org.sakaiproject.memory.api.MemoryService"/> </property> <property name="sqlService"> <ref bean="org.sakaiproject.db.api.SqlService"/> </property> <!-- snip --> </bean> <bean id="org.sakaiproject.assignment.api.AssignmentService" class="edu.gatech.sakai.assignment.BasicAutoSubmitAssignmentService" singleton="true"> <property name="assignmentService"> <ref bean="org.sakaiproject.assignment.impl.AssignmentService"/> </property> <property name="shouldAutoSubmitByDefault"> <value>false</value> </property> </bean> </beans> Std bean config Decoration
Wrapping Gradebook publicclass GaTechDeleteSuppressingGradebookContextObserver implements EntityProducer, ContextObserver { private ContextObserver gradebookContextObserver; private EntityProducer gradebookEntityProducer; publicvoid init() { EntityManager.registerEntityProducer(this, null); } public Entity getEntity(Reference ref) { return gradebookEntityProducer.getEntity(ref); } publicvoid contextCreated(String context, boolean toolPlacement) { gradebookContextObserver.contextCreated(context, toolPlacement); } // disabled cascaded deletion by NOT passing this call through publicvoid contextDeleted(String context, boolean toolPlacment) { // do nothing } // … etc etc etc … } Technically, needn’t be the same object pass-throughs Disabled cascaded deletion
JMX • Banner services monitored and managed via JMX beans • Very easy to configure and deploy with Spring and Tomcat • Except that JMX RMI involves two ports, one of which floats • Deployed a slightly modified version of a workaround originally published by George Lindholm
Ant <property name="jmx.url" value="service:jmx:rmi://localhost:10889/jndi/rmi://localhost:10888/server" /> <target name="startJms" description="Show JMX Cluster state"> <jmx:open url="${jmx.url}"/> <jmx:invoke name="LDIS:name=JmsMessageReceiver" operation="start"/> <jmx:get name="LDIS:name=JmsMessageReceiver" attribute="Running" resultproperty="running" echo="false"/> <echo> LDIS:name=JmsMessageReceiver Running: ${running} </echo> </target>