org.cloudcoder.app.server.rpc.GetCoursesAndProblemsServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.cloudcoder.app.server.rpc.GetCoursesAndProblemsServiceImpl.java

Source

// CloudCoder - a web-based pedagogical programming environment
// Copyright (C) 2011-2015, Jaime Spacco <jspacco@knox.edu>
// Copyright (C) 2011-2015, David H. Hovemeyer <david.hovemeyer@gmail.com>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package org.cloudcoder.app.server.rpc;

import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.StringWriter;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.xml.bind.DatatypeConverter;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.StatusLine;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService;
import org.cloudcoder.app.server.persist.Database;
import org.cloudcoder.app.shared.dto.ShareExercisesResult;
import org.cloudcoder.app.shared.model.CloudCoderAuthenticationException;
import org.cloudcoder.app.shared.model.ConfigurationSetting;
import org.cloudcoder.app.shared.model.ConfigurationSettingName;
import org.cloudcoder.app.shared.model.Course;
import org.cloudcoder.app.shared.model.CourseAndCourseRegistration;
import org.cloudcoder.app.shared.model.CourseCreationSpec;
import org.cloudcoder.app.shared.model.CourseRegistration;
import org.cloudcoder.app.shared.model.CourseRegistrationList;
import org.cloudcoder.app.shared.model.Module;
import org.cloudcoder.app.shared.model.ModuleNameComparator;
import org.cloudcoder.app.shared.model.NamedTestResult;
import org.cloudcoder.app.shared.model.OperationResult;
import org.cloudcoder.app.shared.model.Problem;
import org.cloudcoder.app.shared.model.ProblemAndSubmissionReceipt;
import org.cloudcoder.app.shared.model.ProblemAndTestCaseList;
import org.cloudcoder.app.shared.model.ProblemAuthorship;
import org.cloudcoder.app.shared.model.Quiz;
import org.cloudcoder.app.shared.model.SubmissionReceipt;
import org.cloudcoder.app.shared.model.Term;
import org.cloudcoder.app.shared.model.TestCase;
import org.cloudcoder.app.shared.model.User;
import org.cloudcoder.app.shared.model.UserAndSubmissionReceipt;
import org.cloudcoder.app.shared.model.json.JSONConversion;
import org.cloudcoder.app.shared.model.json.ReflectionFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.gwt.user.server.rpc.RemoteServiceServlet;

/**
 * RPC servlet for accessing courses and problems.
 */
public class GetCoursesAndProblemsServiceImpl extends RemoteServiceServlet implements GetCoursesAndProblemsService {
    private static final long serialVersionUID = 1L;
    private static final Logger logger = LoggerFactory.getLogger(GetCoursesAndProblemsServiceImpl.class);

    @Override
    public Course[] getCourses() throws CloudCoderAuthenticationException {
        // make sure the client has authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        logger.info("Loading courses for user " + user.getUsername());

        List<? extends Object[]> resultList = Database.getInstance().getCoursesForUser(user);

        Course[] result = new Course[resultList.size()];
        int count = 0;
        for (Object[] tuple : resultList) {
            Course course = (Course) tuple[0];
            Term term = (Term) tuple[1];
            course.setTerm(term);
            result[count++] = course;

            logger.info("Found course: " + course.getName() + ": " + course.getTitle());
        }

        return result;
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#getCourseAndCourseRegistrations()
     */
    @Override
    public CourseAndCourseRegistration[] getCourseAndCourseRegistrations()
            throws CloudCoderAuthenticationException {
        // make sure the client has authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        logger.info("Loading courses and registrations for user " + user.getUsername());

        List<? extends Object[]> resultList = Database.getInstance().getCoursesForUser(user);

        CourseAndCourseRegistration[] result = new CourseAndCourseRegistration[resultList.size()];
        int count = 0;
        for (Object[] tuple : resultList) {
            Course course = (Course) tuple[0];
            Term term = (Term) tuple[1];
            course.setTerm(term);
            CourseRegistration reg = (CourseRegistration) tuple[2];

            CourseAndCourseRegistration obj = new CourseAndCourseRegistration();
            obj.setCourse(course);
            obj.setCourseRegistration(reg);

            result[count++] = obj;
        }

        return result;
    }

    @Override
    public Problem[] getProblems(Course course) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        List<Problem> resultList = Database.getInstance().getProblemsInCourse(user, course).getProblemList();
        for (Problem p : resultList) {
            logger.info(p.getTestname() + " - " + p.getBriefDescription());
        }

        return resultList.toArray(new Problem[resultList.size()]);
    }

