package rewardsadmin.web.springmvc20; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.springframework.validation.DataBinder; import org.springframework.web.bind.ServletRequestDataBinder; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.multiaction.MultiActionController; import rewards.Dining; import rewards.RewardConfirmation; import rewards.RewardNetwork; import rewardsadmin.app.Reward; import rewardsadmin.app.RewardLookupService; import common.datetime.SimpleDate; import common.datetime.SimpleDateEditor; import common.money.MonetaryAmount; import common.money.MonetaryAmountEditor; /** * A Spring MVC controller for managing reward entities on behalf of authorized user requests. * This controller is a MultiActionController, encapsulating all reward controller logic in * distinct handler methods. Each handler method performs a distinct user interface action * and is mapped to a URL by convention. The URL mapping conventions are: * * <ul> * <li>GET /reward/new -> calls {@link #newForm(HttpServletRequest, HttpServletResponse)} * to setup and render a form to create a new reward. * <li>POST /reward/new -> calls {@link #create(HttpServletRequest, HttpServletResponse)} * to create the new reward entity and save it to the database. * <li>GET /reward/show/${confirmationNumber} -> calls {@link #show(HttpServletRequest, HttpServletResponse)} * to display the details about the reward with the confirmation number. * </ul> * * To carry out its actions, this controller depends on services in the application layer. These services are injected * into this controller using constructor-dependency injection. */ public class RewardController extends MultiActionController { /** * The reward network application responsible for creating rewards. */ private RewardNetwork rewardNetwork; /** * Responsible for retrieving details on previously confirmed rewards. */ private RewardLookupService rewardLookupService; /** * Creates a reward controller. * @param rewardNetwork application responsible for creating rewards * @param rewardLookupService responsible for retrieving historical * information on previously confirmed rewards. */ public RewardController(RewardNetwork rewardNetwork, RewardLookupService rewardLookupService) { this.rewardNetwork = rewardNetwork; this.rewardLookupService = rewardLookupService; // resolves the request to a method on this controller class setMethodNameResolver(new PathBasedMethodNameResolver()); } /** * Prepares the "new reward" form for display before it is rendered, then * selects it for display. Preparation logic consists of registering a * custom property editor to properly format initial form field values. * @param request the request * @param response the response * @return a selection of the "new reward" view with the data needed to render it */ public ModelAndView newForm(HttpServletRequest request, HttpServletResponse response) { RewardForm rewardForm = new RewardForm(); DataBinder binder = new DataBinder(rewardForm, "rewardForm"); // installs a type converter to convert from String to MonetaryAmount during form data binding binder.registerCustomEditor(MonetaryAmount.class, new MonetaryAmountEditor()); return new ModelAndView("reward/new", binder.getBindingResult().getModel()); } /** * Creates a new reward by binding incoming data in the POST request to a * {@link RewardForm} backing object, performing type conversion and * validation, then invoking the configured {@link RewardNetwork}. * * If there are data binding errors, this action method will select the new * reward form again to allow the user to correct their errors. * @param request the request * @param response the response * @return on error, a selection of the "reward/new" containing the errors * the user will need to correct; on success, a redirect to the "reward/show" * controller to display confirmed reward details */ public ModelAndView create(HttpServletRequest request, HttpServletResponse response) { RewardForm rewardForm = new RewardForm(); ServletRequestDataBinder binder = new ServletRequestDataBinder(rewardForm, "rewardForm"); // enforces these fields are present in the form binder.setRequiredFields(new String[] { "creditCardNumber", "amount", "merchantNumber" }); binder.registerCustomEditor(MonetaryAmount.class, new MonetaryAmountEditor()); // copies parameters in the request to the reward form binder.bind(request); if (binder.getBindingResult().hasErrors()) { return new ModelAndView("reward/new", binder.getBindingResult().getModel()); } else { Dining dining = rewardForm.createDining(); RewardConfirmation confirmation = rewardNetwork.rewardAccountFor(dining); return new ModelAndView("redirect:show/" + confirmation.getConfirmationNumber()); } } /** * Displays details about a previously confirmed reward. View preparation logic consists of installing a data binder * the view will use to render formatted reward values such as amounts and dates. * @param request the request * @param response the response * @return a selection of the "show" view to render the reward */ public ModelAndView show(HttpServletRequest request, HttpServletResponse response) { Reward reward = rewardLookupService.lookupReward(extractConfirmationNumber(request)); DataBinder binder = new DataBinder(reward, "reward"); binder.registerCustomEditor(MonetaryAmount.class, new MonetaryAmountEditor()); binder.registerCustomEditor(SimpleDate.class, new SimpleDateEditor()); return new ModelAndView("reward/show", binder.getBindingResult().getModel()); } /** * Simple little helper method that extracts the confirmation number from the HTTP request path. * @param request the request e.g. with a URL like /reward/1 * @return the confirmation number e.g. 1 */ private String extractConfirmationNumber(HttpServletRequest request) { int lastSlash = request.getPathInfo().lastIndexOf('/'); return request.getPathInfo().substring(lastSlash + 1); } }