org.jasig.ssp.web.api.PersonController.java Source code

Java tutorial

Introduction

Here is the source code for org.jasig.ssp.web.api.PersonController.java

Source

/**
 * Licensed to Apereo under one or more contributor license
 * agreements. See the NOTICE file distributed with this work
 * for additional information regarding copyright ownership.
 * Apereo 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 the following location:
 *
 *   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.ssp.web.api;

import java.util.Map;
import java.util.SortedSet;
import java.util.UUID;

import javax.mail.SendFailedException;
import javax.validation.Valid;

import org.apache.commons.lang.NotImplementedException;
import org.jasig.ssp.dao.PersonExistsException;
import org.jasig.ssp.factory.PersonTOFactory;
import org.jasig.ssp.model.ObjectStatus;
import org.jasig.ssp.model.Person;
import org.jasig.ssp.security.permissions.Permission;
import org.jasig.ssp.service.ObjectNotFoundException;
import org.jasig.ssp.service.PersonEmailService;
import org.jasig.ssp.service.PersonService;
import org.jasig.ssp.service.SecurityService;
import org.jasig.ssp.service.external.ExternalPersonService;
import org.jasig.ssp.transferobject.CoachPersonLiteTO;
import org.jasig.ssp.transferobject.PagedResponse;
import org.jasig.ssp.transferobject.PersonLiteTO;
import org.jasig.ssp.transferobject.PersonSearchResultTO;
import org.jasig.ssp.transferobject.PersonTO;
import org.jasig.ssp.transferobject.ServiceResponse;
import org.jasig.ssp.transferobject.form.EmailStudentRequestForm;
import org.jasig.ssp.util.sort.PagingWrapper;
import org.jasig.ssp.util.sort.SortingAndPaging;
import org.jasig.ssp.util.transaction.WithTransaction;
import org.jasig.ssp.web.api.validation.ValidationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * Service methods for manipulating data about people in the system.
 * <p>
 * Mapped to URI path <code>/1/person</code>
 */
@Controller
@RequestMapping("/1/person")
public class PersonController extends AbstractBaseController {

    private static final Logger LOGGER = LoggerFactory.getLogger(PersonController.class);

    @Autowired
    private transient PersonService service;

    @Autowired
    private transient PersonEmailService personEmailService;

    @Autowired
    private transient ExternalPersonService externalPersonService;

    @Autowired
    private transient PersonTOFactory factory;

    @Autowired
    protected transient SecurityService securityService;

    @Autowired
    private WithTransaction withTransaction;

    /**
     * Retrieve every instance in the database filtered by the supplied status.
     * 
     * @param status
     *            Filter by this status.
     * @param start
     *            First result (0-based index) to return. Parameter must be a
     *            positive, non-zero integer. Often comes from client as a
     *            parameter labeled <code>start</code>. A null value indicates
     *            to return rows starting from index 0.
     * @param limit
     *            Maximum number of results to return. Parameter must be a
     *            positive, non-zero integer. Often comes from client as a
     *            parameter labeled <code>limit</code>. A null value indicates
     *            return all rows from the start parameter to the end of the
     *            data.
     * @param sort
     *            Property name. If null or empty string, the default sort will
     *            be used. If non-empty, must be a case-sensitive model property
     *            name. Often comes from client as a parameter labeled
     *            <code>sort</code>. Example sort expression:
     *            <code>propertyName</code>
     * @param sortDirection
     *            Ascending/descending keyword. If null or empty string, the
     *            default sort will be used. Must be <code>ASC</code> or
     *            <code>DESC</code>.
     * @return All entities in the database filtered by the supplied status.
     */
    @RequestMapping(method = RequestMethod.GET)
    @PreAuthorize("hasRole('ROLE_PERSON_READ') or hasRole('ROLE_PERSON_FILTERED_READ')")
    public @ResponseBody PagedResponse<PersonTO> getAll(final @RequestParam(required = false) ObjectStatus status,
            final @RequestParam(required = false) Integer start,
            final @RequestParam(required = false) Integer limit, final @RequestParam(required = false) String sort,
            final @RequestParam(required = false) String sortDirection) {

        // Performance is so terrible against a production-sized dataset that
        // we've got to turn this op off.
        throw new UnsupportedOperationException(
                "The 'list all persons' API" + " is disabled. Consider /1/person/<id> or /1/person/search"
                        + " and /1/person/students/search instead.");

        // Leaving code commented out b/c we'll want to turn this back on once
        // performance problems are sorted out.
        //      final PagingWrapper<Person> people = service.getAll(SortingAndPaging
        //            .createForSingleSortWithPaging(status, start, limit, sort, sortDirection,
        //                  null));
        //
        //      return new PagedResponse<PersonTO>(true, people.getResults(),
        //            factory.asTOList(people.getRows()));
    }

