/*
* #%L
* Course Signup Webapp
* %%
* Copyright (C) 2010 - 2013 University of Oxford
* %%
* Licensed under the Educational Community 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://opensource.org/licenses/ecl2
*
* 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.
* #L%
*/
package uk.ac.ox.oucs.vle.resources;
import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.codehaus.jackson.JsonEncoding;
import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.JsonGenerationException;
import org.codehaus.jackson.JsonGenerator;
import org.codehaus.jackson.map.JsonMappingException;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.map.annotate.JsonView;
import org.codehaus.jackson.map.type.TypeFactory;
import uk.ac.ox.oucs.vle.*;
import uk.ac.ox.oucs.vle.CourseSignupService.Range;
import uk.ac.ox.oucs.vle.resources.Views.Flat;
import javax.inject.Inject;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.core.Response.Status;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;
//@Path("/course/")
@Path("course{cobomo:(/cobomo)?}")
public class CourseResource {
private static final Log log = LogFactory.getLog(CourseResource.class);
@Inject
private CourseSignupService courseService;
@Inject
private SearchService searchService;
@Inject
private SakaiProxy sakaiProxy;
private JsonFactory jsonFactory = new JsonFactory();
// This is a direct binding to Jackson. Really we should be using EntityProviders
@Inject
private ObjectMapper objectMapper;
@Path("/{id}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public StreamingOutput getCourse(@PathParam("id") final String courseId, @QueryParam("range") final Range range) {
final CourseGroup course = courseService.getCourseGroup(courseId, range);
if (course == null) {
throw new WebAppNotFoundException();
}
return new GroupStreamingOutput(course);
}
@Path("/{id}")
@GET
// Commented because of a problem with MSIE Accept Headers (See WL-2563)
// @Produces({MediaType.APPLICATION_XHTML_XML, MediaType.TEXT_HTML})
public String getCoursePage(
@PathParam("id") final String courseId,
@QueryParam("range") final Range range,
@Context final HttpServletRequest request,
@Context final HttpServletResponse response) throws ServletException, IOException {
RequestDispatcher dispatcher = request.getRequestDispatcher("/pages/static/course.jsp");
request.setAttribute("openCourse", courseId);
dispatcher.forward(request, response);
return "";
}
@Path("/all")
@GET
@Produces(MediaType.APPLICATION_JSON)
public StreamingOutput getCourses(@QueryParam("range") final Range range) {
boolean externalUser = false;
if (sakaiProxy.isAnonymousUser()) {
externalUser = true;
}
final Map<String, String> departments = courseService.getDepartments();
final List<CourseGroup> groups = courseService.search("", range, externalUser);
if (groups == null) {
throw new WebAppNotFoundException();
}
return new AllGroupsStreamingOutput(departments, groups);
}
/**
* This gets all the courses for a department that have upcoming
* parts.
* @param deptId The department to load the courses for.
* @return An array of jsTree nodes.
*/
@Path("/dept/{deptId}")
@Produces(MediaType.APPLICATION_JSON)
@GET
public StreamingOutput getCoursesUpcoming(@PathParam("deptId") final String deptId, @QueryParam("components") final Range range) {
boolean externalUser = false;
if (sakaiProxy.isAnonymousUser()) {
externalUser = true;
}
if (isProvider(deptId)) {
if (range.equals(Range.PREVIOUS)) {
List<CourseGroup> courses = courseService.getCourseGroupsByDept(deptId, range, externalUser);
return new GroupsStreamingOutput(Collections.<SubUnit>emptyList(), courses, deptId, range.name(), false);
} else {
List<SubUnit> subUnits = courseService.getSubUnitsByDept(deptId);
List<CourseGroup> courses = courseService.getCourseGroupsByDept(deptId, range, externalUser);
List<CourseGroup> previous = courseService.getCourseGroupsByDept(deptId, Range.PREVIOUS, externalUser);
return new GroupsStreamingOutput(subUnits, courses, deptId, range.name(), !previous.isEmpty());
}
} else {
if (range.equals(Range.PREVIOUS)) {
List<CourseGroup> courses = courseService.getCourseGroupsBySubUnit(deptId, range, externalUser);
return new GroupsStreamingOutput(Collections.<SubUnit>emptyList(), courses, deptId, range.name(), false);
} else {
List<CourseGroup> courses = courseService.getCourseGroupsBySubUnit(deptId, range, externalUser);
List<CourseGroup> previous = courseService.getCourseGroupsBySubUnit(deptId, Range.PREVIOUS, externalUser);
return new GroupsStreamingOutput(Collections.<SubUnit>emptyList(), courses, deptId, range.name(), !previous.isEmpty());
}
}
}
@Path("/admin")
@GET
@Produces(MediaType.APPLICATION_JSON)
@JsonView(Flat.class)
public Response getAdminCourse() throws JsonGenerationException, JsonMappingException, IOException {
if(sakaiProxy.isAnonymousUser()) {
throw new WebAppForbiddenException();
}
List <CourseGroup> groups = courseService.getAdministering();
GenericEntity<List<CourseGroup>> genericEntity = new GenericEntity<List<CourseGroup>>(groups) {};
return Response.ok(genericEntity).build();
}
@Path("/lecture")
@GET
@Produces(MediaType.APPLICATION_JSON)
@JsonView(Flat.class)
public Response getLectureCourse() throws JsonGenerationException, JsonMappingException, IOException {
if (sakaiProxy.isAnonymousUser()) {
throw new WebAppForbiddenException();
}
List<CourseGroup> groups = courseService.getLecturing();
GenericEntity<List<CourseGroup>> genericEntity = new GenericEntity<List<CourseGroup>>(groups) {};
return Response.ok(genericEntity).build();
}
@Path("/search")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response setCourses(@QueryParam("terms") String terms) throws JsonGenerationException, JsonMappingException, IOException {
if (sakaiProxy.isAnonymousUser()) {
throw new WebAppForbiddenException();
}
if (terms == null) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity("No query parameter terms supplied").build());
}
List<CourseGroup> groups = courseService.search(terms, Range.UPCOMING, false);
return Response.ok(objectMapper.typedWriter(TypeFactory.collectionType(List.class, CourseGroup.class)).writeValueAsString(groups)).build();
}
@Path("/solr/{command}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public StreamingOutput setSolr(@PathParam("command") final String command, @Context UriInfo uriInfo)
throws JsonGenerationException, JsonMappingException, IOException {
if (!"select".equals(command)) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity("command not supported").build());
}
String query = uriInfo.getRequestUri().getRawQuery();
if (null == query) {
throw new WebApplicationException(Response.status(Status.BAD_REQUEST).entity("invalid query").build());
}
ResultsWrapper results;
try {
results = searchService.select(query);
return new StringStreamingOutput(results);
} catch (IOException e) {
log.error(e.getMessage(), e);
throw new WebApplicationException(Response.serverError().entity("Search not currently available.").build());
}
}
@Path("/calendar")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getCourseCalendar() throws JsonGenerationException, JsonMappingException, IOException {
boolean externalUser = false;
if (sakaiProxy.isAnonymousUser()) {
externalUser = true;
}
List <CourseGroup> groups = courseService.getCourseCalendar(externalUser, null);
// TODO Just return the coursegroups (no nested objects).
return Response.ok(objectMapper.typedWriter(TypeFactory.collectionType(List.class, CourseGroup.class)).writeValueAsString(groups)).build();
}
@Path("/nodates")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Response getCourseNoDates() throws JsonGenerationException, JsonMappingException, IOException {
boolean externalUser = false;
if (sakaiProxy.isAnonymousUser()) {
externalUser = true;
}
List <CourseGroup> groups = courseService.getCourseNoDates(externalUser, null);
// TODO Just return the coursegroups (no nested objects).
return Response.ok(objectMapper.typedWriter(TypeFactory.collectionType(List.class, CourseGroup.class)).writeValueAsString(groups)).build();
}
@Path("/hide")
@POST
public Response hide(@FormParam("courseId")String courseId, @FormParam("hideCourse")Boolean hideCourse) {
courseService.setHideCourse(courseId, hideCourse);
return Response.ok().build();
}
/**
* Formats a duration sensibly.
* @param remaining Time remaining in milliseconds.
* @return a String roughly representing the duration.
*/
private String formatDuration(long remaining) {
if (remaining < 1000) {
return "< 1 second";
} else if (remaining < 60000) {
return remaining / 1000 + " seconds";
} else if (remaining < 3600000) {
return remaining / 60000 + " minutes";
} else if (remaining < 86400000) {
return remaining / 3600000 + " hours";
} else {
return remaining / 86400000 + " days";
}
}
private CourseSummary summary(Date now, CourseGroup courseGroup) {
// Calculate the summary based on the available components.
if (courseGroup.getComponents().isEmpty()) {
return new CourseSummary("none available", Collections.singletonList(CourseState.UNKNOWN));
}
Integer recentDays = courseService.getRecentDays();
Collection<CourseState> states = new ArrayList<CourseState>();
//CourseState state = CourseState.UNKNOWN;
Date nextOpen = new Date(Long.MAX_VALUE);
Date willClose = new Date(0);
boolean isOneOpen = false;
boolean isOneBookable = false;
boolean areSomePlaces = false;
boolean isNew = false;
for (CourseComponent component: courseGroup.getComponents()) {
if (!isOneBookable) {
isOneBookable = component.getBookable();
}
if (null != component.getOpens()) {
// Check if component is the earliest one opening in the future.
//boolean isGoingToOpen = component.getOpens().after(now) && component.getOpens().before(nextOpen);
if (component.getOpens().after(now) && component.getOpens().before(nextOpen)) {
nextOpen = component.getOpens();
}
if (null != component.getCloses()) {
// Check if the component is open and is open for the longest.
if (component.getOpens().before(now) && component.getCloses().after(willClose)) {
willClose = component.getCloses();
}
boolean isOpen = component.getOpens().before(now) && component.getCloses().after(now);
if (!isOneOpen && isOpen) {
isOneOpen = true;
}
if (isOpen) {
if (component.getPlaces() > 0) {
areSomePlaces = true;
}
}
}
}
Calendar recent = new GregorianCalendar();
recent.setTime(component.getCreated());
recent.add(Calendar.DATE, (recentDays));
if (now.before(recent.getTime())) {
isNew = true;
}
}
String detail = null;
if (isNew) {
states.add(CourseState.NEW);
}
//String newCourse = isNew ? "*NEW*" : "";
if (!isOneBookable) {
states.add(CourseState.UNBOOKABLE);
return new CourseSummary(null, states);
}
if (isOneOpen) {
if (areSomePlaces) {
long remaining = willClose.getTime() - now.getTime();
detail = "close in "+ formatDuration(remaining);
states.add(CourseState.OPEN);
} else {
detail = "full";
states.add(CourseState.FULL);
}
} else {
if (nextOpen.getTime() == Long.MAX_VALUE) {
states.add(CourseState.UNBOOKABLE);
} else {
long until = nextOpen.getTime() - now.getTime();
detail = "open in "+ formatDuration(until);
}
}
return new CourseSummary(detail, states);
}
/**
*
* @param code
* @return
*/
private boolean isProvider(String code) {
return courseService.isDepartmentCode(code);
}
// Maybe I should have just implemented a tuple.
static enum CourseState {OPEN, FULL, UNBOOKABLE, UNKNOWN, NEW}
class CourseSummary {
private String detail;
private Collection<CourseState> states;
CourseSummary(String detail, Collection<CourseState> states) {
this.detail = detail;
this.states = states;
}
String getDetail() {
return this.detail;
}
String getCourseState() {
StringBuffer sb = new StringBuffer();
for (CourseState state : states) {
sb.append(state.toString().toLowerCase());
sb.append(" ");
}
return sb.toString();
}
}
private class GroupsStreamingOutput implements StreamingOutput {
private final List<SubUnit> subUnits;
private final List<CourseGroup> courses;
private final String deptId;
private final String range;
private final boolean previous;
private GroupsStreamingOutput(List<SubUnit> subUnits, List<CourseGroup> courses, String deptId, String range, boolean previous) {
this.subUnits = subUnits;
this.courses = courses;
this.deptId = deptId;
this.range = range;
this.previous = previous;
}
public void write(OutputStream out) throws IOException {
Date now = courseService.getNow();
JsonGenerator gen = jsonFactory.createJsonGenerator(out, JsonEncoding.UTF8);
gen.writeStartObject();
gen.writeObjectField("dept", deptId);
gen.writeObjectField("range", range);
gen.writeArrayFieldStart("tree");
for (SubUnit subUnit : subUnits) {
gen.writeStartObject();
gen.writeObjectFieldStart("attr");
gen.writeStringField("id", subUnit.getCode());
gen.writeEndObject();
gen.writeStringField("data", (String)subUnit.getName());
gen.writeStringField("state", "closed");
gen.writeArrayFieldStart("children");
gen.writeEndArray();
gen.writeEndObject();
}
for (CourseGroup courseGroup : courses) {
gen.writeStartObject();
CourseSummary state = summary(now, courseGroup);
gen.writeObjectFieldStart("attr");
gen.writeObjectField("id", courseGroup.getCourseId());
gen.writeObjectField("class", state.getCourseState().toString().toLowerCase());
gen.writeEndObject();
gen.writeObjectField("data",
courseGroup.getTitle() +
(state.getDetail() == null ? "" : (" ("+state.getDetail()+")"))
//(state.getNew() == null ? "" : " " + (state.getNew()))
);
gen.writeEndObject();
}
if (previous) {
gen.writeStartObject();
gen.writeObjectFieldStart("attr");
gen.writeStringField("id", deptId+"-PREVIOUS");
gen.writeEndObject();
gen.writeStringField("data", "Previous");
gen.writeStringField("state", "closed");
//gen.writeArrayFieldStart("children");
//gen.writeEndArray();
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeEndObject();
gen.close();
}
}
private class AllGroupsStreamingOutput implements StreamingOutput {
private final Map<String, String> departments;
private final List<CourseGroup> courses;
private AllGroupsStreamingOutput(Map<String, String> departments, List<CourseGroup> courses) {
this.departments = departments;
this.courses = courses;
}
public void write(OutputStream out) throws IOException {
JsonGenerator gen = jsonFactory.createJsonGenerator(out, JsonEncoding.UTF8);
gen.writeStartArray();
for (CourseGroup courseGroup : courses) {
gen.writeStartObject();
gen.writeObjectField("id", courseGroup.getCourseId());
gen.writeObjectField("description", courseGroup.getDescription());
gen.writeObjectField("title", courseGroup.getTitle());
gen.writeArrayFieldStart("components");
for (CourseComponent component : courseGroup.getComponents()) {
gen.writeStartObject();
gen.writeObjectField("id", component.getPresentationId());
gen.writeObjectField("title", component.getTitle());
gen.writeObjectField("location", component.getLocation());
gen.writeObjectField("teachingDetails", component.getTeachingDetails());
gen.writeObjectField("size", component.getSize());
if (null != component.getOpens()) {
gen.writeObjectField("opens", component.getOpens().getTime());
}
if (null != component.getCloses()) {
gen.writeObjectField("closes", component.getCloses().getTime());
}
if (null != component.getBaseDate()) {
gen.writeObjectField("baseDate", component.getBaseDate().getTime());
}
gen.writeObjectField("opensText", component.getOpensText());
gen.writeObjectField("sessions", component.getSessions());
gen.writeObjectField("when", component.getWhen());
gen.writeObjectField("bookable", component.getBookable());
gen.writeObjectField("places", component.getPlaces());
gen.writeObjectField("created", component.getCreated().getTime());
gen.writeObjectField("source", component.getSource());
gen.writeObjectField("componentSet", component.getComponentSet());
gen.writeObjectField("source", component.getSource());
if (null != component.getPresenter()) {
gen.writeObjectFieldStart("presenter");
gen.writeObjectField("name", component.getPresenter().getName());
gen.writeObjectField("email", component.getPresenter().getEmail());
gen.writeEndObject();
}
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeArrayFieldStart("administrators");
for (Person administrator : courseGroup.getAdministrators()) {
gen.writeStartObject();
gen.writeObjectField("id", administrator.getId());
gen.writeObjectField("name", administrator.getName());
gen.writeObjectField("type", administrator.getType());
gen.writeObjectField("email", administrator.getEmail());
//gen.writeObjectField("units", administrator.getUnits());
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeArrayFieldStart("superusers");
for (Person superuser : courseGroup.getSuperusers()) {
gen.writeStartObject();
gen.writeObjectField("id", superuser.getId());
gen.writeObjectField("name", superuser.getName());
gen.writeObjectField("type", superuser.getType());
gen.writeObjectField("email", superuser.getEmail());
//gen.writeObjectField("units", superuser.getUnits());
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeArrayFieldStart("department");
gen.writeObject(departments.get(courseGroup.getDepartmentCode()));
for (String code : courseGroup.getOtherDepartments()) {
gen.writeObject(departments.get(code));
}
gen.writeEndArray();
gen.writeArrayFieldStart("categories_rdf");
for (CourseCategory category : courseGroup.getCategories(CourseGroup.CategoryType.RDF)) {
gen.writeObject(category.getName());
}
gen.writeEndArray();
gen.writeArrayFieldStart("categories_jacs");
for (CourseCategory category : courseGroup.getCategories(CourseGroup.CategoryType.JACS)) {
gen.writeObject(category.getName());
}
gen.writeEndArray();
gen.writeArrayFieldStart("categories_rm");
for (CourseCategory category : courseGroup.getCategories(CourseGroup.CategoryType.RM)) {
gen.writeObject(category.getName());
}
gen.writeEndArray();
gen.writeEndObject();
}
gen.writeEndArray();
gen.close();
}
}
private class GroupStreamingOutput implements StreamingOutput {
private final CourseGroup course;
private GroupStreamingOutput(CourseGroup course) {
this.course = course;
}
public void write(OutputStream out) throws IOException {
JsonGenerator gen = jsonFactory.createJsonGenerator(out, JsonEncoding.UTF8);
gen.writeStartObject();
gen.writeObjectField("id", course.getCourseId());
gen.writeObjectField("description", course.getDescription());
gen.writeObjectField("title", course.getTitle());
gen.writeObjectField("supervisorApproval", course.getSupervisorApproval());
gen.writeObjectField("administratorApproval", course.getAdministratorApproval());
gen.writeObjectField("visibility", course.getVisibility());
//gen.writeObjectField("homeApproval", courseGroup.getHomeApproval());
gen.writeObjectField("isAdmin", course.getIsAdmin());
gen.writeObjectField("isSuperuser", course.getIsSuperuser());
gen.writeObjectField("department", course.getDepartment());
gen.writeObjectField("departmentCode", course.getDepartmentCode());
gen.writeObjectField("subUnit", course.getSubUnit());
gen.writeObjectField("subUnitCode", course.getSubUnitCode());
gen.writeObjectField("prerequisite", course.getPrerequisite());
gen.writeObjectField("regulations", course.getRegulations());
gen.writeObjectField("source", course.getSource());
gen.writeArrayFieldStart("components");
for (CourseComponent component : course.getComponents()) {
gen.writeStartObject();
gen.writeObjectField("id", component.getPresentationId());
gen.writeObjectField("location", component.getLocation());
gen.writeObjectField("teachingDetails", component.getTeachingDetails());
gen.writeObjectField("size", component.getSize());
if (null != component.getOpens()) {
gen.writeObjectField("opens", component.getOpens().getTime());
}
gen.writeObjectField("opensText", component.getOpensText());
if (null != component.getCloses()) {
gen.writeObjectField("closes", component.getCloses().getTime());
}
gen.writeObjectField("closesText", component.getClosesText());
if (null != component.getBaseDate()) {
gen.writeObjectField("baseDate", component.getBaseDate().getTime());
}
gen.writeObjectField("title", component.getTitle());
gen.writeObjectField("when", component.getWhen());
gen.writeObjectField("bookable", component.getBookable());
if (null != component.getStarts()) {
gen.writeObjectField("starts", component.getStarts().getTime());
}
gen.writeObjectField("startsText", component.getStartsText());
if (null != component.getEnds()) {
gen.writeObjectField("ends", component.getEnds().getTime());
}
gen.writeObjectField("endsText", component.getEndsText());
gen.writeObjectField("places", component.getPlaces());
gen.writeObjectField("componentSet", component.getComponentSet());
gen.writeObjectField("sessionCount", component.getSessions());
gen.writeObjectField("applyTo", component.getApplyTo());
gen.writeObjectField("memberApplyTo", component.getMemberApplyTo());
gen.writeObjectField("attendanceMode", component.getAttendanceMode());
gen.writeObjectField("attendanceModeText", component.getAttendanceModeText());
gen.writeObjectField("attendancePattern", component.getAttendancePattern());
gen.writeObjectField("attendancePatternText", component.getAttendancePatternText());
if (null != component.getPresenter()) {
gen.writeObjectFieldStart("presenter");
gen.writeObjectField("name", component.getPresenter().getName());
gen.writeObjectField("email", component.getPresenter().getEmail());
//gen.writeObjectField("units", component.getPresenter().getUnits());
gen.writeEndObject();
}
gen.writeArrayFieldStart("sessions");
for (CourseComponentSession session : component.getComponentSessions()) {
gen.writeStartObject();
gen.writeObjectField("sessionId", session.getSessionId());
gen.writeObjectField("sessionStart", session.getSessionStart().getTime());
gen.writeObjectField("sessionStartText", session.getSessionStartText());
gen.writeObjectField("sessionEnd", session.getSessionEnd().getTime());
gen.writeObjectField("sessionEndText", session.getSessionEndText());
gen.writeObjectField("location", session.getLocation());
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeArrayFieldStart("administrators");
for (Person administrator : course.getAdministrators()) {
gen.writeStartObject();
gen.writeObjectField("id", administrator.getId());
gen.writeObjectField("name", administrator.getName());
gen.writeObjectField("type", administrator.getType());
gen.writeObjectField("email", administrator.getEmail());
gen.writeObjectField("firstName", administrator.getFirstName());
gen.writeObjectField("lastName", administrator.getLastName());
gen.writeObjectField("departmentName", administrator.getDepartmentName());
//gen.writeObjectField("units", administrator.getUnits());
gen.writeObjectField("webauthId", administrator.getWebauthId());
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeArrayFieldStart("superusers");
for (Person superuser : course.getSuperusers()) {
gen.writeStartObject();
gen.writeObjectField("id", superuser.getId());
gen.writeObjectField("name", superuser.getName());
gen.writeObjectField("type", superuser.getType());
gen.writeObjectField("email", superuser.getEmail());
gen.writeObjectField("firstName", superuser.getFirstName());
gen.writeObjectField("lastName", superuser.getLastName());
gen.writeObjectField("departmentName", superuser.getDepartmentName());
//gen.writeObjectField("units", superuser.getUnits());
gen.writeObjectField("webauthId", superuser.getWebauthId());
gen.writeEndObject();
}
gen.writeEndArray();
gen.writeArrayFieldStart("otherDepartments");
for (String code : course.getOtherDepartments()) {
gen.writeObject(code);
}
gen.writeEndArray();
gen.writeArrayFieldStart("categories_rdf");
for (CourseCategory category : course.getCategories(CourseGroup.CategoryType.RDF)) {
gen.writeObject(category.getName());
}
gen.writeEndArray();
gen.writeArrayFieldStart("categories_jacs");
for (CourseCategory category : course.getCategories(CourseGroup.CategoryType.JACS)) {
gen.writeObject(category.getName());
}
gen.writeEndArray();
gen.writeArrayFieldStart("categories_rm");
for (CourseCategory category : course.getCategories(CourseGroup.CategoryType.RM)) {
gen.writeObject(category.getName());
}
gen.writeEndArray();
gen.writeEndObject();
gen.close();
}
}
/**
* This class is so that we can close the results wrapper after we've streamed the content back to the client.
*/
private class StringStreamingOutput implements StreamingOutput {
private final ResultsWrapper results;
private StringStreamingOutput(ResultsWrapper results) {
this.results = results;
}
public void write(OutputStream out) {
try {
IOUtils.copy(results.getInputStream(), out);
} catch (IOException e) {
log.warn(e.getMessage(), e);
throw new WebApplicationException(Response.status(Status.SERVICE_UNAVAILABLE)
.entity("Service Unavailable, IO Exception with Search Engine").build());
} finally {
results.disconnect();
}
}
}
}