/******************************************************************************* * Copyright (c) 2012 OpenLegacy Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * OpenLegacy Inc. - initial API and implementation *******************************************************************************/ package org.openlegacy.terminal.mvc.rest; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.exolab.castor.xml.MarshalException; import org.exolab.castor.xml.Unmarshaller; import org.exolab.castor.xml.ValidationException; import org.openlegacy.exceptions.EntityNotFoundException; import org.openlegacy.exceptions.RegistryException; import org.openlegacy.modules.login.Login; import org.openlegacy.modules.login.LoginException; import org.openlegacy.modules.menu.Menu; import org.openlegacy.modules.menu.MenuItem; import org.openlegacy.modules.navigation.Navigation; import org.openlegacy.terminal.ScreenEntity; import org.openlegacy.terminal.TerminalSession; import org.openlegacy.terminal.definitions.ScreenEntityDefinition; import org.openlegacy.terminal.json.ScreenEntitySerializationUtils; import org.openlegacy.terminal.modules.trail.TrailUtil; import org.openlegacy.terminal.services.ScreenEntitiesRegistry; import org.openlegacy.terminal.support.SimpleScreenEntityWrapper; import org.openlegacy.terminal.utils.ScreenEntityUtils; import org.openlegacy.utils.ProxyUtil; import org.springframework.stereotype.Controller; import org.springframework.ui.ModelMap; 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.servlet.ModelAndView; import org.xml.sax.InputSource; import java.io.ByteArrayInputStream; import java.io.IOException; import java.text.MessageFormat; import javax.inject.Inject; import javax.servlet.http.HttpServletResponse; /** * OpenLegacy default REST API controller. Handles GET/POST requests in the format of JSON or XML. Also handles login /logoff of * the host session * * @author Roi Mor * */ @Controller public class DefaultRestController { private static final String JSON = "application/json"; private static final String XML = "application/xml"; private static final String SCREEN_MODEL = "screenModel"; private static final String ACTION = "action"; private final static Log logger = LogFactory.getLog(DefaultRestController.class); private static final String USER = "user"; private static final String PASSWORD = "password"; /** * Whether to perform login on session start. Can be overridden from /application.properties * defaultRestController.requiresLogin=true */ private boolean requiresLogin = false; @Inject private TerminalSession terminalSession; @Inject private ScreenEntitiesRegistry screenEntitiesRegistry; @Inject private ScreenEntityUtils screenEntityUtils; @Inject private TrailUtil trailUtil; @RequestMapping(value = "/login", consumes = { JSON, XML }) public void login(@RequestParam(USER) String user, @RequestParam(PASSWORD) String password, HttpServletResponse response) throws IOException { try { terminalSession.getModule(Login.class).login(user, password); } catch (RegistryException e) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); } catch (LoginException e) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED, e.getMessage()); } response.setStatus(HttpServletResponse.SC_OK); } @RequestMapping(value = "/{screen}", method = RequestMethod.GET, consumes = { JSON, XML }) public ModelAndView getScreenEntity(@PathVariable("screen") String screenEntityName, HttpServletResponse response) throws IOException { return getEntityRequest(screenEntityName, null, response); } @RequestMapping(value = "/{screen}/{key}", method = RequestMethod.GET, consumes = { JSON, XML }) public ModelAndView getScreenEntityWithKey(@PathVariable("screen") String screenEntityName, @PathVariable("key") String key, HttpServletResponse response) throws IOException { return getEntityRequest(screenEntityName, key, response); } private ModelAndView getEntityRequest(String screenEntityName, Object key, HttpServletResponse response) throws IOException { if (!authenticate(response)) { return null; } try { ScreenEntity screenEntity; if (key == null) { screenEntity = (ScreenEntity)terminalSession.getEntity(screenEntityName); } else { screenEntity = (ScreenEntity)terminalSession.getEntity(screenEntityName, key); } return getEntityInner(screenEntity); } catch (EntityNotFoundException e) { response.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage()); return null; } } @RequestMapping(value = "/", method = RequestMethod.GET, consumes = { JSON, XML }) public ModelAndView getScreenEntity(HttpServletResponse response) throws IOException { if (!authenticate(response)) { return null; } ScreenEntity screenEntity = terminalSession.getEntity(); return getEntityInner(screenEntity); } private ModelAndView getEntityInner(ScreenEntity screenEntity) { screenEntity = ProxyUtil.getTargetObject(screenEntity); ScreenEntityDefinition entityDefinitions = screenEntitiesRegistry.get(screenEntity.getClass()); SimpleScreenEntityWrapper wrapper = new SimpleScreenEntityWrapper(screenEntity, terminalSession.getModule( Navigation.class).getPaths(), entityDefinitions.getActions()); return new ModelAndView(SCREEN_MODEL, SCREEN_MODEL, wrapper); } @RequestMapping(value = "/menu", method = RequestMethod.GET, consumes = { JSON, XML }) public Object getMenu(ModelMap model) { MenuItem menus = terminalSession.getModule(Menu.class).getMenuTree(); return menus; } /** * Accepts a post request in JSON format, de-serialize it to a screen entity, and send it to the host * * @param screenEntityName * @param action * @param json * @param response * Return HTTP OK (200) is success * @throws IOException */ @RequestMapping(value = "/{screen}", method = RequestMethod.POST, consumes = JSON) public void postScreenEntityJson(@PathVariable("screen") String screenEntityName, @RequestParam(ACTION) String action, @RequestBody String json, HttpServletResponse response) throws IOException { if (!authenticate(response)) { return; } Class<?> entityClass = findAndHandleNotFound(screenEntityName, response); if (entityClass == null) { return; } ScreenEntity screenEntity = null; try { screenEntity = (ScreenEntity)ScreenEntitySerializationUtils.deserialize(json, entityClass); } catch (Exception e) { handleDeserializationException(screenEntityName, response, e); return; } screenEntityUtils.sendScreenEntity(terminalSession, screenEntity, action); response.setStatus(HttpServletResponse.SC_OK); } @RequestMapping(value = "/{screen}", method = RequestMethod.POST, consumes = XML) public void postScreenEntityXml(@PathVariable("screen") String screenEntityName, @RequestParam(ACTION) String action, @RequestBody String xml, HttpServletResponse response) throws IOException { if (!authenticate(response)) { return; } Class<?> entityClass = findAndHandleNotFound(screenEntityName, response); if (entityClass == null) { return; } ScreenEntity screenEntity = null; try { InputSource inputSource = new InputSource(new ByteArrayInputStream(xml.getBytes())); screenEntity = (ScreenEntity)Unmarshaller.unmarshal(entityClass, inputSource); } catch (MarshalException e) { handleDeserializationException(screenEntityName, response, e); return; } catch (ValidationException e) { handleDeserializationException(screenEntityName, response, e); return; } screenEntityUtils.sendScreenEntity(terminalSession, screenEntity, action); response.setStatus(HttpServletResponse.SC_OK); } private boolean authenticate(HttpServletResponse response) throws IOException { if (!requiresLogin) { return true; } if (!terminalSession.getModule(Login.class).isLoggedIn()) { response.sendError(HttpServletResponse.SC_UNAUTHORIZED); } return true; } private static void handleDeserializationException(String screenEntityName, HttpServletResponse response, Exception e) throws IOException { String message = MessageFormat.format("Unable to desirialize entity {0}", screenEntityName); response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); logger.fatal(message, e); return; } /** * Look for the given screen entity in the registry, and return HTTP 400 (BAD REQUEST) in case it's not found * * @param screenEntityName * @param response * @return * @throws IOException */ private Class<?> findAndHandleNotFound(String screenEntityName, HttpServletResponse response) throws IOException { Class<?> entityClass = screenEntitiesRegistry.getEntityClass(screenEntityName); if (entityClass == null) { String message = MessageFormat.format("Screen entity {0} not found", screenEntityName); response.sendError(HttpServletResponse.SC_BAD_REQUEST, message); logger.error(message); } return entityClass; } public void setRequiresLogin(boolean requiresLogin) { this.requiresLogin = requiresLogin; } }