StoreAttribute method in SessionAttributeStore implementation not called in Spring 4.0 using @SessionAttributes

时间:2015-06-15 15:05:11

标签: java spring session spring-mvc browser-tab

I'm trying to work with different models in differnt tabs with Spring 4.0. I use the solution outlined by duckranger here -> https://github.com/duckranger/Spring-MVC-conversation

My Controller looks like this:

@Controller
@RequestMapping("/counter")
@SessionAttributes("mycounter")
public class CounterController {

    @ModelAttribute("mycounter")
    public MyCounter populateCounter(){
        return new MyCounter(0);
    }

    @RequestMapping(method = RequestMethod.GET)
    public String get(@ModelAttribute("mycounter") MyCounter mycntr) {
        return "counter";
    }

    // Obtain 'mycounter' object for this user's session and increment it
    @RequestMapping(method = RequestMethod.POST)
    public String post(@ModelAttribute("mycounter") MyCounter myCounter) {
        myCounter.increment();
        return "redirect:/counter";
    }
}

root-context.xml:

<bean id="requestMappingHandlerAdapter"
    class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="sessionAttributeStore">
        <ref bean="conversationalSessionAttributeStore" />
    </property>
</bean>

<bean id="conversationalSessionAttributeStore"
    class="de.telekom.cldb.admin.security.ConversationalSessionAttributeStore">
    <property name="keepAliveConversations" value="10" />
    <property name="requestMappingHandlerAdapter">
        <ref bean="requestMappingHandlerAdapter" />
    </property>     
</bean>

Implementation of SessionAttributeStore is exactlty as in the code by duckranger:

public class ConversationalSessionAttributeStore implements SessionAttributeStore, InitializingBean {

    @Inject
    private RequestMappingHandlerAdapter requestMappingHandlerAdapter;  
    private static final Logger logger = Logger.getLogger(ConversationalSessionAttributeStore.class);

    private int keepAliveConversations = 10;

    public final static String CID_FIELD = "_cid";
    public final static String SESSION_MAP = "sessionConversationMap";

    @Override
    public void storeAttribute(WebRequest request, String attributeName, Object attributeValue) {
        Assert.notNull(request, "WebRequest must not be null");
        Assert.notNull(attributeName, "Attribute name must not be null");
        Assert.notNull(attributeValue, "Attribute value must not be null");

        String cId = getConversationId(request);
        if (cId == null || cId.trim().length() == 0) {
            cId = UUID.randomUUID().toString();
        }
        request.setAttribute(CID_FIELD, cId, WebRequest.SCOPE_REQUEST);
        logger.debug("storeAttribute - storing bean reference for (" + attributeName + ").");
        store(request, attributeName, attributeValue, cId);
    }

    @Override
    public Object retrieveAttribute(WebRequest request, String attributeName) {
        Assert.notNull(request, "WebRequest must not be null");
        Assert.notNull(attributeName, "Attribute name must not be null");

        if (getConversationId(request) != null) {
            if (logger.isDebugEnabled()) {
                logger.debug("retrieveAttribute - retrieving bean reference for (" + attributeName + ") for conversation (" + getConversationId(request) + ").");
            }
            return getConversationStore(request, getConversationId(request)).get(attributeName);
        } else {
            return null;
        }
    }

    @Override
    public void cleanupAttribute(WebRequest request, String attributeName) {
        Assert.notNull(request, "WebRequest must not be null");
        Assert.notNull(attributeName, "Attribute name must not be null");

        if (logger.isDebugEnabled()) {
            logger.debug("cleanupAttribute - removing bean reference for (" + attributeName + ") from conversation (" + getConversationId(request) + ").");
        }

        Map<String, Object> conversationStore = getConversationStore(request, getConversationId(request));
        conversationStore.remove(attributeName);

        // Delete the conversation store from the session if empty
        if (conversationStore.isEmpty()) {
            getSessionConversationsMap(request).remove(getConversationId(request));
        }
    }