    public Problem[] getProblemsForUser(Course course, int userId) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = Database.getInstance().getUserGivenId(userId);

        List<Problem> resultList = Database.getInstance().getProblemsInCourse(user, course).getProblemList();
        for (Problem p : resultList) {
            logger.warn(p.getTestname() + " - " + p.getBriefDescription());
        }

        return resultList.toArray(new Problem[resultList.size()]);
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#getProblemAndSubscriptionReceipts(org.cloudcoder.app.shared.model.Course)
     */
    @Override
    public ProblemAndSubmissionReceipt[] getProblemAndSubscriptionReceipts(Course course, User forUser,
            Module module) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        logger.info("getting problems/submission receipts for authenticated user " + user.getUsername());

        List<ProblemAndSubmissionReceipt> resultList = Database.getInstance()
                .getProblemAndSubscriptionReceiptsInCourse(user, course, forUser, module);
        logger.info("Received " + resultList.size() + " problems/submission receipts");
        return resultList.toArray(new ProblemAndSubmissionReceipt[resultList.size()]);
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#getBestSubmissionReceipts(org.cloudcoder.app.shared.model.Problem)
     */
    @Override
    public UserAndSubmissionReceipt[] getBestSubmissionReceipts(Problem problem, int section)
            throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        // Return best submission receipts for each user in course
        List<UserAndSubmissionReceipt> result = Database.getInstance().getBestSubmissionReceipts(problem, section,
                user);
        return result.toArray(new UserAndSubmissionReceipt[result.size()]);
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#getTestCasesForProblem(int)
     */
    @Override
    public TestCase[] getTestCasesForProblem(int problemId) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        return Database.getInstance().getTestCasesForProblem(user, true, problemId);
    }

    @Override
    public TestCase[] getNonSecretTestCasesForProblem(int problemId) throws CloudCoderAuthenticationException {
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);
        TestCase[] testCases = Database.getInstance().getTestCasesForProblem(user, false, problemId);
        ArrayList<TestCase> nonSecretTestCases = new ArrayList<TestCase>();
        for (TestCase tc : testCases) {
            if (!tc.isSecret()) {
                nonSecretTestCases.add(tc);
            }
        }
        return nonSecretTestCases.toArray(new TestCase[nonSecretTestCases.size()]);
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#getTestCaseNamesForProblem(int)
     */
    @Override
    public String[] getTestCaseNamesForProblem(int problemId) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);
        TestCase[] testCaseList = Database.getInstance().getTestCasesForProblem(user, false, problemId);
        String[] result = new String[testCaseList.length];
        for (int i = 0; i < testCaseList.length; i++) {
            result[i] = testCaseList[i].getTestCaseName();
        }
        return result;
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#storeProblemAndTestCaseList(org.cloudcoder.app.shared.model.ProblemAndTestCaseList)
     */
    @Override
    public ProblemAndTestCaseList storeProblemAndTestCaseList(ProblemAndTestCaseList problemAndTestCaseList,
            Course course) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        // Store in database
        Database.getInstance().storeProblemAndTestCaseList(problemAndTestCaseList, course, user);

        // Return updated object
        return problemAndTestCaseList;
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#submitExercise(org.cloudcoder.app.shared.model.ProblemAndTestCaseList, java.lang.String, java.lang.String)
     */
    @Override
    public OperationResult submitExercise(ProblemAndTestCaseList exercise, String repoUsername, String repoPassword)
            throws CloudCoderAuthenticationException {
        logger.warn("Sharing exercise: " + exercise.getProblem().getTestname());

        // Only a course instructor may share an exercise.
        User authenticatedUser = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);
        Course course = new Course();
        course.setId(exercise.getProblem().getCourseId());
        Database.getInstance().reloadModelObject(course);
        CourseRegistrationList regList = Database.getInstance().findCourseRegistrations(authenticatedUser, course);
        if (!regList.isInstructor()) {
            return new OperationResult(false, "You must be an instructor to share an exercise");
        }

        // Get the exercise repository URL
        ConfigurationSetting repoUrlSetting = Database.getInstance()
                .getConfigurationSetting(ConfigurationSettingName.PUB_REPOSITORY_URL);
        if (repoUrlSetting == null) {
            return new OperationResult(false, "URL of exercise repository is not configured");
        }
        String repoUrl = repoUrlSetting.getValue();
        if (repoUrl.endsWith("/")) {
            repoUrl = repoUrl.substring(0, repoUrl.length() - 1);
        }

        HttpPost post = new HttpPost(repoUrl + "/exercisedata");

        // Encode an Authorization header using the provided repository username and password.
        String authHeaderValue = "Basic " + DatatypeConverter
                .printBase64Binary((repoUsername + ":" + repoPassword).getBytes(Charset.forName("UTF-8")));
        //System.out.println("Authorization: " + authHeaderValue);
        post.addHeader("Authorization", authHeaderValue);

        // Convert the exercise to a JSON string
        StringEntity entity;
        StringWriter sw = new StringWriter();
        try {
            JSONConversion.writeProblemAndTestCaseData(exercise, sw);
            entity = new StringEntity(sw.toString(), ContentType.create("application/json", "UTF-8"));
        } catch (IOException e) {
            return new OperationResult(false, "Could not convert exercise to JSON: " + e.getMessage());
        }
        post.setEntity(entity);

        // POST the exercise to the repository
        HttpClient client = new DefaultHttpClient();
        try {
            HttpResponse response = client.execute(post);

            StatusLine statusLine = response.getStatusLine();

            if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
                // Update the exercise's shared flag so we have a record that it was shared.
                exercise.getProblem().setShared(true);
                Database.getInstance().storeProblemAndTestCaseList(exercise, course, authenticatedUser);

                return new OperationResult(true, "Exercise successfully published to the repository - thank you!");
            } else if (statusLine.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                return new OperationResult(false,
                        "Authentication with repository failed - incorrect username/password?");
            } else {
                return new OperationResult(false,
                        "Failed to publish exercise to repository: " + statusLine.getReasonPhrase());
            }
        } catch (ClientProtocolException e) {
            return new OperationResult(false, "Error sending exercise to repository: " + e.getMessage());
        } catch (IOException e) {
            return new OperationResult(false, "Error sending exercise to repository: " + e.getMessage());
        } finally {
            client.getConnectionManager().shutdown();
        }
    }

    @Override
    public ShareExercisesResult submitExercises(Problem[] problems, String repoUsername, String repoPassword)
            throws CloudCoderAuthenticationException {
        logger.warn("Sharing " + problems.length + " exercises");

        // create the result place holder
        ShareExercisesResult result = new ShareExercisesResult(problems.length);

        if (problems.length == 0) {
            result.failAll("No problems to be shared!");
            return result;
        }

        // Only a course instructor may share an exercise.
        User authenticatedUser = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);
        Course course = new Course();
        course.setId(problems[0].getCourseId());
        Database.getInstance().reloadModelObject(course);
        CourseRegistrationList regList = Database.getInstance().findCourseRegistrations(authenticatedUser, course);
        if (!regList.isInstructor()) {
            result.failAll("You must be an instructor to share an exercise");
            return result;
        }

        // Get the exercise repository URL
        ConfigurationSetting repoUrlSetting = Database.getInstance()
                .getConfigurationSetting(ConfigurationSettingName.PUB_REPOSITORY_URL);
        if (repoUrlSetting == null) {
            result.failAll("URL of exercise repository is not configured");
            return result;
        }
        String repoUrl = repoUrlSetting.getValue();
        if (repoUrl.endsWith("/")) {
            repoUrl = repoUrl.substring(0, repoUrl.length() - 1);
        }

        HttpPost post = new HttpPost(repoUrl + "/exercisedata");

        // Encode an Authorization header using the provided repository username and password.
        String authHeaderValue = "Basic " + DatatypeConverter
                .printBase64Binary((repoUsername + ":" + repoPassword).getBytes(Charset.forName("UTF-8")));
        //System.out.println("Authorization: " + authHeaderValue);
        post.addHeader("Authorization", authHeaderValue);

        // Now go through and upload each problem
        // For now, we do this one at a time
        // In the future we could send problems and test cases 
        // to the repo in bulk, and add a new web service to handle it

        for (Problem p : problems) {
            // Look up the test cases
            List<TestCase> testCaseList = Database.getInstance().getTestCasesForProblem(p.getProblemId());
            ProblemAndTestCaseList exercise = new ProblemAndTestCaseList();
            exercise.setProblem(p);
            exercise.setTestCaseList(testCaseList);

            // Convert the exercise to a JSON string
            StringEntity entity;
            StringWriter sw = new StringWriter();
            try {
                JSONConversion.writeProblemAndTestCaseData(exercise, sw);
                entity = new StringEntity(sw.toString(), ContentType.create("application/json", "UTF-8"));
            } catch (IOException e) {
                // fail remaining test cases and return our results thus far
                // some exercises may have been successfully shared
                result.failRemaining("Could not convert exercise to JSON: " + e.getMessage());
                return result;
            }
            post.setEntity(entity);

            // POST the exercise to the repository
            HttpClient client = new DefaultHttpClient();
            try {
                HttpResponse response = client.execute(post);

                StatusLine statusLine = response.getStatusLine();

                if (statusLine.getStatusCode() == HttpStatus.SC_OK) {
                    // Update the exercise's shared flag so we have a record that it was shared.
                    exercise.getProblem().setShared(true);
                    exercise.getProblem().setProblemAuthorship(ProblemAuthorship.IMPORTED);
                    Database.getInstance().storeProblemAndTestCaseList(exercise, course, authenticatedUser);
                    result.success();
                } else if (statusLine.getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
                    result.failRemaining("Authentication with repository failed - incorrect username/password?");
                    return result;
                } else {
                    result.failRemaining(
                            "Failed to publish exercise to repository: " + statusLine.getReasonPhrase());
                    return result;
                }
            } catch (ClientProtocolException e) {
                result.failRemaining("Error sending exercise to repository: " + e.getMessage());
                return result;
            } catch (IOException e) {
                result.failRemaining("Error sending exercise to repository: " + e.getMessage());
                return result;
            } finally {
                client.getConnectionManager().shutdown();
            }
        }
        result.allSucceeded("Successfully uploaded " + problems.length + " exercise to repository.  Thanks!");
        return result;
    }

    @Override
    public ProblemAndTestCaseList importExercise(Course course, String exerciseHash)
            throws CloudCoderAuthenticationException {
        if (course == null || exerciseHash == null) {
            throw new IllegalArgumentException();
        }

        // Make sure a user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        // Find user's registration in the course: if user is not instructor,
        // import is not allowed
        CourseRegistrationList reg = Database.getInstance().findCourseRegistrations(user, course);
        if (!reg.isInstructor()) {
            throw new CloudCoderAuthenticationException("Only an instructor can import a problem in a course");
        }

        // Attempt to load the problem from the exercise repository.
        ConfigurationSetting repoUrlSetting = Database.getInstance()
                .getConfigurationSetting(ConfigurationSettingName.PUB_REPOSITORY_URL);
        if (repoUrlSetting == null) {
            logger.error("Repository URL configuration setting is not set");
            return null;
        }

        // GET the exercise from the repository
        HttpGet get = new HttpGet(repoUrlSetting.getValue() + "/exercisedata/" + exerciseHash);
        ProblemAndTestCaseList exercise = null;

        HttpClient client = new DefaultHttpClient();
        try {
            HttpResponse response = client.execute(get);

            HttpEntity entity = response.getEntity();

            ContentType contentType = ContentType.getOrDefault(entity);
            Reader reader = new InputStreamReader(entity.getContent(), contentType.getCharset());

            exercise = new ProblemAndTestCaseList();
            exercise.setTestCaseList(new TestCase[0]);
            JSONConversion.readProblemAndTestCaseData(exercise, ReflectionFactory.forClass(Problem.class),
                    ReflectionFactory.forClass(TestCase.class), reader);

            // Set the course id
            exercise.getProblem().setCourseId(course.getId());
        } catch (IOException e) {
            logger.error("Error importing exercise from repository", e);
            return null;
        } finally {
            client.getConnectionManager().shutdown();
        }

        // Set "when assigned" and "when due" to reasonable default values
        long now = System.currentTimeMillis();
        exercise.getProblem().setWhenAssigned(now);
        exercise.getProblem().setWhenDue(now + 48L * 60L * 60L * 1000L);

        // Set problem authorship as IMPORTED
        exercise.getProblem().setProblemAuthorship(ProblemAuthorship.IMPORTED);

        // For IMPORTED problems, parent_hash is actually the hash of the problem
        // itself.  If the problem is modified (and the authorship changed
        // to IMPORTED_AND_MODIFIED), then the (unchanged) parent_hash value
        // really does reflect the "parent" problem.
        exercise.getProblem().setParentHash(exerciseHash);

        // Store the exercise in the database
        exercise = Database.getInstance().storeProblemAndTestCaseList(exercise, course, user);

        return exercise;
    }

    @Override
    public OperationResult deleteProblem(Course course, Problem problem) throws CloudCoderAuthenticationException {
        // Make sure a user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);
        boolean result = Database.getInstance().deleteProblem(user, course, problem);
        return new OperationResult(result, result ? "Problem deleted successfully" : "Could not delete problem");
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#startQuiz(org.cloudcoder.app.shared.model.Problem, int)
     */
    @Override
    public Quiz startQuiz(Problem problem, int section) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);
        return Database.getInstance().startQuiz(user, problem, section);
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#findCurrentQuiz(org.cloudcoder.app.shared.model.Problem)
     */
    @Override
    public Quiz findCurrentQuiz(Problem problem) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        // Find current quiz (if any)
        Quiz quiz = Database.getInstance().findCurrentQuiz(user, problem);

        if (quiz != null) {
            // Set the end time to the current time: this allows the client
            // to compute how long the quiz has been active
            quiz.setEndTime(System.currentTimeMillis());
        }

        return quiz;
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#endQuiz(org.cloudcoder.app.shared.model.Quiz)
     */
    @Override
    public Boolean endQuiz(Quiz quiz) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        return Database.getInstance().endQuiz(user, quiz);
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#getModulesForCourse(org.cloudcoder.app.shared.model.Course)
     */
    @Override
    public Module[] getModulesForCourse(Course course) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        Module[] modulesForCourse = Database.getInstance().getModulesForCourse(user, course);

        // Sort using ModuleNameComparator
        Arrays.sort(modulesForCourse, new ModuleNameComparator());

        return modulesForCourse;
    }

    /* (non-Javadoc)
     * @see org.cloudcoder.app.client.rpc.GetCoursesAndProblemsService#setModuleName(org.cloudcoder.app.shared.model.Problem, java.lang.String)
     */
    @Override
    public Module setModule(Problem problem, String moduleName) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        return Database.getInstance().setModule(user, problem, moduleName);
    }

    @Override
    public Integer[] getSectionsForCourse(Course course) throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User user = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        return Database.getInstance().getSectionsForCourse(course, user);
    }

    @Override
    public SubmissionReceipt[] getAllSubmissionReceiptsForUser(Problem problem, User user)
            throws CloudCoderAuthenticationException {
        // Make sure user is authenticated
        User authenticatedUser = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        // Make sure that either
        //   (1) authenticated user is an instructor, or
        //   (2) authenticated user is the user whose submission receipts
        //       are being requested
        CourseRegistrationList regList = Database.getInstance().findCourseRegistrations(authenticatedUser,
                problem.getCourseId());
        logger.info("User {} requesting submission receipts for user {}", authenticatedUser.getId(), user.getId());
        if (!(regList.isInstructor() || authenticatedUser.getId() == user.getId())) {
            // Authenticated user is not authorized to view submissions for
            // requested user
            return new SubmissionReceipt[0];
        }

        return Database.getInstance().getAllSubmissionReceiptsForUser(problem, user);
    }

    @Override
    public NamedTestResult[] getTestResultsForSubmission(Problem problem, SubmissionReceipt receipt)
            throws CloudCoderAuthenticationException {
        User authenticatedUser = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);
        return Database.getInstance().getTestResultsForSubmission(authenticatedUser, problem, receipt);
    }

    @Override
    public void startImportAllProblemsFromCourse(Course source, Course dest)
            throws CloudCoderAuthenticationException {
        User authenticatedUser = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        // Make sure that the authenticated user is registered as an instructor for
        // both the source and destination courses.
        boolean sourceInstructor = false, destInstructor = false;
        List<? extends Object[]> courses = Database.getInstance().getCoursesForUser(authenticatedUser);
        for (Object[] triple : courses) {
            CourseRegistration reg = (CourseRegistration) triple[2];
            if (reg.getCourseId() == source.getId() && reg.getRegistrationType().isInstructor()) {
                sourceInstructor = true;
            }
            if (reg.getCourseId() == dest.getId() && reg.getRegistrationType().isInstructor()) {
                destInstructor = true;
            }
        }

        // Create a FutureImportCourseResult
        FutureImportCourseResult result = new FutureImportCourseResult();
        getThreadLocalRequest().getSession().setAttribute(SessionAttributeKeys.FUTURE_IMPORT_COURSE_RESULT_KEY,
                result);

        if (!sourceInstructor || !destInstructor) {
            result.set(new OperationResult(false, "Permission denied (not an instructor)"));
            return;
        }

        // Start the actual operation
        result.start(source, dest, authenticatedUser);
    }

    @Override
    public OperationResult checkImportAllProblemsFromCourse() {
        FutureImportCourseResult result = (FutureImportCourseResult) getThreadLocalRequest().getSession()
                .getAttribute(SessionAttributeKeys.FUTURE_IMPORT_COURSE_RESULT_KEY);
        if (result == null) {
            return new OperationResult(false, "There doesn't seem to be a pending operation to import exercises?");
        }
        OperationResult poll = result.poll();
        if (poll != null) {
            getThreadLocalRequest().getSession()
                    .removeAttribute(SessionAttributeKeys.FUTURE_IMPORT_COURSE_RESULT_KEY);
        }
        return poll;
    }

    @Override
    public OperationResult updateProblemDates(Problem[] problems) throws CloudCoderAuthenticationException {
        // Get authenticated user
        User authenticatedUser = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        return Database.getInstance().updateProblemDates(authenticatedUser, problems);
    }

    @Override
    public Term[] getTerms() {
        // Note that we don't authenticate the user here
        return Database.getInstance().getTerms();
    }

    @Override
    public OperationResult createCourse(CourseCreationSpec spec) throws CloudCoderAuthenticationException {
        User authenticatedUser = ServletUtil.checkClientIsAuthenticated(getThreadLocalRequest(),
                GetCoursesAndProblemsServiceImpl.class);

        // User must be a superuser
        if (!authenticatedUser.isSuperuser()) {
            return new OperationResult(false, "Only superusers can create a course");
        }

        return Database.getInstance().createCourse(spec);
    }
}