    @RequestMapping(value = "/coach", method = RequestMethod.GET)
    @PreAuthorize("hasRole('ROLE_PERSON_READ') or hasRole('ROLE_PERSON_FILTERED_READ')")
    public @ResponseBody PagedResponse<CoachPersonLiteTO> getAllCoaches(
            final @RequestParam(required = false) ObjectStatus status,
            final @RequestParam(required = false) Integer start,
            final @RequestParam(required = false) Integer limit, final @RequestParam(required = false) String sort,
            final @RequestParam(required = false) String sortDirection) {
        final PagingWrapper<CoachPersonLiteTO> coaches = service.getAllCoachesLite(
                SortingAndPaging.createForSingleSortWithPaging(status, start, limit, sort, sortDirection, null));

        return new PagedResponse<CoachPersonLiteTO>(true, coaches.getResults(), coaches.getRows());

    }

    @RequestMapping(value = "/email", method = RequestMethod.POST)
    @PreAuthorize("hasRole('ROLE_PERSON_WRITE') or hasRole('ROLE_PERSON_FILTERED_WRITE')")
    public @ResponseBody Map<String, UUID> emailStudent(final @RequestBody EmailStudentRequestForm emailRequest)
            throws ObjectNotFoundException, ValidationException {
        final Map<String, UUID> rslt = personEmailService.emailStudent(emailRequest);
        return rslt;
    }

    @RequestMapping(value = "/currentCoachesLite", method = RequestMethod.GET)
    @PreAuthorize("hasRole('ROLE_PERSON_READ') or hasRole('ROLE_PERSON_FILTERED_READ')")
    public @ResponseBody PagedResponse<PersonLiteTO> getAllCurrentCoaches() {
        final SortedSet<CoachPersonLiteTO> coaches = service
                .getAllCurrentCoachesLite(CoachPersonLiteTO.COACH_PERSON_LITE_TO_NAME_AND_ID_COMPARATOR);

        return new PagedResponse<PersonLiteTO>(true, new Long(coaches.size()),
                PersonLiteTO.toTOListFromCoachTOs(coaches));
    }

