001/*
002 * (C) Copyright 2006-2008 Nuxeo SA (http://nuxeo.com/) and others.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 *
016 * Contributors:
017 *     bstefanescu
018 *
019 * $Id$
020 */
021
022package org.nuxeo.ecm.webengine.session;
023
024import java.security.Principal;
025import java.util.HashMap;
026import java.util.Map;
027
028import javax.servlet.http.HttpServletRequest;
029import javax.servlet.http.HttpSession;
030
031import org.apache.commons.logging.Log;
032import org.apache.commons.logging.LogFactory;
033import org.nuxeo.ecm.webengine.jaxrs.context.RequestCleanupHandler;
034import org.nuxeo.ecm.webengine.jaxrs.context.RequestContext;
035
036/**
037 * Used to store user session. This object is cached in a the HTTP session Principal, subject and credentials are
038 * immutable per user session
039 *
040 * @author <a href="mailto:bs@nuxeo.com">Bogdan Stefanescu</a>
041 */
042// TODO: should be synchronized? concurrent access may happen for the same
043// session
044public final class UserSession extends HashMap<String, Object> {
045
046    private static final long serialVersionUID = 260562970988817064L;
047
048    protected static final Log log = LogFactory.getLog(UserSession.class);
049
050    protected Map<Class<?>, ComponentMap<?>> comps = new HashMap<Class<?>, ComponentMap<?>>();
051
052    protected HttpServletRequest request;
053
054    protected UserSession(HttpServletRequest request) {
055        this.request = request;
056    }
057
058    public static UserSession getCurrentSession(HttpServletRequest request) {
059        String key = UserSession.class.getName();
060        HttpSession session = request.getSession(false);
061        UserSession us = null;
062        if (session != null) {
063            us = (UserSession) session.getAttribute(key);
064        }
065        if (us == null) {
066            us = (UserSession) request.getAttribute(key);
067        }
068        if (us == null) {
069            us = new UserSession(request);
070            if (session != null) {
071                session.setAttribute(key, us);
072            } else {
073                request.setAttribute(key, us);
074            }
075        }
076        return us;
077    }
078
079    public Principal getPrincipal() {
080        return request.getUserPrincipal();
081    }
082
083    /**
084     * Register a cleanup handler that will be invoked when HTTP request terminate. This method is not thread safe.
085     */
086    public static void addRequestCleanupHandler(HttpServletRequest request, RequestCleanupHandler handler) {
087        RequestContext.getActiveContext(request).addRequestCleanupHandler(handler);
088    }
089
090    /**
091     * Finds an existing component.
092     * <p>
093     * The component state will not be modified before being returned as in {@link #getComponent(Class, String)}.
094     * <p>
095     * If the component was not found in that session, returns null.
096     */
097    @SuppressWarnings("unchecked")
098    public synchronized <T extends Component> T findComponent(Class<T> type, String name) {
099        ComponentMap<T> map = (ComponentMap<T>) comps.get(type);
100        if (map == null) {
101            return null;
102        }
103        if (name == null) {
104            return map.getComponent();
105        } else {
106            return type.cast(map.get(name));
107        }
108    }
109
110    /**
111     * Gets a component given its class and an optional name.
112     * <p>
113     * If the component was not yet created in this session, it will be created and registered against the session.
114     */
115    @SuppressWarnings("unchecked")
116    public synchronized <T extends Component> T getComponent(Class<T> type, String name) throws SessionException {
117        ComponentMap<T> map = (ComponentMap<T>) comps.get(type);
118        T comp;
119        if (map == null) {
120            map = new ComponentMap<T>();
121            comps.put(type, map);
122        } else {
123            if (name == null) {
124                comp = map.getComponent();
125            } else {
126                comp = type.cast(map.get(name));
127            }
128            if (comp != null) {
129                return comp;
130            }
131        }
132        // component not found
133        try {
134            comp = type.newInstance();
135        } catch (ReflectiveOperationException e) {
136            throw new SessionException("Failed to instantiate component: " + type, e);
137        }
138        comp.initialize(this, name);
139        if (name == null) {
140            map.setComponent(comp);
141        } else {
142            map.put(name, comp);
143        }
144        return type.cast(comp);
145    }
146
147    public <T extends Component> T getComponent(Class<T> type) throws SessionException {
148        return getComponent(type, null);
149    }
150
151    @SuppressWarnings("unchecked")
152    public <T extends Component> T getComponent(String typeName, String name) throws SessionException {
153        try {
154            Class<T> type = (Class<T>) Class.forName(typeName);
155            return getComponent(type, name);
156        } catch (ClassNotFoundException e) {
157            throw new SessionException("Could not find component class: " + typeName, e);
158        }
159    }
160
161    /**
162     * Gets component by ID.
163     * <p>
164     * The ID is of the form <code>type#name</code> for non-null names and <code>type</code> for null names.
165     */
166    @SuppressWarnings("unchecked")
167    public <T extends Component> T getComponent(String id) throws SessionException {
168        int p = id.lastIndexOf('#');
169        if (p > -1) {
170            return (T) getComponent(id.substring(0, p), id.substring(p + 1));
171        } else {
172            return (T) getComponent(id, null);
173        }
174    }
175
176}