package rewardsadmin.web.springmvc; import javax.servlet.http.HttpServletRequest; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; import org.springframework.validation.BindingResult; import org.springframework.web.bind.WebDataBinder; import org.springframework.web.bind.annotation.InitBinder; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; 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 encapsulates 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()} * to setup and render a form to create a new reward. * <li>POST /reward/new -> calls {@link #create(RewardForm, BindingResult)} to * create the new reward entity and save it to the database. * <li>GET /reward/show/${confirmationNumber} -> calls {@link #show(HttpServletRequest, ModelMap) * 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. */ @Controller @RequestMapping("/reward/*/**") public class RewardController { private static final String FORM_VIEW = "reward/new"; private static final String SHOW_VIEW = "reward/show"; private static final String REDIRECT_SHOW_VIEW = "redirect:show"; /** * The reward network application responsible for creating rewards. */ @Autowired private RewardNetwork rewardNetwork; /** * Responsible for retrieving details on previously confirmed rewards. */ @Autowired private RewardLookupService rewardLookupService; /** * Configures the data binder by setting required fields and registering custom * property editors to be used for display and binding of custom types * @param binder the data binder to configure */ @InitBinder public void initBinder(WebDataBinder binder) { binder.setRequiredFields(new String[] { "creditCardNumber", "amount", "merchantNumber" }); binder.registerCustomEditor(MonetaryAmount.class, new MonetaryAmountEditor()); binder.registerCustomEditor(SimpleDate.class, new SimpleDateEditor()); } /** * 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. * @return a selection of the "new reward" view with the data needed to * render it */ @RequestMapping(value="/reward/new", method = RequestMethod.GET) public ModelAndView newForm() { RewardForm rewardForm = new RewardForm(); WebDataBinder binder = new WebDataBinder(rewardForm, "rewardForm"); initBinder(binder); return new ModelAndView(FORM_VIEW, 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 rewardForm the bound reward form * @param result the binding result object * @return on error(s), the form view logical name, forcing the user back to the * form to correct the error(s). On success, a redirect to the show method of this * controller to display confirm reward details */ @RequestMapping(value="/reward/new", method = RequestMethod.POST) public String create(@ModelAttribute("rewardForm") RewardForm rewardForm, BindingResult result) { if (result.hasErrors()) { return FORM_VIEW; } else { Dining dining = rewardForm.createDining(); RewardConfirmation confirmation = rewardNetwork.rewardAccountFor(dining); return REDIRECT_SHOW_VIEW + "/" + 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 model the model map into which the reward object will be stored * @return a model and view containing the show view name and the reward object */ @RequestMapping(value="/reward/show/*", method = RequestMethod.GET) public ModelAndView show(HttpServletRequest request, ModelMap model) { Reward reward = rewardLookupService.lookupReward(extractConfirmationNumber(request)); WebDataBinder binder = new WebDataBinder(reward, "reward"); initBinder(binder); return new ModelAndView(SHOW_VIEW, 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); } }