Search code examples
springservletsspring-mvcjmesa

jMesa Spring sync error when saving worksheet


I'm working with jMesa framework (a useful table rendering API) and I have encountered an error. When working with worksheets that allow the modifications of data on the air by ajax requests, it sends those requests to a servlet called org.jmesa.worksheet.servlet.WorksheetServlet. This servlet manages the changes made on the table and session variables, so I don't have to worry about it.

The problem starts when pressing the save button. You can make all changes you want but they are not saved until you press this button. Then, jMesa starts the magic and manages the request and session, but it's done in two sevlets at the same time (I think this is the problem). Those are:

  1. The controller which returns the .jsp page (mine)
  2. The jMesa servlet

Don't know why, my servlet is called first when the jMesa should be, because when I render the new view the worksheet must be updated. Here is the code and the problem detailed.

Web.xml

    <!-- Front controller of the application -->
    <servlet>
        <servlet-name>takeme</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!-- Configuration file of the application -->
            <param-name>contextConfigLocation</param-name>
            <param-value>/WEB-INF/configuration/app-config.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>

    <!-- Select what will be mapped by the servlet. In this case everything from /takeme servlet -->
    <servlet-mapping>
        <servlet-name>takeme</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>

    <!-- Servlet used to modify jMesa tables -->
    <servlet>
        <servlet-name>worksheet</servlet-name>
        <servlet-class>org.jmesa.worksheet.servlet.WorksheetServlet</servlet-class>
    </servlet>

    <!-- JMesa worksheet mapping -->
    <servlet-mapping>
        <servlet-name>worksheet</servlet-name>
        <url-pattern>*.wrk</url-pattern>
    </servlet-mapping>

Controller:

@Controller
public class AdminController {

    @Autowired
    private UserManager userManager;

    @RequestMapping(value = "/users")
    public String adminUsersGet(ModelMap model, HttpServletRequest request, HttpServletResponse response){
        //Set users to the model creating the TableModel
        model.addAttribute("users", TableCreator.createUsersTable(request, response, userManager));
        //Add a callback to jMesa save method
        TableModelUtils.saveWorksheet("usersTable", request, new UserWorksheetSaver(userManager, cityManager, businessManager));
        //Return the view
        return "admin/adminusers";
    }
}

Here in the createUsersTable I create the table, that is not relevant because it's an ordinary operation. This method returns a String containing plain html built by jMesa. When you call the render() method, jMesa checks the state of the worksheet and decides how to show the table. Inside render it checks if there is anything not saved (that is true when the other servlet isn't called first).

Here is the save method unimplemented but enough to see the problem.

WorksheetSaver:

public class UserWorksheetSaver implements WorksheetSaver{

    public void saveWorksheet(Worksheet worksheet) {
        Iterator<WorksheetRow> worksheetRows = worksheet.getRows().iterator();
        while (worksheetRows.hasNext()) {
            boolean valid = true;

            WorksheetRow worksheetRow = worksheetRows.next();

            String uniqueValue = worksheetRow.getUniqueProperty().getValue();
            String message = null;

            if (worksheetRow.getRowStatus().equals(WorksheetRowStatus.ADD)) {

            } else if (worksheetRow.getRowStatus().equals(WorksheetRowStatus.MODIFY)) {

            } else if (worksheetRow.getRowStatus().equals(WorksheetRowStatus.REMOVE)) {

            }
            if (valid) {
                //This tells jMesa I have updated all rows
                worksheetRows.remove();
            }
        }
    }
}

Since all remove have executed, the hasChanges() method of jMesa returns false (depends on the size of the rows modified) so everithing goes ok.

So, as I know what is the problem, ¿how can I make jMesa work before my controller?. Note: all the examples in jMesa have been made without annotations and I need it WITH annotations. And I have tested changing the order in web.xml file.

Thanks.


Solution

  • Finally it was not related to servlet problems. When you save a worksheet it sets a flag into the TableModel to true. When someone renders this table, it saves the data before performing any operation. So, what solves the problem is:

    @Controller
    public class AdminController {
    
        @Autowired
        private UserManager userManager;
    
        @RequestMapping(value = "/users")
        public String adminUsersGet(ModelMap model, HttpServletRequest request, HttpServletResponse response){
            //Add a callback to jMesa save method
            TableModelUtils.saveWorksheet("usersTable", request, new UserWorksheetSaver(userManager, cityManager, businessManager));
            //Set users to the model creating the TableModel
            model.addAttribute("users", TableCreator.createUsersTable(request, response, userManager));
            //Return the view
            return "admin/adminusers";
        }
    }
    

    Changing the order of the operations solves then this problem.