Package com.alibaba.citrus.service.requestcontext.session.store.cookie.impl

Source Code of com.alibaba.citrus.service.requestcontext.session.store.cookie.impl.SingleValuedCookieStoreImpl$State

/*
* Copyright (c) 2002-2012 Alibaba Group Holding Limited.
* All rights reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*    http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.alibaba.citrus.service.requestcontext.session.store.cookie.impl;

import static com.alibaba.citrus.util.ArrayUtil.*;
import static com.alibaba.citrus.util.Assert.*;
import static com.alibaba.citrus.util.CollectionUtil.*;
import static com.alibaba.citrus.util.StringUtil.*;

import java.util.List;
import java.util.Map;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;

import com.alibaba.citrus.service.requestcontext.session.ExactMatchesOnlySessionStore;
import com.alibaba.citrus.service.requestcontext.session.store.cookie.AbstractCookieStore;
import com.alibaba.citrus.service.requestcontext.session.valueencoder.SessionValueEncoder;
import com.alibaba.citrus.service.requestcontext.session.valueencoder.impl.SimpleValueEncoder;
import com.alibaba.citrus.util.ToStringBuilder;
import com.alibaba.citrus.util.ToStringBuilder.MapBuilder;

/**
* 将Session状态保存在cookie中。
* <ul>
* <li>每个store只能保存一个值。</li>
* <li>将仅有的session attribute value用<code>SessionValueEncoder</code>编码成字符串。</li>
* </ul>
*
* @author Michael Zhou
*/
public class SingleValuedCookieStoreImpl extends AbstractCookieStore implements ExactMatchesOnlySessionStore {
    private String[]              attrNames;
    private SessionValueEncoder[] encoders;

    public void initAttributeNames(String[] attrNames) {
        this.attrNames = attrNames;
        assertTrue(attrNames.length <= 1, "Session store %s supports only 1 mapping to attribute name", getName());
    }

    public void setValueEncoders(SessionValueEncoder[] encoders) {
        this.encoders = encoders;
    }

    @Override
    protected void init() throws Exception {
        if (isEmptyArray(encoders)) {
            encoders = new SessionValueEncoder[] { createDefaultSessionValueEncoder() };
        }
    }

    protected SessionValueEncoder createDefaultSessionValueEncoder() throws Exception {
        SimpleValueEncoder simpleValueEncoder = new SimpleValueEncoder();
        simpleValueEncoder.afterPropertiesSet();
        return simpleValueEncoder;
    }

    public Iterable<String> getAttributeNames(String sessionID, StoreContext storeContext) {
        State state = getState(storeContext);
        return state.attributes.keySet();
    }

    public Object loadAttribute(String attrName, String sessionID, StoreContext storeContext) {
        State state = getState(storeContext);
        return state.attributes.get(attrName);
    }

    public void invalidate(String sessionID, StoreContext storeContext) {
        State state = getState(storeContext);

        if (!isSurvivesInInvalidating()) {
            state.attributes.clear();
        }
    }

    public void commit(Map<String, Object> modifiedAttrs, String sessionID, StoreContext storeContext) {
        State state = getState(storeContext);

        if (state.cookieCommitted) {
            return;
        }

        state.cookieCommitted = true;

        String attrName = attrNames[0];

        if (modifiedAttrs.containsKey(attrName)) {
            Object attrValue = modifiedAttrs.get(attrName);

            if (attrValue == null) {
                if (log.isDebugEnabled()) {
                    log.debug("Remove from session: {}", attrName);
                }

                state.attributes.remove(attrName);
            } else {
                if (log.isDebugEnabled()) {
                    log.debug("Set to session: {} = {}", attrName, attrValue);
                }

                state.attributes.put(attrName, attrValue);
            }
        }

        String cookieState = null;

        if (!state.attributes.isEmpty()) {
            try {
                cookieState = encoders[0].encode(state.attributes.get(attrName), storeContext);
            } catch (Exception e) {
                log.warn("Failed to encode session state", e);
            }
        }

        cookieState = trimToEmpty(cookieState);

        writeCookie(storeContext.getSessionRequestContext().getResponse(), getName(), cookieState);
    }

