001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2010–2011 ApexIdentity Inc. 015 * Portions Copyright 2011-2014 ForgeRock AS. 016 */ 017 018package org.forgerock.openig.servlet; 019 020import java.io.IOException; 021import java.util.AbstractMap; 022import java.util.AbstractSet; 023import java.util.ArrayList; 024import java.util.Enumeration; 025import java.util.Iterator; 026import java.util.List; 027import java.util.NoSuchElementException; 028import java.util.Set; 029 030import javax.servlet.http.HttpServletRequest; 031import javax.servlet.http.HttpSession; 032 033import org.forgerock.openig.http.Session; 034 035/** 036 * Exposes the session managed by the servlet container as an exchange session. 037 * This implementation will get a servlet session if already allocated, 038 * otherwise will not create one until an attempt is made to put an attribute in 039 * it. 040 */ 041public class ServletSession extends AbstractMap<String, Object> implements Session { 042 043 /** The servlet request from which to get a servlet session object. */ 044 private final HttpServletRequest request; 045 046 /** The servlet session object, if available. */ 047 private volatile HttpSession httpSession; 048 049 /** The Map entrySet view of the session attributes. */ 050 private final Set<Entry<String, Object>> attributes = new AbstractSet<Entry<String, Object>>() { 051 @Override 052 public void clear() { 053 ServletSession.this.clear(); 054 } 055 056 @Override 057 public boolean contains(final Object o) { 058 return (o instanceof Entry) 059 && ServletSession.this.containsKey(((Entry<?, ?>) o).getKey()); 060 } 061 062 @Override 063 public boolean isEmpty() { 064 return ServletSession.this.isEmpty(); 065 } 066 067 @Override 068 public Iterator<Entry<String, Object>> iterator() { 069 return new Iterator<Entry<String, Object>>() { 070 @SuppressWarnings("unchecked") 071 final Enumeration<String> names = httpSession != null ? httpSession 072 .getAttributeNames() : null; 073 074 @Override 075 public boolean hasNext() { 076 return names != null && names.hasMoreElements(); 077 } 078 079 @Override 080 public Entry<String, Object> next() { 081 if (names == null) { 082 throw new NoSuchElementException(); 083 } 084 final String name = names.nextElement(); 085 return new SimpleEntry<String, Object>(name, httpSession.getAttribute(name)) { 086 private static final long serialVersionUID = -2957899005221454275L; 087 088 @Override 089 public Object setValue(final Object value) { 090 put(getKey(), value); 091 return super.setValue(value); 092 } 093 }; 094 } 095 096 @Override 097 public void remove() { 098 // Enumerations do not support concurrent removals. 099 throw new UnsupportedOperationException(); 100 } 101 }; 102 } 103 104 @Override 105 public boolean remove(final Object o) { 106 return (o instanceof Entry) 107 && ServletSession.this.remove(((Entry<?, ?>) o).getKey()) != null; 108 } 109 110 @Override 111 public int size() { 112 return ServletSession.this.size(); 113 } 114 }; 115 116 /** 117 * Creates a new session object which manages sessions through the provided 118 * servlet request object. 119 * 120 * @param request the servlet request object through which servlet sessions are 121 * managed. 122 */ 123 public ServletSession(final HttpServletRequest request) { 124 this.request = request; 125 // get session if already allocated 126 this.httpSession = request.getSession(false); 127 } 128 129 @Override 130 public void clear() { 131 if (httpSession != null) { 132 // Do in 2 steps to avoid CME. 133 @SuppressWarnings("unchecked") 134 final Enumeration<String> attributes = httpSession.getAttributeNames(); 135 final List<String> names = new ArrayList<String>(); 136 while (attributes.hasMoreElements()) { 137 names.add(attributes.nextElement()); 138 } 139 for (final String name : names) { 140 httpSession.removeAttribute(name); 141 } 142 } 143 } 144 145 @Override 146 public boolean containsKey(final Object key) { 147 return get(key) != null; 148 } 149 150 @Override 151 public Set<Entry<String, Object>> entrySet() { 152 return attributes; 153 } 154 155 @Override 156 public Object get(final Object key) { 157 Object value = null; 158 if (key instanceof String && httpSession != null) { 159 value = httpSession.getAttribute((String) key); 160 } 161 return value; 162 } 163 164 @Override 165 public boolean isEmpty() { 166 return httpSession == null || !httpSession.getAttributeNames().hasMoreElements(); 167 } 168 169 @Override 170 public synchronized Object put(final String key, final Object value) { 171 final Object old = get(key); 172 if (httpSession == null) { 173 // create session just-in-time 174 httpSession = request.getSession(true); 175 } 176 httpSession.setAttribute(key, value); 177 return old; 178 } 179 180 @Override 181 public Object remove(final Object key) { 182 final Object old = get(key); 183 if (key instanceof String && httpSession != null) { 184 httpSession.removeAttribute((String) key); 185 } 186 return old; 187 } 188 189 @Override 190 public int size() { 191 int size = 0; 192 if (httpSession != null) { 193 final Enumeration<?> attributes = httpSession.getAttributeNames(); 194 while (attributes.hasMoreElements()) { 195 attributes.nextElement(); 196 size++; 197 } 198 } 199 return size; 200 } 201 202 @Override 203 public void close() throws IOException { 204 // Nothing to do when using HttpSession 205 } 206}