diff --git a/courses-portlet-api/pom.xml b/courses-portlet-api/pom.xml
index 453c820..2237fbc 100644
--- a/courses-portlet-api/pom.xml
+++ b/courses-portlet-api/pom.xml
@@ -16,6 +16,10 @@
org.jvnet.jaxb2_commons
jaxb2-basics-runtime
+
+ commons-lang
+ commons-lang
+
diff --git a/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/CachingDegreeProgressDao.java b/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/CachingDegreeProgressDao.java
new file mode 100644
index 0000000..b642331
--- /dev/null
+++ b/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/CachingDegreeProgressDao.java
@@ -0,0 +1,93 @@
+/**
+ * Licensed to Jasig under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Jasig licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.portlet.degreeprogress.dao;
+
+import javax.portlet.PortletRequest;
+import net.sf.ehcache.Cache;
+import net.sf.ehcache.Element;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jasig.portlet.degreeprogress.model.xml.DegreeProgressReport;
+
+/**
+ * This {@link org.jasig.portlet.degreeprogress.dao.IDegreeProgressDao} implementation decorates one or more other DAOs and
+ * adds caching features to them. Whatever data the underlying DAOs provide
+ * will be stored in cache according to parameters specified in ehcache.xml.
+ * All caching is per-user.
+ *
+ * @author Chris Waymire, chris@waymire.net
+ */
+public class CachingDegreeProgressDao implements IDegreeProgressDao {
+ private final Log log = LogFactory.getLog(getClass());
+
+ /*
+ * Spring-wired dependencies
+ */
+
+ private IDegreeProgressDao enclosedDegreeProgressDao;
+
+ public void setEnclosedDegreeProgressDao(IDegreeProgressDao enclosedDegreeProgressDao) {
+ this.enclosedDegreeProgressDao = enclosedDegreeProgressDao;
+ }
+
+ private Cache progressReportCache;
+
+ public void setProgressReportCache(Cache progressReportCache) {
+ this.progressReportCache = progressReportCache;
+ }
+
+ @Override
+ public DegreeProgressReport getProgressReport(PortletRequest request) {
+ DegreeProgressReport rslt = null;
+ String cacheKey = getDegreeProgressCacheKey(request);
+ Element m = progressReportCache.get(cacheKey);
+
+ if (m != null) {
+ rslt = (DegreeProgressReport) m.getValue();
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Fetching new DegreeProgressReport from enclosedDegreeProgressDao for user '" + request.getRemoteUser() + "'");
+ }
+ rslt = enclosedDegreeProgressDao.getProgressReport(request);
+ m = new Element(cacheKey, rslt);
+ progressReportCache.put(m);
+ }
+ return rslt;
+ }
+
+ @Override
+ public Boolean getWebEnabled(PortletRequest request) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public WhatIfRequest createWhatIfRequest(PortletRequest request) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ @Override
+ public DegreeProgressReport getWhatIfReport(WhatIfRequest whatIfRequest) {
+ return null; //To change body of implemented methods use File | Settings | File Templates.
+ }
+
+ private String getDegreeProgressCacheKey(PortletRequest request) {
+ return CachingDegreeProgressDao.class.getName() + "." + request.getRemoteUser();
+
+ }
+}
diff --git a/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/mock/MockDegreeProgramDaoImpl.java b/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/mock/MockDegreeProgramDaoImpl.java
index aa0bb96..9c83d79 100644
--- a/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/mock/MockDegreeProgramDaoImpl.java
+++ b/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/mock/MockDegreeProgramDaoImpl.java
@@ -8,7 +8,6 @@
import org.jasig.portlet.degreeprogress.dao.ProgramInformation;
import org.springframework.stereotype.Service;
-@Service
public class MockDegreeProgramDaoImpl implements IDegreeProgramDao {
public DegreeProgramSummary getProgramSummary(PortletRequest request) {
diff --git a/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/mock/MockDegreeProgressDaoImpl.java b/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/mock/MockDegreeProgressDaoImpl.java
index b5c77ea..cf9101d 100644
--- a/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/mock/MockDegreeProgressDaoImpl.java
+++ b/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/mock/MockDegreeProgressDaoImpl.java
@@ -23,7 +23,6 @@
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Service;
-@Service
public class MockDegreeProgressDaoImpl implements IDegreeProgressDao, InitializingBean {
protected final Log log = LogFactory.getLog(getClass());
diff --git a/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/xml/HttpDegreeProgressDaoImpl.java b/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/xml/HttpDegreeProgressDaoImpl.java
new file mode 100644
index 0000000..5515fd2
--- /dev/null
+++ b/courses-portlet-dao/src/main/java/org/jasig/portlet/degreeprogress/dao/xml/HttpDegreeProgressDaoImpl.java
@@ -0,0 +1,167 @@
+/**
+ * Licensed to Jasig under one or more contributor license
+ * agreements. See the NOTICE file distributed with this work
+ * for additional information regarding copyright ownership.
+ * Jasig licenses this file to you under the Apache License,
+ * Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a
+ * copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.jasig.portlet.degreeprogress.dao.xml;
+
+import java.util.HashMap;
+import java.util.Map;
+import javax.portlet.PortletRequest;
+import javax.xml.bind.JAXBElement;
+import org.apache.commons.codec.binary.Base64;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.jasig.portlet.courses.util.IParameterEvaluator;
+import org.jasig.portlet.degreeprogress.dao.IDegreeProgressDao;
+import org.jasig.portlet.degreeprogress.dao.WhatIfRequest;
+import org.jasig.portlet.degreeprogress.model.StudentCourseRegistration;
+import org.jasig.portlet.degreeprogress.model.xml.Course;
+import org.jasig.portlet.degreeprogress.model.xml.CourseRequirement;
+import org.jasig.portlet.degreeprogress.model.xml.DegreeProgressReport;
+import org.jasig.portlet.degreeprogress.model.xml.DegreeRequirementSection;
+import org.jasig.portlet.degreeprogress.model.xml.GeneralRequirementType;
+import org.jasig.portlet.degreeprogress.model.xml.GpaRequirement;
+import org.jasig.portlet.degreeprogress.model.xml.Grade;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * HttpDegreeProgressDaoImpl retrieves degree progress from a Basic Authentication
+ * protected XML feed.
+ *
+ * @author Chris Waymire, chris@waymire.net
+ */
+public class HttpDegreeProgressDaoImpl implements IDegreeProgressDao {
+ private final Log log = LogFactory.getLog(getClass());
+
+ private IParameterEvaluator usernameEvaluator;
+
+ public void setUsernameEvaluator(IParameterEvaluator usernameEvaluator) {
+ this.usernameEvaluator = usernameEvaluator;
+ }
+
+ private IParameterEvaluator passwordEvaluator;
+
+ private Map urlParams = new HashMap();
+
+ public void setUrlParams(Map params) {
+ this.urlParams = params;
+ }
+
+ public void setPasswordEvaluator(IParameterEvaluator passwordEvaluator) {
+ this.passwordEvaluator = passwordEvaluator;
+ }
+
+ private RestTemplate restTemplate;
+
+ public void setRestTemplate(RestTemplate restTemplate) {
+ this.restTemplate = restTemplate;
+ }
+
+ private String degreeProgressUrlFormat = null;
+
+ public void setDegreeProgressUrlFormat(String urlFormat) {
+ this.degreeProgressUrlFormat = urlFormat;
+ }
+
+ @Override
+ public DegreeProgressReport getProgressReport(PortletRequest request) {
+ Map params = createParameters(request, urlParams);
+ if (log.isDebugEnabled()) {
+ log.debug("Invoking uri '" + degreeProgressUrlFormat + "' with the following parameters: " + params.toString());
+ }
+
+ HttpEntity> requestEntity = getRequestEntity(request);
+ HttpEntity response = restTemplate.exchange(
+ degreeProgressUrlFormat, HttpMethod.GET, requestEntity,
+ DegreeProgressReport.class, params);
+
+ DegreeProgressReport report = response.getBody();
+ for (DegreeRequirementSection section : report.getDegreeRequirementSections()) {
+ for (JAXBElement extends GeneralRequirementType> requirement : section.getGeneralRequirements()) {
+ GeneralRequirementType req = requirement.getValue();
+ if (req instanceof GpaRequirement) {
+ section.setRequiredGpa(((GpaRequirement) req).getRequiredGpa());
+ }
+ }
+ for (CourseRequirement req : section.getCourseRequirements()) {
+ for (Course course : req.getCourses()) {
+ StudentCourseRegistration registration = new StudentCourseRegistration();
+ registration.setCredits(course.getCredits());
+ registration.setSource(course.getSource());
+ registration.setSemester(course.getSemester());
+ registration.setCourse(course);
+ Grade grade = new Grade();
+ grade.setCode(course.getGrade().getCode());
+ registration.setGrade(grade);
+ req.getRegistrations().add(registration);
+ }
+ }
+ report.addSection(section);
+ }
+ return report;
+ }
+
+ @Override
+ public Boolean getWebEnabled(PortletRequest request) {
+ return Boolean.TRUE;
+ }
+
+ @Override
+ public WhatIfRequest createWhatIfRequest(PortletRequest request) {
+ return null;
+ }
+
+ @Override
+ public DegreeProgressReport getWhatIfReport(WhatIfRequest whatIfRequest) {
+ return null;
+ }
+
+ /**
+ * Get a request entity prepared for basic authentication.
+ */
+ protected HttpEntity> getRequestEntity(PortletRequest request) {
+
+ String username = usernameEvaluator.evaluate(request);
+ String password = passwordEvaluator.evaluate(request);
+
+ if (log.isDebugEnabled()) {
+ boolean hasPassword = password != null;
+ log.debug("Preparing HttpEntity for user '" + username + "' (password provided = " + hasPassword + ")");
+ }
+
+ HttpHeaders requestHeaders = new HttpHeaders();
+ String authString = username.concat(":").concat(password);
+ String encodedAuthString = new Base64().encodeToString(authString.getBytes());
+ requestHeaders.set("Authorization", "Basic ".concat(encodedAuthString));
+
+ HttpEntity> rslt = new HttpEntity
\ No newline at end of file
diff --git a/courses-portlet-webapp/src/main/webapp/icons/arrow_left.png b/courses-portlet-webapp/src/main/webapp/icons/arrow_left.png
new file mode 100644
index 0000000..5dc6967
Binary files /dev/null and b/courses-portlet-webapp/src/main/webapp/icons/arrow_left.png differ
diff --git a/pom.xml b/pom.xml
index 7893f0f..7b71756 100644
--- a/pom.xml
+++ b/pom.xml
@@ -63,7 +63,7 @@
commons-lang
commons-lang
- 2.3
+ 2.6
javax.portlet