    /** 取得cookie store的状态。 */
    private State getState(StoreContext storeContext) {
        State state = (State) storeContext.getState();

        if (state == null) {
            state = new State();
            storeContext.setState(state);
        }

        ensureCookieLoading(state, storeContext.getSessionRequestContext().getRequest(), storeContext);
        return state;
    }

    /** 确保cookie被装载。 */
    private void ensureCookieLoading(State state, HttpServletRequest request, StoreContext storeContext) {
        if (state.cookieLoaded) {
            return;
        }

        state.cookieLoaded = true;

        // 读取cookie
        state.requestCookieValue = readCookie(request);

        // 依次使用所有encoders,试着对cookieValue解码,如果失败,则返回空表
        // 如果成功,则返回单值map。
        state.attributes = decodeCookieValue(state.requestCookieValue, storeContext);
    }

    /** 读取cookies。 */
    private String readCookie(HttpServletRequest request) {
        Cookie[] cookies = request.getCookies();

        if (cookies == null) {
            cookies = new Cookie[0];
        }

        // 扫描cookie。
        String cookieValue = null;

        for (Cookie cookie : cookies) {
            String cookieName = cookie.getName();

            if (getName().equals(cookieName)) {
                cookieValue = cookie.getValue();

                if (log.isDebugEnabled()) {
                    log.debug("[{}] Loading cookie: {}[length={}]={}", new Object[] { getStoreName(), getName(),
                                                                                      cookie.getValue().length(), cookie.getValue() });
                }

                break;
            }
        }

        return cookieValue;
    }

    private Map<String, Object> decodeCookieValue(String cookieValue, StoreContext storeContext) {
        Map<String, Object> attrs = createHashMap(4);

        if (cookieValue == null) {
            return attrs; // empty map
        }

        List<Exception> encoderExceptions = null;

        for (SessionValueEncoder encoder : encoders) {
            try {
                attrs.put(attrNames[0], encoder.decode(cookieValue, storeContext));
                log.debug("Succeeded decoding cookieValue using {}", encoder);
                break;
            } catch (Exception e) {
                if (encoderExceptions == null) {
                    encoderExceptions = createLinkedList();
                }

                encoderExceptions.add(e);
                log.trace("Failure decoding cookieValue using {}: {}", encoder, e.getMessage());
            }
        }

        // 如果失败,记录日志
        if (attrs.isEmpty() && encoderExceptions != null) {
            if (log.isWarnEnabled()) {
                ToStringBuilder buf = new ToStringBuilder();

                buf.append("Failed to decode cookie value: ").append(cookieValue);

                int i = 0;
                for (Exception e : encoderExceptions) {
                    buf.format("\n  Encoder #%d - %s threw %s", i + 1, encoders[i].getClass().getSimpleName(), e);
                }

                log.warn(buf.toString());
            }
        } else {
            if (log.isDebugEnabled()) {
                int attrCount = attrs.size();
                ToStringBuilder buf = new ToStringBuilder();

                buf.format("Found %d attributes:", attrCount);

                if (!attrs.isEmpty()) {
                    buf.append(new MapBuilder().setPrintCount(true).setSortKeys(true).appendAll(attrs));
                }

                log.debug(buf.toString());
            }
        }

        return attrs;
    }

    @Override
    protected void toString(MapBuilder mb) {
        super.toString(mb);
        mb.append("encoders", encoders);
    }

    /** 存放cookie的状态。 */
    private class State {
        private boolean             cookieLoaded;
        private boolean             cookieCommitted;
        private String              requestCookieValue;
        private Map<String, Object> attributes;
    }
}
TOP

Related Classes of com.alibaba.citrus.service.requestcontext.session.store.cookie.impl.SingleValuedCookieStoreImpl$State

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.