Best practice to handle exception within controllers spring mvc

Hi,
Inside the controller i am using DB operations. There is a separate class for Dao for performing it. But it may throw exceptions like : EmptyResultDataAccessException
Since this DB calls are made from within several methods inside the controller. Writing try catch for them doesnt work.
so i can put try/catch inside the dao class itself. But then i need to return proper response code and error message.
what are the possible ways to do it.

One way i found is  to extend my controller with a basecontroller having the following code :

public class BaseController {

    @ExceptionHandler(AppException.class)
    public void handleServerException(HttpServletRequest request, HttpServletResponse response, AppException e){
        try{
            String msg = "Page not found";
            response.getWriter().write(msg);
            response.setStatus(e.getStatusCode());
            response.setContentType(MediaType.TEXT_HTML.toString());

        }catch(IOException ioe){
            //TODO :            logger.error("Unable to process error",ioe);

        }
    }

Open in new window


Then within try/catch i use catch (EmptyResultDataAccessException e) {
            throw new AppException(e.getMessage(), 404, "Not Found");
        }


Please comment on this approach. Are there better ways ?

Thanks
Rohit BajajAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

mccarlIT Business Systems Analyst / Software DeveloperCommented:
I would say that using the @ExceptionHandler annotated method should be the most appropriate, however, I would have that method directly handle the EmptyResultDataAccessException (rather than catching it yourself and just throwing a different Exception). That way you have even less common code in your main controller methods.
0
Rohit BajajAuthor Commented:
Hi,
The exact code which is throwing exception is :

 @Override
    public Snippet getSnippetById(String id) {
        String query = "select id,title,mode,text from snippets where id=?";
        Snippet snippet;
        try {
            snippet = jdbcTemplate.queryForObject(query, new Object[]{id}, new SnippetMaper());
        } catch (EmptyResultDataAccessException e) {
            throw new AppException(e.getMessage(), 404, "Page not found");
        }
        catch (RuntimeException re) {
            throw new AppException(re.getMessage(),500,"Internal Server Error");
        }
        return snippet;
    }

Open in new window


So basically i need to handle two exceptions. That way i will need to write two exception handlers one for Empty Exception and other for RunTimeException ?
0
mccarlIT Business Systems Analyst / Software DeveloperCommented:
Even if you need two exception handlers, it will probably less code than the alternative. I assume that you have more than 2 controller methods (I think I saw in another question of yours at least 5 or 6), so you can remove the try/catch code from all of those controller methods and you only need to add two exception handler methods. Sounds like a win to me. Also, since you have made a BaseController class for other controllers to extend, then you remove all the try/catch code from any controller that you develop and still only have those two exception handler methods. Win win!

However, my next question is... Is your wrapping or handling of a RuntimeException actually necessary? My thought was that Spring will already translate a RuntimeException into a 500 Internal Server Error anyway, without you having to do any extra work. Try it and see what happens.


By the way, what version of Spring are you using? I ask because in later versions, there is a lot of built-in functionality to help you work with responding with status codes and content types, etc. If you can use anything from about Spring 3.1 or later (from memory), you can simplify things a bit.
0

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
Get expert help—faster!

Need expert help—fast? Use the Help Bell for personalized assistance getting answers to your important questions.

Rohit BajajAuthor Commented:
I am using             <spring.version>4.2.1.RELEASE</spring.version>
0
gurpsbassiCommented:
I would say that using the @ExceptionHandler annotated method should be the most appropriate, however, I would have that method directly handle the EmptyResultDataAccessException (rather than catching it yourself and just throwing a different Exception). That way you have even less common code in your main controller methods.


I second that.
0
mccarlIT Business Systems Analyst / Software DeveloperCommented:
Ok, if you can post your full code for your controller(s) I can give you some pointers.

But as a start, your exception handler method should work like this...

public class BaseController {

