001: /**********************************************************************************
002: * $URL: https://source.sakaiproject.org/svn/course-management/tags/sakai_2-4-1/cm-impl/hibernate-impl/impl/src/java/org/sakaiproject/coursemanagement/impl/job/CmSynchronizer.java $
003: * $Id: CmSynchronizer.java 17739 2006-11-01 18:36:41Z jholtzman@berkeley.edu $
004: ***********************************************************************************
005: *
006: * Copyright (c) 2006 The Sakai Foundation.
007: *
008: * Licensed under the Educational Community License, Version 1.0 (the "License");
009: * you may not use this file except in compliance with the License.
010: * You may obtain a copy of the License at
011: *
012: * http://www.opensource.org/licenses/ecl1.php
013: *
014: * Unless required by applicable law or agreed to in writing, software
015: * distributed under the License is distributed on an "AS IS" BASIS,
016: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
017: * See the License for the specific language governing permissions and
018: * limitations under the License.
019: *
020: **********************************************************************************/package org.sakaiproject.coursemanagement.impl.job;
021:
022: import java.io.IOException;
023: import java.io.InputStream;
024: import java.text.ParseException;
025: import java.text.SimpleDateFormat;
026: import java.util.Date;
027: import java.util.HashMap;
028: import java.util.HashSet;
029: import java.util.Iterator;
030: import java.util.List;
031: import java.util.Map;
032: import java.util.Set;
033:
034: import org.apache.commons.logging.Log;
035: import org.apache.commons.logging.LogFactory;
036: import org.jdom.Document;
037: import org.jdom.Element;
038: import org.jdom.JDOMException;
039: import org.jdom.input.SAXBuilder;
040: import org.jdom.xpath.XPath;
041: import org.sakaiproject.coursemanagement.api.AcademicSession;
042: import org.sakaiproject.coursemanagement.api.CanonicalCourse;
043: import org.sakaiproject.coursemanagement.api.CourseManagementAdministration;
044: import org.sakaiproject.coursemanagement.api.CourseManagementService;
045: import org.sakaiproject.coursemanagement.api.CourseOffering;
046: import org.sakaiproject.coursemanagement.api.CourseSet;
047: import org.sakaiproject.coursemanagement.api.Enrollment;
048: import org.sakaiproject.coursemanagement.api.EnrollmentSet;
049: import org.sakaiproject.coursemanagement.api.Meeting;
050: import org.sakaiproject.coursemanagement.api.Membership;
051: import org.sakaiproject.coursemanagement.api.Section;
052:
053: /**
054: * Synchronizes the state of the local CourseManagementService with an external
055: * data source.
056: *
057: * @author <a href="mailto:jholtzman@berkeley.edu">Josh Holtzman</a>
058: *
059: */
060: public abstract class CmSynchronizer {
061: private static final Log log = LogFactory
062: .getLog(CmSynchronizer.class);
063:
064: protected CourseManagementService cmService;
065: protected CourseManagementAdministration cmAdmin;
066:
067: protected abstract InputStream getXmlInputStream();
068:
069: public synchronized void syncAllCmObjects() {
070: long start = System.currentTimeMillis();
071: if (log.isInfoEnabled())
072: log.info("Starting CM synchronization");
073: // Load the xml document from the xml stream
074: InputStream in = null;
075: Document doc = null;
076: try {
077: in = getXmlInputStream();
078: doc = new SAXBuilder().build(in);
079: if (log.isDebugEnabled())
080: log
081: .debug("XML Document built successful from input stream");
082: } catch (Exception e) {
083: log
084: .error("Could not build a jdom document from the xml input stream... "
085: + e);
086: // Close the input stream
087: if (in != null) {
088: try {
089: in.close();
090: } catch (IOException ioe) {
091: log.error("Unable to close input stream " + in);
092: }
093: }
094: throw new RuntimeException(e);
095: }
096:
097: try {
098: reconcileAcademicSessions(doc);
099: reconcileCanonicalCourses(doc);
100: reconcileCourseOfferings(doc);
101: reconcileSections(doc);
102: reconcileEnrollmentSets(doc);
103: reconcileCourseSets(doc);
104: } finally {
105: // Close the input stream
106: if (in != null) {
107: try {
108: in.close();
109: } catch (IOException ioe) {
110: log.error("Unable to close input stream " + in);
111: }
112: }
113: }
114:
115: if (log.isInfoEnabled())
116: log.info("Finished CM synchronization in "
117: + (System.currentTimeMillis() - start) + " ms");
118: }
119:
120: protected void reconcileAcademicSessions(Document doc) {
121: long start = System.currentTimeMillis();
122: if (log.isInfoEnabled())
123: log.info("Reconciling AcademicSessions");
124:
125: // Get a list of all existing academic sessions
126: List existing = cmService.getAcademicSessions();
127:
128: // Create a map of existing AcademicSession EIDs to AcademicSessions
129: Map academicSessionMap = new HashMap();
130: for (Iterator iter = existing.iterator(); iter.hasNext();) {
131: AcademicSession as = (AcademicSession) iter.next();
132: academicSessionMap.put(as.getEid(), as);
133: }
134:
135: // Find the academic sessions specified in the xml doc and reconcile them
136: try {
137: XPath docsPath = XPath
138: .newInstance("/cm-data/academic-sessions/academic-session");
139: List items = docsPath.selectNodes(doc);
140: // Add or update each of the academic sessions specified in the xml
141: for (Iterator iter = items.iterator(); iter.hasNext();) {
142: Element element = (Element) iter.next();
143: String eid = element.getChildText("eid");
144: if (log.isDebugEnabled())
145: log.debug("Found academic section to reconcile: "
146: + eid);
147: if (academicSessionMap.containsKey(eid)) {
148: updateAcademicSession(
149: (AcademicSession) academicSessionMap
150: .get(eid), element);
151: } else {
152: addAcademicSession(element);
153: }
154: }
155: } catch (JDOMException jde) {
156: log.error(jde);
157: }
158:
159: if (log.isInfoEnabled())
160: log.info("Finished reconciling AcademicSessions in "
161: + (System.currentTimeMillis() - start) + " ms");
162: }
163:
164: protected void addAcademicSession(Element element) {
165: String eid = element.getChildText("eid");
166: if (log.isDebugEnabled())
167: log.debug("Adding AcademicSession + " + eid);
168: String title = element.getChildText("title");
169: String description = element.getChildText("description");
170: Date startDate = getDate(element.getChildText("start-date"));
171: Date endDate = getDate(element.getChildText("end-date"));
172: cmAdmin.createAcademicSession(eid, title, description,
173: startDate, endDate);
174: }
175:
176: protected void updateAcademicSession(AcademicSession session,
177: Element element) {
178: if (log.isDebugEnabled())
179: log.debug("Updating AcademicSession + " + session.getEid());
180: session.setTitle(element.getChildText("title"));
181: session.setDescription(element.getChildText("description"));
182: session
183: .setStartDate(getDate(element
184: .getChildText("start-date")));
185: session.setEndDate(getDate(element.getChildText("end-date")));
186: cmAdmin.updateAcademicSession(session);
187: }
188:
189: protected void reconcileCanonicalCourses(Document doc) {
190: long start = System.currentTimeMillis();
191: if (log.isInfoEnabled())
192: log.info("Reconciling CanonicalCourses");
193:
194: try {
195: XPath docsPath = XPath
196: .newInstance("/cm-data/canonical-courses/canonical-course");
197: List items = docsPath.selectNodes(doc);
198: if (log.isDebugEnabled())
199: log.debug("Found " + items.size()
200: + " canonical courses to reconcile");
201:
202: // Add or update each of the canonical courses specified in the xml
203: for (Iterator iter = items.iterator(); iter.hasNext();) {
204: Element element = (Element) iter.next();
205: String eid = element.getChildText("eid");
206: if (log.isDebugEnabled())
207: log.debug("Reconciling canonical course " + eid);
208:
209: if (cmService.isCanonicalCourseDefined(eid)) {
210: updateCanonicalCourse(cmService
211: .getCanonicalCourse(eid), element);
212: } else {
213: addCanonicalCourse(element);
214: }
215: }
216: } catch (JDOMException jde) {
217: log.error(jde);
218: }
219:
220: if (log.isInfoEnabled())
221: log.info("Finished reconciling CanonicalCourses in "
222: + (System.currentTimeMillis() - start) + " ms");
223: }
224:
225: protected void addCanonicalCourse(Element element) {
226: String eid = element.getChildText("eid");
227: if (log.isDebugEnabled())
228: log.debug("Adding CanonicalCourse + " + eid);
229: String title = element.getChildText("title");
230: String description = element.getChildText("description");
231: cmAdmin.createCanonicalCourse(eid, title, description);
232: }
233:
234: protected void updateCanonicalCourse(
235: CanonicalCourse canonicalCourse, Element element) {
236: if (log.isDebugEnabled())
237: log.debug("Updating CanonicalCourse + "
238: + canonicalCourse.getEid());
239: canonicalCourse.setTitle(element.getChildText("title"));
240: canonicalCourse.setDescription(element
241: .getChildText("description"));
242: cmAdmin.updateCanonicalCourse(canonicalCourse);
243: }
244:
245: protected void reconcileCourseOfferings(Document doc) {
246: long start = System.currentTimeMillis();
247: if (log.isInfoEnabled())
248: log.info("Reconciling CourseOfferings");
249:
250: try {
251: XPath docsPath = XPath
252: .newInstance("/cm-data/course-offerings/course-offering");
253: List items = docsPath.selectNodes(doc);
254: if (log.isDebugEnabled())
255: log.debug("Found " + items.size()
256: + " course offerings to reconcile");
257:
258: // Add or update each of the course offerings specified in the xml
259: for (Iterator iter = items.iterator(); iter.hasNext();) {
260: Element element = (Element) iter.next();
261: String eid = element.getChildText("eid");
262: if (log.isDebugEnabled())
263: log.debug("Reconciling course offering " + eid);
264:
265: CourseOffering courseOffering = null;
266: if (cmService.isCourseOfferingDefined(eid)) {
267: courseOffering = updateCourseOffering(cmService
268: .getCourseOffering(eid), element);
269: } else {
270: courseOffering = addCourseOffering(element);
271: }
272:
273: // Update the members
274: Element members = element.getChild("members");
275: if (members != null) {
276: updateCourseOfferingMembers(members, courseOffering);
277: }
278:
279: }
280: } catch (JDOMException jde) {
281: log.error(jde);
282: }
283: if (log.isInfoEnabled())
284: log.info("Finished reconciling CourseOfferings in "
285: + (System.currentTimeMillis() - start) + " ms");
286: }
287:
288: protected CourseOffering updateCourseOffering(
289: CourseOffering courseOffering, Element element) {
290: if (log.isDebugEnabled())
291: log.debug("Updating CourseOffering + "
292: + courseOffering.getEid());
293: AcademicSession newAcademicSession = cmService
294: .getAcademicSession(element
295: .getChildText("academic-session-eid"));
296: courseOffering.setTitle(element.getChildText("title"));
297: courseOffering.setDescription(element
298: .getChildText("description"));
299: courseOffering.setStatus(element.getChildText("status"));
300: courseOffering.setAcademicSession(newAcademicSession);
301: courseOffering.setStartDate(getDate(element
302: .getChildText("start-date")));
303: courseOffering.setEndDate(getDate(element
304: .getChildText("end-date")));
305:
306: // Note: we can't update a course offering's canonical course. This seems reasonable.
307:
308: cmAdmin.updateCourseOffering(courseOffering);
309: return courseOffering;
310: }
311:
312: protected CourseOffering addCourseOffering(Element element) {
313: String eid = element.getChildText("eid");
314: if (log.isDebugEnabled())
315: log.debug("Adding CourseOffering + " + eid);
316: String title = element.getChildText("title");
317: String description = element.getChildText("description");
318: String status = element.getChildText("status");
319: String academicSessionEid = element
320: .getChildText("academic-session-eid");
321: String canonicalCourseEid = element
322: .getChildText("canonical-course-eid");
323: Date startDate = getDate(element.getChildText("start-date"));
324: Date endDate = getDate(element.getChildText("end-date"));
325: return cmAdmin.createCourseOffering(eid, title, description,
326: status, academicSessionEid, canonicalCourseEid,
327: startDate, endDate);
328: }
329:
330: protected void updateCourseOfferingMembers(Element membersElement,
331: CourseOffering courseOffering) {
332: Set existingMembers = cmService
333: .getCourseOfferingMemberships(courseOffering.getEid());
334:
335: // Build a map of existing member userEids to Memberships
336: Map existingMemberMap = new HashMap(existingMembers.size());
337: for (Iterator iter = existingMembers.iterator(); iter.hasNext();) {
338: Membership member = (Membership) iter.next();
339: existingMemberMap.put(member.getUserId(), member);
340: }
341:
342: // Keep track of the new members userEids, and add/update them
343: Set newMembers = new HashSet();
344: List memberElements = membersElement.getChildren("member");
345: for (Iterator iter = memberElements.iterator(); iter.hasNext();) {
346: Element memberElement = (Element) iter.next();
347: String userEid = memberElement.getAttributeValue("userEid");
348: String role = memberElement.getAttributeValue("role");
349: String status = memberElement.getAttributeValue("status");
350: newMembers.add(cmAdmin.addOrUpdateCourseOfferingMembership(
351: userEid, role, courseOffering.getEid(), status));
352: }
353:
354: // For everybody not in the newMembers set, remove their memberships
355: existingMembers.removeAll(newMembers);
356: for (Iterator iter = existingMembers.iterator(); iter.hasNext();) {
357: Membership member = (Membership) iter.next();
358: cmAdmin.removeCourseOfferingMembership(member.getUserId(),
359: courseOffering.getEid());
360: }
361: }
362:
363: protected void reconcileEnrollmentSets(Document doc) {
364: long start = System.currentTimeMillis();
365: if (log.isInfoEnabled())
366: log.info("Reconciling EnrollmentSets");
367:
368: try {
369: XPath docsPath = XPath
370: .newInstance("/cm-data/enrollment-sets/enrollment-set");
371: List items = docsPath.selectNodes(doc);
372: if (log.isDebugEnabled())
373: log.debug("Found " + items.size()
374: + " enrollment sets to reconcile");
375:
376: // Add or update each of the enrollment sets specified in the xml
377: for (Iterator iter = items.iterator(); iter.hasNext();) {
378: Element element = (Element) iter.next();
379: String eid = element.getChildText("eid");
380: if (log.isDebugEnabled())
381: log.debug("Reconciling enrollment set " + eid);
382:
383: EnrollmentSet enr = null;
384: if (cmService.isEnrollmentSetDefined(eid)) {
385: enr = updateEnrollmentSet(cmService
386: .getEnrollmentSet(eid), element);
387: } else {
388: enr = addEnrollmentSet(element);
389: }
390: reconcileEnrollments(element.getChild("enrollments"),
391: enr);
392: reconcileOfficialInstructors(element, enr);
393: }
394: } catch (JDOMException jde) {
395: log.error(jde);
396: }
397:
398: if (log.isInfoEnabled())
399: log.info("Finished reconciling EnrollmentSets in "
400: + (System.currentTimeMillis() - start) + " ms");
401: }
402:
403: protected void reconcileEnrollments(Element enrollmentsElement,
404: EnrollmentSet enrollmentSet) {
405: List newEnrollmentElements = enrollmentsElement
406: .getChildren("enrollment");
407: Set newUserEids = new HashSet();
408: Set existingEnrollments = cmService
409: .getEnrollments(enrollmentSet.getEid());
410:
411: for (Iterator iter = newEnrollmentElements.iterator(); iter
412: .hasNext();) {
413: Element enrollmentElement = (Element) iter.next();
414: String userEid = enrollmentElement.getChildText("userEid");
415: newUserEids.add(userEid);
416: String status = enrollmentElement.getChildText("status");
417: String credits = enrollmentElement.getChildText("credits");
418: String gradingScheme = enrollmentElement
419: .getChildText("grading-scheme");
420: cmAdmin.addOrUpdateEnrollment(userEid, enrollmentSet
421: .getEid(), status, credits, gradingScheme);
422: }
423:
424: for (Iterator iter = existingEnrollments.iterator(); iter
425: .hasNext();) {
426: Enrollment existingEnr = (Enrollment) iter.next();
427: if (!newUserEids.contains(existingEnr.getUserId())) {
428: // Drop this enrollment
429: cmAdmin.removeEnrollment(existingEnr.getUserId(),
430: enrollmentSet.getEid());
431: }
432: }
433: }
434:
435: protected void reconcileOfficialInstructors(Element esElement,
436: EnrollmentSet enrollmentSet) {
437: List newInstructorElements = esElement.getChild(
438: "official-instructors").getChildren(
439: "official-instructor");
440: Set newUserEids = new HashSet();
441:
442: for (Iterator iter = newInstructorElements.iterator(); iter
443: .hasNext();) {
444: String userEid = ((Element) iter.next()).getText();
445: newUserEids.add(userEid);
446: }
447: Set officialInstructors = enrollmentSet
448: .getOfficialInstructors();
449: if (officialInstructors == null) {
450: officialInstructors = new HashSet();
451: enrollmentSet.setOfficialInstructors(officialInstructors);
452: }
453: officialInstructors.clear();
454: officialInstructors.addAll(newUserEids);
455: cmAdmin.updateEnrollmentSet(enrollmentSet);
456: }
457:
458: protected Set getChildValues(Element element) {
459: Set childValues = new HashSet();
460: for (Iterator elementIter = element.getChildren().iterator(); elementIter
461: .hasNext();) {
462: Element childElement = (Element) elementIter.next();
463: childValues.add(childElement.getText());
464: }
465: return childValues;
466: }
467:
468: protected void updateOfficialInstructors(EnrollmentSet enr,
469: Element element) {
470:
471: }
472:
473: protected EnrollmentSet addEnrollmentSet(Element element) {
474: String eid = element.getChildText("eid");
475: if (log.isDebugEnabled())
476: log.debug("Adding EnrollmentSet + " + eid);
477: String title = element.getChildText("title");
478: String description = element.getChildText("description");
479: String category = element.getChildText("category");
480: String courseOfferingEid = element
481: .getChildText("course-offering-eid");
482: String defaultEnrollmentCredits = element
483: .getChildText("default-enrollment-credits");
484: return cmAdmin.createEnrollmentSet(eid, title, description,
485: category, defaultEnrollmentCredits, courseOfferingEid,
486: null);
487: }
488:
489: protected EnrollmentSet updateEnrollmentSet(
490: EnrollmentSet enrollmentSet, Element element) {
491: if (log.isDebugEnabled())
492: log.debug("Updating EnrollmentSet + "
493: + enrollmentSet.getEid());
494: enrollmentSet.setTitle(element.getChildText("title"));
495: enrollmentSet.setDescription(element
496: .getChildText("description"));
497: enrollmentSet.setCategory(element.getChildText("category"));
498: enrollmentSet.setDefaultEnrollmentCredits(element
499: .getChildText("default-enrollment-credits"));
500: // Note: It is not possible to change the course offering, but this seems OK.
501:
502: cmAdmin.updateEnrollmentSet(enrollmentSet);
503: return enrollmentSet;
504: }
505:
506: protected void reconcileSections(Document doc) {
507: long start = System.currentTimeMillis();
508: if (log.isInfoEnabled())
509: log.info("Reconciling Sections");
510:
511: try {
512: XPath docsPath = XPath
513: .newInstance("/cm-data/sections/section");
514: List items = docsPath.selectNodes(doc);
515: if (log.isDebugEnabled())
516: log.debug("Found " + items.size()
517: + " sections to reconcile");
518:
519: // Add or update each of the sections specified in the xml
520: for (Iterator iter = items.iterator(); iter.hasNext();) {
521: Element element = (Element) iter.next();
522: String eid = element.getChildText("eid");
523: if (log.isDebugEnabled())
524: log.debug("Reconciling section " + eid);
525:
526: Section section = null;
527: if (cmService.isSectionDefined(eid)) {
528: section = updateSection(cmService.getSection(eid),
529: element);
530: } else {
531: section = addSection(element);
532: }
533:
534: // Now update the meetings on this section
535: Set meetingTimes = section.getMeetings();
536: if (meetingTimes == null) {
537: meetingTimes = new HashSet();
538: section.setMeetings(meetingTimes);
539: }
540:
541: Element meetingsElement = element.getChild("meetings");
542: for (Iterator meetingIter = meetingsElement
543: .getChildren().iterator(); meetingIter
544: .hasNext();) {
545: Element meetingElement = (Element) meetingIter
546: .next();
547: String location = meetingElement
548: .getChildText("location");
549: // TODO Sync start and finish times
550: // String time = meetingElement.getChildText("time");
551: String notes = meetingElement.getChildText("notes");
552: Meeting meeting = cmAdmin.newSectionMeeting(eid,
553: location, null, null, notes);
554: meetingTimes.add(meeting);
555: }
556: cmAdmin.updateSection(section);
557:
558: // Update the members
559: Element members = element.getChild("members");
560: if (members != null) {
561: updateSectionMembers(members, section);
562: }
563: }
564: } catch (JDOMException jde) {
565: log.error(jde);
566: }
567: if (log.isInfoEnabled())
568: log.info("Finished reconciling Sections in "
569: + (System.currentTimeMillis() - start) + " ms");
570: }
571:
572: protected Section updateSection(Section section, Element element) {
573: if (log.isDebugEnabled())
574: log.debug("Updating Section + " + section.getEid());
575: section.setTitle(element.getChildText("title"));
576: section.setDescription(element.getChildText("description"));
577: section.setCategory(element.getChildText("category"));
578: if (cmService.isSectionDefined(element
579: .getChildText("parent-section-eid"))) {
580: section.setParent(cmService.getSection(element
581: .getChildText("parent-section-eid")));
582: }
583: // Note: There's no way to change the course offering. This makes sense, though.
584:
585: if (cmService.isEnrollmentSetDefined(element
586: .getChildText("enrollment-set-eid"))) {
587: section.setEnrollmentSet(cmService.getEnrollmentSet(element
588: .getChildText("enrollment-set-eid")));
589: }
590: cmAdmin.updateSection(section);
591: return section;
592: }
593:
594: protected Section addSection(Element element) {
595: String eid = element.getChildText("eid");
596: if (log.isDebugEnabled())
597: log.debug("Adding Section + " + eid);
598: String title = element.getChildText("title");
599: String description = element.getChildText("description");
600: String category = element.getChildText("category");
601: String parentSectionEid = null;
602: String parentIdFromXml = element
603: .getChildText("parent-section-eid");
604: if (parentIdFromXml != null && !"".equals(parentIdFromXml)) {
605: parentSectionEid = parentIdFromXml;
606: }
607: String courseOfferingEid = element
608: .getChildText("course-offering-eid");
609: String enrollmentSetEid = null;
610: String enrollmentSetEidFromXml = element
611: .getChildText("enrollment-set-eid");
612: if (cmService.isEnrollmentSetDefined(enrollmentSetEidFromXml)) {
613: enrollmentSetEid = enrollmentSetEidFromXml;
614: }
615: return cmAdmin.createSection(eid, title, description, category,
616: parentSectionEid, courseOfferingEid, enrollmentSetEid);
617: }
618:
619: protected void updateSectionMembers(Element membersElement,
620: Section section) {
621: Set existingMembers = cmService.getSectionMemberships(section
622: .getEid());
623:
624: // Build a map of existing member userEids to Memberships
625: Map existingMemberMap = new HashMap(existingMembers.size());
626: for (Iterator iter = existingMembers.iterator(); iter.hasNext();) {
627: Membership member = (Membership) iter.next();
628: existingMemberMap.put(member.getUserId(), member);
629: }
630:
631: // Keep track of the new members userEids, and add/update them
632: Set newMembers = new HashSet();
633: List memberElements = membersElement.getChildren("member");
634: for (Iterator iter = memberElements.iterator(); iter.hasNext();) {
635: Element memberElement = (Element) iter.next();
636: String userEid = memberElement.getAttributeValue("userEid");
637: String role = memberElement.getAttributeValue("role");
638: String status = memberElement.getAttributeValue("status");
639: newMembers.add(cmAdmin.addOrUpdateSectionMembership(
640: userEid, role, section.getEid(), status));
641: }
642:
643: // For everybody not in the newMembers set, remove their memberships
644: existingMembers.removeAll(newMembers);
645: for (Iterator iter = existingMembers.iterator(); iter.hasNext();) {
646: Membership member = (Membership) iter.next();
647: cmAdmin.removeSectionMembership(member.getUserId(), section
648: .getEid());
649: }
650: }
651:
652: protected void reconcileCourseSets(Document doc) {
653: long start = System.currentTimeMillis();
654: if (log.isInfoEnabled())
655: log.info("Reconciling CourseSets");
656:
657: try {
658: XPath docsPath = XPath
659: .newInstance("/cm-data/course-sets/course-set");
660: List items = docsPath.selectNodes(doc);
661: if (log.isDebugEnabled())
662: log.debug("Found " + items.size()
663: + " course sets to reconcile");
664:
665: // Add or update each of the course offerings specified in the xml
666: for (Iterator iter = items.iterator(); iter.hasNext();) {
667: Element element = (Element) iter.next();
668: String eid = element.getChildText("eid");
669: if (log.isDebugEnabled())
670: log.debug("Reconciling course set " + eid);
671:
672: CourseSet courseSet = null;
673: if (cmService.isCourseSetDefined(eid)) {
674: courseSet = updateCourseSet(cmService
675: .getCourseSet(eid), element);
676: } else {
677: courseSet = addCourseSet(element);
678: }
679:
680: // Update the members
681: Element members = element.getChild("members");
682: if (members != null) {
683: updateCourseSetMembers(members, courseSet);
684: }
685: }
686: } catch (JDOMException jde) {
687: log.error(jde);
688: }
689: if (log.isInfoEnabled())
690: log.info("Finished reconciling CourseSets in "
691: + (System.currentTimeMillis() - start) + " ms");
692: }
693:
694: protected void updateCourseSetMembers(Element membersElement,
695: CourseSet courseSet) {
696: Set existingMembers = cmService
697: .getCourseSetMemberships(courseSet.getEid());
698:
699: // Build a map of existing member userEids to Memberships
700: Map existingMemberMap = new HashMap(existingMembers.size());
701: for (Iterator iter = existingMembers.iterator(); iter.hasNext();) {
702: Membership member = (Membership) iter.next();
703: existingMemberMap.put(member.getUserId(), member);
704: }
705:
706: // Keep track of the new members userEids, and add/update them
707: Set newMembers = new HashSet();
708: List memberElements = membersElement.getChildren("member");
709: for (Iterator iter = memberElements.iterator(); iter.hasNext();) {
710: Element memberElement = (Element) iter.next();
711: String userEid = memberElement.getAttributeValue("userEid");
712: String role = memberElement.getAttributeValue("role");
713: String status = memberElement.getAttributeValue("status");
714: newMembers.add(cmAdmin.addOrUpdateCourseSetMembership(
715: userEid, role, courseSet.getEid(), status));
716: }
717:
718: // For everybody not in the newMembers set, remove their memberships
719: existingMembers.removeAll(newMembers);
720: for (Iterator iter = existingMembers.iterator(); iter.hasNext();) {
721: Membership member = (Membership) iter.next();
722: cmAdmin.removeCourseSetMembership(member.getUserId(),
723: courseSet.getEid());
724: }
725: }
726:
727: protected CourseSet updateCourseSet(CourseSet courseSet,
728: Element element) {
729: if (log.isDebugEnabled())
730: log.debug("Updating CourseSet + " + courseSet.getEid());
731: courseSet.setTitle(element.getChildText("title"));
732: courseSet.setDescription(element.getChildText("description"));
733: courseSet.setCategory(element.getChildText("category"));
734: String parentEid = element.getChildText("parent-course-set");
735: if (cmService.isCourseSetDefined(parentEid)) {
736: CourseSet parent = cmService.getCourseSet(parentEid);
737: courseSet.setParent(parent);
738: }
739: cmAdmin.updateCourseSet(courseSet);
740: return courseSet;
741: }
742:
743: protected CourseSet addCourseSet(Element element) {
744: String eid = element.getChildText("eid");
745: if (log.isDebugEnabled())
746: log.debug("Adding CourseSet + " + eid);
747: String title = element.getChildText("title");
748: String description = element.getChildText("description");
749: String category = element.getChildText("category");
750: String parentEid = null;
751: String parentFromXml = element
752: .getChildText("parent-course-set");
753: if (parentFromXml != null && !"".equals(parentFromXml)) {
754: parentEid = parentFromXml;
755: }
756: return cmAdmin.createCourseSet(eid, title, description,
757: category, parentEid);
758: }
759:
760: protected Date getDate(String str) {
761: if (str == null || "".equals(str)) {
762: return null;
763: }
764: SimpleDateFormat df = new SimpleDateFormat("M/d/yyyy");
765: try {
766: return df.parse(str);
767: } catch (ParseException pe) {
768: log.warn("Invalid date: " + str);
769: return null;
770: }
771: }
772:
773: // Dependency Injection
774:
775: public void setCmAdmin(CourseManagementAdministration cmAdmin) {
776: this .cmAdmin = cmAdmin;
777: }
778:
779: public void setCmService(CourseManagementService cmService) {
780: this.cmService = cmService;
781: }
782: }
|