    /**
     * Retrieve a specific conversation's map of objects from the session. Will create the conversation map if it does not exist.
     * 
     * The conversation map is stored inside a session map - which is a map of maps. If this does not exist yet- it will be created too.
     * 
     * @param request
     * - the incoming request
     * @param conversationId
     * - the conversation id we are dealing with
     * @return - the conversation's map
     */
    private Map<String, Object> getConversationStore(WebRequest request, String conversationId) {

        Map<String, Object> conversationMap = getSessionConversationsMap(request).get(conversationId);
        if (conversationId != null && conversationMap == null) {
            conversationMap = new HashMap<String, Object>();
            getSessionConversationsMap(request).put(conversationId, conversationMap);
        }
        return conversationMap;
    }

    /**
     * Get the session's conversations map.
     * 
     * @param request
     * - the request
     * @return - LinkedHashMap of all the conversations and their maps
     */
    private LinkedHashMap<String, Map<String, Object>> getSessionConversationsMap(WebRequest request) {
        @SuppressWarnings("unchecked")
        LinkedHashMap<String, Map<String, Object>> sessionMap = (LinkedHashMap<String, Map<String, Object>>) request.getAttribute(SESSION_MAP, WebRequest.SCOPE_SESSION);
        if (sessionMap == null) {
            sessionMap = new LinkedHashMap<String, Map<String, Object>>();
            request.setAttribute(SESSION_MAP, sessionMap, WebRequest.SCOPE_SESSION);
        }
        return sessionMap;
    }

    /**
     * Store an object on the session. If the configured maximum number of live conversations to keep is reached - clear out the oldest conversation. (If max number is configured as 0 - no removal will happen)
     * 
     * @param request
     * - the web request
     * @param attributeName
     * - the name of the attribute (from @SessionAttributes)
     * @param attributeValue
     * - the value to store
     */
    private void store(WebRequest request, String attributeName, Object attributeValue, String cId) {
        LinkedHashMap<String, Map<String, Object>> sessionConversationsMap = getSessionConversationsMap(request);
        if (keepAliveConversations > 0 && sessionConversationsMap.size() >= keepAliveConversations && !sessionConversationsMap.containsKey(cId)) {
            // clear oldest conversation
            String key = sessionConversationsMap.keySet().iterator().next();
            sessionConversationsMap.remove(key);
        }
        getConversationStore(request, cId).put(attributeName, attributeValue);

    }

    public int getKeepAliveConversations() {
        return keepAliveConversations;
    }

    public void setKeepAliveConversations(int numConversationsToKeep) {
        keepAliveConversations = numConversationsToKeep;
    }

    /**
     * Helper method to get conversation id from the web request
     * 
     * @param request
     * - Incoming request
     * @return - the conversationId (note that this is a request parameter, and only gets there on form submit)
     */
    private String getConversationId(WebRequest request) {
        String cid = request.getParameter(CID_FIELD);
        if (cid == null) {
            cid = (String) request.getAttribute(CID_FIELD, WebRequest.SCOPE_REQUEST);
        }
        return cid;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        requestMappingHandlerAdapter.setSessionAttributeStore(this);
    }

    public RequestMappingHandlerAdapter getRequestMappingHandlerAdapter() {
        return requestMappingHandlerAdapter;
    }

    public void setRequestMappingHandlerAdapter(RequestMappingHandlerAdapter requestMappingHandlerAdapter) {
        this.requestMappingHandlerAdapter = requestMappingHandlerAdapter;
    }
}

counter.jsp looks like this:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<html>
<head>
    <meta http-equiv="content-type" content="text/html; charset=UTF-8">
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>Counter</title>
</head>
<body>
    <p>${mycounter.count}</p>
    <form action="counter" method="post">
        <input type="submit">
    </form>
</body>
</html>

My problem is that the storeAttribute method in the SessionAttributeStore implementation is not being called at all. Also, on POST request the value of the count member variable is being overwritten when I open an additional tab, but that's probably because the session map logic isn't invoked.

What am I missing here? Thank you all for the help in advance. al

edit #1 I've realised now that the DefaultSessionAttributeStore's method storeAttribute is called. So it seems to me that I'm not configuring my custom implementation properly.

0 个答案:

没有答案