    @ExceptionHandler(EmptyResultDataAccessException.class)
    public ResponseEntity<String> handleServerException(EmptyResultDataAccessException e){
        return new ResponseEntity<String>("Page not found", HttpStatus.NOT_FOUND);
    }

Open in new window

0
Rohit BajajAuthor Commented:
Thanks. I am posting the full code :
SnippetController :
package org.directi.code.controller;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.map.ObjectMapper;
import org.directi.code.dao.SnippetDao;
import org.directi.code.model.Snippet;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

@Controller
public class SnippetController extends BaseController {

    @Autowired
    SnippetDao snippetDao;

    private static final Logger logger = Logger.getLogger(SnippetController.class);

    @RequestMapping(value = "/")
    public String newSnippet(HttpServletResponse response) throws IOException {
        return "new";
    }

    @RequestMapping(value = "/snippets", method = RequestMethod.POST)
    public void createSnippet(@RequestBody String jsonString, RedirectAttributes redirectAttributes, HttpServletResponse response, HttpServletRequest request) throws IOException {
        Snippet snippet = new ObjectMapper().readValue(jsonString, Snippet.class);
        String uuid = snippetDao.insertData(snippet);
        response.addHeader("Location", "http://" + request.getHeader("Host") + "/snippets/" + uuid);
    }