    /**
     * Retrieves the specified instance from persistent storage.
     * 
     * @param id
     *            The specific id to use to lookup the associated data.
     * @return The specified instance if found.
     * @throws ObjectNotFoundException
     *             If specified object could not be found.
     * @throws ValidationException
     *             If that specified data is not invalid.
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.GET)
    @PreAuthorize("hasRole('ROLE_PERSON_READ') or hasRole('ROLE_PERSON_FILTERED_READ')")
    public @ResponseBody PersonTO get(final @PathVariable UUID id) throws ObjectNotFoundException {
        final Person model = service.get(id);
        if (model == null) {
            return null;
        }

        return new PersonTO(model);
    }

    @RequestMapping(value = "/lite/{id}", method = RequestMethod.GET)
    @PreAuthorize("hasRole('ROLE_PERSON_READ') or hasRole('ROLE_PERSON_FILTERED_READ')")
    @ResponseBody
    PersonLiteTO getLite(final @PathVariable UUID id) throws ObjectNotFoundException {
        final Person model = service.get(id);
        if (model == null) {
            return null;
        }
        return new PersonLiteTO(model);
    }

    @RequestMapping(value = "/searchlite/{id}", method = RequestMethod.GET)
    @PreAuthorize("hasRole('ROLE_PERSON_READ') or hasRole('ROLE_PERSON_FILTERED_READ')")
    @ResponseBody
    PersonSearchResultTO getSearchLite(final @PathVariable UUID id) throws ObjectNotFoundException {
        final Person model = service.get(id);
        if (model == null) {
            return null;
        }
        return new PersonSearchResultTO(model);
    }

    @RequestMapping(value = "/bySchoolId/{id}", method = RequestMethod.GET)
    @PreAuthorize("hasRole('ROLE_PERSON_READ') or hasRole('ROLE_PERSON_FILTERED_READ')")
    public @ResponseBody PersonTO bySchoolId(final @PathVariable String id) throws ObjectNotFoundException {

        final Person model = service.getBySchoolId(id, false);
        if (model == null) {
            return null;
        }

        PersonTO personTO = new PersonTO(model);
        service.evict(model);
        return personTO;
    }

    /**
     * Persist a new instance of the specified object.
     * <p>
     * Must not include an id.
     * 
     * @param obj
     *            New instance to persist.
     * @return Original instance plus the generated id.
     * @throws ObjectNotFoundException
     *             If specified object could not be found.
     * @throws ValidationException
     *             If the specified data contains an id (since it shouldn't).
     */
    @RequestMapping(method = RequestMethod.POST)
    @PreAuthorize("hasRole('ROLE_PERSON_WRITE') or hasRole('ROLE_PERSON_FILTERED_WRITE')")
    public @ResponseBody PersonTO create(final @Valid @RequestBody PersonTO obj)
            throws ValidationException, ObjectNotFoundException {
        if (obj.getId() != null) {
            throw new ValidationException(
                    "You submitted a person with an id to the create method.  Did you mean to save?");
        }

        final Person model = factory.from(obj);

        Person createdModel = null;
        PersonExistsException conflict = null;
        if (null != model) {
            int retryLimit = 1;
            do {
                try {
                    conflict = null;
                    createdModel = service.create(model);
                    if (null != createdModel) {
                        // syncing newly created person to external person table
                        externalPersonService.updatePersonFromExternalPerson(createdModel);
                        return new PersonTO(createdModel);
                    }
                } catch (PersonExistsException e) {
                    LOGGER.info("Person creation conflicted with an existing"
                            + " record. Will be retried {} times before" + " raising an error to the caller.",
                            retryLimit, e);
                    conflict = e;
                    // try to tell the caller which record conflicted, by PK
                    // else something deleted the person from under us?
                }
            } while (retryLimit-- > 0);
            if (null == createdModel) {
                if (conflict != null) {
                    throw conflict;
                }
                return null;
            }
        }
        return null;
    }

    /**
     * Persist any changes to the specified instance.
     * 
     * @param id
     *            Explicit id to the instance to persist.
     * @param obj
     *            Full instance to persist.
     * @return The update data object instance.
     * @throws ObjectNotFoundException
     *             If specified object could not be found.
     * @throws ValidationException
     *             If the specified id is null.
     * @throws SendFailedException 
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.PUT)
    @PreAuthorize("hasRole('ROLE_PERSON_WRITE') or hasRole('ROLE_PERSON_FILTERED_WRITE')")
    public @ResponseBody PersonTO save(final @PathVariable UUID id, final @Valid @RequestBody PersonTO obj)
            throws ObjectNotFoundException, ValidationException, SendFailedException {
        if (id == null) {
            throw new ValidationException(
                    "You submitted a person without an id to the save method.  Did you mean to create?");
        }
        //This call has to always be before 
        //final Person model = factory.from(obj);
        UUID oldCoachId = service.getCoachIdForStudent(obj);

        final Person model = factory.from(obj);
        model.setId(id);

        if (!model.getCoach().getId().equals(oldCoachId)) {
            personEmailService.sendCoachingAssignmentChangeEmail(model, oldCoachId);
        }
        final Person savedPerson = service.save(model);
        if (null != savedPerson) {
            return new PersonTO(savedPerson);
        }
        return null;
    }

    /**
     * Marks the specified data instance with a status of
     * {@link ObjectStatus#INACTIVE}.
     * 
     * @param id
     *            The id of the data instance to mark deleted.
     * @return Success boolean.
     * @throws ObjectNotFoundException
     *             If specified object could not be found.
     */
    @RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
    @PreAuthorize(Permission.SECURITY_PERSON_DELETE)
    public @ResponseBody ServiceResponse delete(final @PathVariable UUID id) throws ObjectNotFoundException {
        service.delete(id);
        return new ServiceResponse(true);
    }

    //@RequestMapping(value = "/{id}/history/print", method = RequestMethod.GET)
    @PreAuthorize(Permission.SECURITY_PERSON_READ)
    public @ResponseBody PersonTO historyPrint(final @PathVariable UUID id) throws ObjectNotFoundException {
        // final Person model = service.get(id);
        // :TODO historyPrint on PersonController
        throw new NotImplementedException();
    }

    @Override
    protected Logger getLogger() {
        return LOGGER;
    }
}