001    /*
002     * Licensed to the Apache Software Foundation (ASF) under one
003     * or more contributor license agreements.  See the NOTICE file
004     * distributed with this work for additional information
005     * regarding copyright ownership.  The ASF licenses this file
006     * to you under the Apache License, Version 2.0 (the
007     * "License"); you may not use this file except in compliance
008     * with the License.  You may obtain a copy of the License at
009     *
010     *     http://www.apache.org/licenses/LICENSE-2.0
011     *
012     * Unless required by applicable law or agreed to in writing,
013     * software distributed under the License is distributed on an
014     * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
015     * KIND, either express or implied.  See the License for the
016     * specific language governing permissions and limitations
017     * under the License.
018     */
019    package org.apache.shiro.web.servlet;
020    
021    import org.apache.shiro.session.InvalidSessionException;
022    import org.apache.shiro.session.Session;
023    import org.apache.shiro.web.session.HttpServletSession;
024    
025    import javax.servlet.ServletContext;
026    import javax.servlet.http.HttpServletRequest;
027    import javax.servlet.http.HttpSession;
028    import javax.servlet.http.HttpSessionBindingEvent;
029    import javax.servlet.http.HttpSessionBindingListener;
030    import java.util.*;
031    
032    
033    /**
034     * Wrapper class that uses a Shiro {@link Session Session} under the hood for all session operations instead of the
035     * Servlet Container's session mechanism.  This is required in heterogeneous client environments where the Session
036     * is used on both the business tier as well as in multiple client technologies (web, swing, flash, etc) since
037     * Servlet container sessions alone cannot support this feature.
038     *
039     * @since 0.2
040     */
041    public class ShiroHttpSession implements HttpSession {
042    
043        //TODO - complete JavaDoc
044    
045        public static final String DEFAULT_SESSION_ID_NAME = "JSESSIONID";
046    
047        private static final Enumeration EMPTY_ENUMERATION = new Enumeration() {
048            public boolean hasMoreElements() {
049                return false;
050            }
051    
052            public Object nextElement() {
053                return null;
054            }
055        };
056    
057        @SuppressWarnings({"deprecation"})
058        private static final javax.servlet.http.HttpSessionContext HTTP_SESSION_CONTEXT =
059                new javax.servlet.http.HttpSessionContext() {
060                    public HttpSession getSession(String s) {
061                        return null;
062                    }
063    
064                    public Enumeration getIds() {
065                        return EMPTY_ENUMERATION;
066                    }
067                };
068    
069        protected ServletContext servletContext = null;
070        protected HttpServletRequest currentRequest = null;
071        protected Session session = null; //'real' Shiro Session
072    
073        public ShiroHttpSession(Session session, HttpServletRequest currentRequest, ServletContext servletContext) {
074            if (session instanceof HttpServletSession) {
075                String msg = "Session constructor argument cannot be an instance of HttpServletSession.  This is enforced to " +
076                        "prevent circular dependencies and infinite loops.";
077                throw new IllegalArgumentException(msg);
078            }
079            this.session = session;
080            this.currentRequest = currentRequest;
081            this.servletContext = servletContext;
082        }
083    
084        public Session getSession() {
085            return this.session;
086        }
087    
088        public long getCreationTime() {
089            try {
090                return getSession().getStartTimestamp().getTime();
091            } catch (Exception e) {
092                throw new IllegalStateException(e);
093            }
094        }
095    
096        public String getId() {
097            return getSession().getId().toString();
098        }
099    
100        public long getLastAccessedTime() {
101            return getSession().getLastAccessTime().getTime();
102        }
103    
104        public ServletContext getServletContext() {
105            return this.servletContext;
106        }
107    
108        public void setMaxInactiveInterval(int i) {
109            try {
110                getSession().setTimeout(i * 1000);
111            } catch (InvalidSessionException e) {
112                throw new IllegalStateException(e);
113            }
114        }
115    
116        public int getMaxInactiveInterval() {
117            try {
118                return (new Long(getSession().getTimeout() / 1000)).intValue();
119            } catch (InvalidSessionException e) {
120                throw new IllegalStateException(e);
121            }
122        }
123    
124        @SuppressWarnings({"deprecation"})
125        public javax.servlet.http.HttpSessionContext getSessionContext() {
126            return HTTP_SESSION_CONTEXT;
127        }
128    
129        public Object getAttribute(String s) {
130            try {
131                return getSession().getAttribute(s);
132            } catch (InvalidSessionException e) {
133                throw new IllegalStateException(e);
134            }
135        }
136    
137        public Object getValue(String s) {
138            return getAttribute(s);
139        }
140    
141        @SuppressWarnings({"unchecked"})
142        protected Set<String> getKeyNames() {
143            Collection<Object> keySet;
144            try {
145                keySet = getSession().getAttributeKeys();
146            } catch (InvalidSessionException e) {
147                throw new IllegalStateException(e);
148            }
149            Set<String> keyNames;
150            if (keySet != null && !keySet.isEmpty()) {
151                keyNames = new HashSet<String>(keySet.size());
152                for (Object o : keySet) {
153                    keyNames.add(o.toString());
154                }
155            } else {
156                keyNames = Collections.EMPTY_SET;
157            }
158            return keyNames;
159        }
160    
161        public Enumeration getAttributeNames() {
162            Set<String> keyNames = getKeyNames();
163            final Iterator iterator = keyNames.iterator();
164            return new Enumeration() {
165                public boolean hasMoreElements() {
166                    return iterator.hasNext();
167                }
168    
169                public Object nextElement() {
170                    return iterator.next();
171                }
172            };
173        }
174    
175        public String[] getValueNames() {
176            Set<String> keyNames = getKeyNames();
177            String[] array = new String[keyNames.size()];
178            if (keyNames.size() > 0) {
179                array = keyNames.toArray(array);
180            }
181            return array;
182        }
183    
184        protected void afterBound(String s, Object o) {
185            if (o instanceof HttpSessionBindingListener) {
186                HttpSessionBindingListener listener = (HttpSessionBindingListener) o;
187                HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, s, o);
188                listener.valueBound(event);
189            }
190        }
191    
192        protected void afterUnbound(String s, Object o) {
193            if (o instanceof HttpSessionBindingListener) {
194                HttpSessionBindingListener listener = (HttpSessionBindingListener) o;
195                HttpSessionBindingEvent event = new HttpSessionBindingEvent(this, s, o);
196                listener.valueUnbound(event);
197            }
198        }
199    
200        public void setAttribute(String s, Object o) {
201            try {
202                getSession().setAttribute(s, o);
203                afterBound(s, o);
204            } catch (InvalidSessionException e) {
205                //noinspection finally
206                try {
207                    afterUnbound(s, o);
208                } finally {
209                    //noinspection ThrowFromFinallyBlock
210                    throw new IllegalStateException(e);
211                }
212            }
213        }
214    
215        public void putValue(String s, Object o) {
216            setAttribute(s, o);
217        }
218    
219        public void removeAttribute(String s) {
220            try {
221                Object attribute = getSession().removeAttribute(s);
222                afterUnbound(s, attribute);
223            } catch (InvalidSessionException e) {
224                throw new IllegalStateException(e);
225            }
226        }
227    
228        public void removeValue(String s) {
229            removeAttribute(s);
230        }
231    
232        public void invalidate() {
233            try {
234                getSession().stop();
235            } catch (InvalidSessionException e) {
236                throw new IllegalStateException(e);
237            }
238        }
239    
240        public boolean isNew() {
241            Boolean value = (Boolean) currentRequest.getAttribute(ShiroHttpServletRequest.REFERENCED_SESSION_IS_NEW);
242            return value != null && value.equals(Boolean.TRUE);
243        }
244    }