    @RequestMapping(value = "/snippets/{id}", method = RequestMethod.GET)
    public ModelAndView showSnippet(@PathVariable String id) {
        Snippet snippet = snippetDao.getSnippetById(id);
        String mode = snippet.getMode();
        ObjectMapper objectMapper = new ObjectMapper();
        String extraScripts = "";

        try {
            byte[] jsonData = Files.readAllBytes(Paths.get("utils/meta.json"));
            JsonNode rootNode = objectMapper.readTree(jsonData);
            JsonNode deps = rootNode.path("deps");
            JsonNode modeString = deps.path(mode);
            String jsFileList = modeString.getTextValue();
            String[] jsFileNames = jsFileList.split(",");
            for (int i = 0; i < jsFileNames.length; i++) {
                extraScripts += "<script src=\"/resources/lib/codemirror" + jsFileNames[i] + "\"></script>\n";
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        ModelAndView mav = new ModelAndView("snippet");
        snippet.setText(StringUtils.replaceEach(snippet.getText(), new String[]{"&", "\"", "<", ">"}, new String[]{"&amp;", "&quot;", "&lt;", "&gt;"}));
        mav.addObject("snippet", snippet);
        mav.addObject("extraScripts", extraScripts);
        return mav;
    }

    @RequestMapping(value = "/snippets/{id}", method = RequestMethod.PUT)
    public void showSnippet(@RequestBody String jsonString, @PathVariable String id, HttpServletResponse response) {
        try {
            Snippet snippet = new ObjectMapper().readValue(jsonString, Snippet.class);
            snippet.setId(id);
            snippetDao.updateData(snippet);
            response.setStatus(200);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @RequestMapping(value = "/snippets/{id}/edit", method = RequestMethod.GET)
    public ModelAndView editSnippet(@PathVariable String id) {
        Snippet snippet = snippetDao.getSnippetById(id);
        snippet.setText(StringUtils.replaceEach(snippet.getText(), new String[]{"&", "\"", "<", ">"}, new String[]{"&amp;", "&quot;", "&lt;", "&gt;"}));
        ModelAndView mav = new ModelAndView("edit");
        mav.addObject("snippet", snippet);
        return mav;
    }

    @RequestMapping(value = "/snippets/{id}/raw", method = RequestMethod.GET)
    @ResponseBody
    public String rawSnippet(@PathVariable String id, HttpServletResponse response) {
        Snippet snippet = snippetDao.getSnippetById(id);
        String filename = snippetFilename(snippet.getTitle(), snippet.getMode());
        response.setHeader("Content-Type", "text/plain");
        response.setHeader("Content-Disposition", "inline; filename=\"" + filename + "\"");
        return snippet.getText();
    }

    private String snippetFilename(String title, String mode) {

        String filename = title.replaceAll("\\s", "_").replaceAll("\\W", "");
        ObjectMapper objectMapper = new ObjectMapper();
        String extension = "";
        try {
            byte[] jsonData = Files.readAllBytes(Paths.get("utils/meta.json"));
            JsonNode rootNode = objectMapper.readTree(jsonData);
            JsonNode extensions = rootNode.path("extensions");
            JsonNode extensionNode = extensions.path(mode);
            extension = extensionNode.getTextValue();

        } catch (IOException e) {
            e.printStackTrace();
        }
        filename += "." + extension;
        return filename;
    }
}

Open in new window


BaseController.java :

package org.directi.code.controller;


import org.apache.log4j.Logger;
import org.directi.code.exception.AppException;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.ExceptionHandler;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public abstract class BaseController {
    private static final Logger logger  = Logger.getLogger(BaseController.class);

    @ExceptionHandler(AppException.class)
    public void handleServerException(HttpServletRequest request, HttpServletResponse response, AppException e){
        try{
            response.getWriter().write(e.getErrMessage());
            response.setStatus(e.getStatusCode());
            response.setContentType(MediaType.TEXT_HTML.toString());

        }catch(IOException ioe){
            logger.error("Unable to process error",ioe);

        }
    }
}

Open in new window


SnippetDaoImpl.java
package org.directi.code.dao;

import org.apache.log4j.Logger;
import org.directi.code.model.Snippet;
import org.directi.code.exception.AppException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowMapper;
import org.springframework.stereotype.Component;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.UUID;

@Component
public class SnippetDaoImpl implements SnippetDao {
    @Autowired
    private JdbcTemplate jdbcTemplate;

    private static final Logger logger = Logger.getLogger(SnippetDaoImpl.class);

    public void setJdbcTemplate(JdbcTemplate template) {
        this.jdbcTemplate = template;
    }

    @Override
    public String insertData(Snippet snippet) {
        UUID uuid = UUID.randomUUID();
        String randomUUIDString = uuid.toString();
        snippet.setId(randomUUIDString);
        jdbcTemplate.update("INSERT INTO snippets (id, title, mode, text) VALUES (?, ?, ?, ?)", snippet.getId(), snippet.getTitle(), snippet.getMode(), snippet.getText());
        return randomUUIDString;
    }

    @Override
    public void updateData(Snippet snippet) {
        jdbcTemplate.update("UPDATE snippets set text = ? where id = ?", snippet.getText(), snippet.getId());
    }

    @Override
    public Snippet getSnippetById(String id) {
        String query = "select id,title,mode,text from snippets where id=?";
        Snippet snippet;
        try {
            snippet = jdbcTemplate.queryForObject(query, new Object[]{id}, new SnippetMaper());
        } catch (EmptyResultDataAccessException e) {
            logger.error("No records found",e);
            throw new AppException(e.getMessage(), 404, "Page not found");
        }
        catch (RuntimeException e) {
            logger.error("Error retrieving data from db",e);
            throw new AppException(e.getMessage(),500,"Internal Server Error");
        }
        return snippet;
    }

    private static final class SnippetMaper implements RowMapper<Snippet> {
        public Snippet mapRow(ResultSet rs, int rowNum) throws SQLException {
            Snippet snippet = new Snippet();
            snippet.setText(rs.getString("text"));
            snippet.setId(rs.getString("id"));
            snippet.setMode(rs.getString("mode"));
            snippet.setTitle(rs.getString("title"));
            return snippet;
        }
    }
}

Open in new window

0
mccarlIT Business Systems Analyst / Software DeveloperCommented:
Ok, your controller code doesn't look too bad at the moment. I think if you try an implement the things that I have mentioned (across a number of your questions) it would look pretty clean. The things I am talking about are.... using Spring automatic RequestBody conversion using Jackson, moving the meta.json data handling to a separate class, and using ResponseEntity rather than dealing directly with the HttpServletResponse object.
0
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Web Applications

From novice to tech pro — start learning today.

Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.