1// 2// ======================================================================== 3// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. 4// ------------------------------------------------------------------------ 5// All rights reserved. This program and the accompanying materials 6// are made available under the terms of the Eclipse Public License v1.0 7// and Apache License v2.0 which accompanies this distribution. 8// 9// The Eclipse Public License is available at 10// http://www.eclipse.org/legal/epl-v10.html 11// 12// The Apache License v2.0 is available at 13// http://www.opensource.org/licenses/apache2.0.php 14// 15// You may elect to redistribute this code under either of these licenses. 16// ======================================================================== 17// 18 19package org.eclipse.jetty.server.session; 20 21import java.io.DataOutputStream; 22import java.io.File; 23import java.io.FileInputStream; 24import java.io.FileNotFoundException; 25import java.io.FileOutputStream; 26import java.io.IOException; 27import java.io.ObjectOutputStream; 28import java.io.OutputStream; 29import java.util.Enumeration; 30 31import javax.servlet.http.HttpServletRequest; 32 33import org.eclipse.jetty.util.IO; 34import org.eclipse.jetty.util.log.Log; 35import org.eclipse.jetty.util.log.Logger; 36 37public class HashedSession extends AbstractSession 38{ 39 private static final Logger LOG = Log.getLogger(HashedSession.class); 40 41 private final HashSessionManager _hashSessionManager; 42 43 /** Whether the session has been saved because it has been deemed idle; 44 * in which case its attribute map will have been saved and cleared. */ 45 private transient boolean _idled = false; 46 47 /** Whether there has already been an attempt to save this session 48 * which has failed. If there has, there will be no more save attempts 49 * for this session. This is to stop the logs being flooded with errors 50 * due to serialization failures that are most likely caused by user 51 * data stored in the session that is not serializable. */ 52 private transient boolean _saveFailed = false; 53 54 /* ------------------------------------------------------------- */ 55 protected HashedSession(HashSessionManager hashSessionManager, HttpServletRequest request) 56 { 57 super(hashSessionManager,request); 58 _hashSessionManager = hashSessionManager; 59 } 60 61 /* ------------------------------------------------------------- */ 62 protected HashedSession(HashSessionManager hashSessionManager, long created, long accessed, String clusterId) 63 { 64 super(hashSessionManager,created, accessed, clusterId); 65 _hashSessionManager = hashSessionManager; 66 } 67 68 /* ------------------------------------------------------------- */ 69 protected void checkValid() 70 { 71 if (_hashSessionManager._idleSavePeriodMs!=0) 72 deIdle(); 73 super.checkValid(); 74 } 75 76 /* ------------------------------------------------------------- */ 77 @Override 78 public void setMaxInactiveInterval(int secs) 79 { 80 super.setMaxInactiveInterval(secs); 81 if (getMaxInactiveInterval()>0&&(getMaxInactiveInterval()*1000L/10)<_hashSessionManager._scavengePeriodMs) 82 _hashSessionManager.setScavengePeriod((secs+9)/10); 83 } 84 85 /* ------------------------------------------------------------ */ 86 @Override 87 protected void doInvalidate() 88 throws IllegalStateException 89 { 90 super.doInvalidate(); 91 92 // Remove from the disk 93 if (_hashSessionManager._storeDir!=null && getId()!=null) 94 { 95 String id=getId(); 96 File f = new File(_hashSessionManager._storeDir, id); 97 f.delete(); 98 } 99 } 100 101 /* ------------------------------------------------------------ */ 102 synchronized void save(boolean reactivate) 103 throws Exception 104 { 105 // Only idle the session if not already idled and no previous save/idle has failed 106 if (!isIdled() && !_saveFailed) 107 { 108 if (LOG.isDebugEnabled()) 109 LOG.debug("Saving {} {}",super.getId(),reactivate); 110 111 File file = null; 112 FileOutputStream fos = null; 113 114 try 115 { 116 file = new File(_hashSessionManager._storeDir, super.getId()); 117 118 if (file.exists()) 119 file.delete(); 120 file.createNewFile(); 121 fos = new FileOutputStream(file); 122 willPassivate(); 123 save(fos); 124 IO.close(fos); 125 if (reactivate) 126 didActivate(); 127 else 128 clearAttributes(); 129 } 130 catch (Exception e) 131 { 132 saveFailed(); // We won't try again for this session 133 if (fos != null) IO.close(fos); 134 if (file != null) file.delete(); // No point keeping the file if we didn't save the whole session 135 throw e; 136 } 137 } 138 } 139 /* ------------------------------------------------------------ */ 140 public synchronized void save(OutputStream os) throws IOException 141 { 142 DataOutputStream out = new DataOutputStream(os); 143 out.writeUTF(getClusterId()); 144 out.writeUTF(getNodeId()); 145 out.writeLong(getCreationTime()); 146 out.writeLong(getAccessed()); 147 148 /* Don't write these out, as they don't make sense to store because they 149 * either they cannot be true or their value will be restored in the 150 * Session constructor. 151 */ 152 //out.writeBoolean(_invalid); 153 //out.writeBoolean(_doInvalidate); 154 //out.writeLong(_maxIdleMs); 155 //out.writeBoolean( _newSession); 156 out.writeInt(getRequests()); 157 out.writeInt(getAttributes()); 158 ObjectOutputStream oos = new ObjectOutputStream(out); 159 Enumeration<String> e=getAttributeNames(); 160 while(e.hasMoreElements()) 161 { 162 String key=e.nextElement(); 163 oos.writeUTF(key); 164 oos.writeObject(doGet(key)); 165 } 166 oos.close(); 167 } 168 169 /* ------------------------------------------------------------ */ 170 public synchronized void deIdle() 171 { 172 if (isIdled()) 173 { 174 // Access now to prevent race with idling period 175 access(System.currentTimeMillis()); 176 177 if (LOG.isDebugEnabled()) 178 LOG.debug("De-idling " + super.getId()); 179 180 FileInputStream fis = null; 181 182 try 183 { 184 File file = new File(_hashSessionManager._storeDir, super.getId()); 185 if (!file.exists() || !file.canRead()) 186 throw new FileNotFoundException(file.getName()); 187 188 fis = new FileInputStream(file); 189 _idled = false; 190 _hashSessionManager.restoreSession(fis, this); 191 IO.close(fis); 192 193 didActivate(); 194 195 // If we are doing period saves, then there is no point deleting at this point 196 if (_hashSessionManager._savePeriodMs == 0) 197 file.delete(); 198 } 199 catch (Exception e) 200 { 201 LOG.warn("Problem de-idling session " + super.getId(), e); 202 if (fis != null) IO.close(fis);//Must ensure closed before invalidate 203 invalidate(); 204 } 205 } 206 } 207 208 209 /* ------------------------------------------------------------ */ 210 /** 211 * Idle the session to reduce session memory footprint. 212 * 213 * The session is idled by persisting it, then clearing the session values attribute map and finally setting 214 * it to an idled state. 215 */ 216 public synchronized void idle() 217 throws Exception 218 { 219 save(false); 220 _idled = true; 221 } 222 223 /* ------------------------------------------------------------ */ 224 public synchronized boolean isIdled() 225 { 226 return _idled; 227 } 228 229 /* ------------------------------------------------------------ */ 230 public synchronized boolean isSaveFailed() 231 { 232 return _saveFailed; 233 } 234 235 /* ------------------------------------------------------------ */ 236 public synchronized void saveFailed() 237 { 238 _saveFailed = true; 239 } 240 241} 242