1 /* 2 * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package sun.security.pkcs11; 27 28 import java.lang.ref.*; 29 import java.util.*; 30 import java.util.concurrent.atomic.AtomicInteger; 31 32 import java.security.*; 33 34 import sun.security.pkcs11.wrapper.*; 35 36 /** 37 * A session object. Sessions are obtained via the SessionManager, 38 * see there for details. Most code will only ever need one method in 39 * this class, the id() method to obtain the session id. 40 * 41 * @author Andreas Sterbenz 42 * @since 1.5 43 */ 44 final class Session implements Comparable<Session> { 45 46 // time after which to close idle sessions, in milliseconds (3 minutes) 47 private final static long MAX_IDLE_TIME = 3 * 60 * 1000; 48 49 // token instance 50 final Token token; 51 52 // session id 53 private final long id; 54 55 // number of objects created within this session 56 private final AtomicInteger createdObjects; 57 58 // time this session was last used 59 // not synchronized/volatile for performance, so may be unreliable 60 // this could lead to idle sessions being closed early, but that is harmless 61 private long lastAccess; 62 63 private final SessionRef sessionRef; 64 65 Session(Token token, long id) { 66 this.token = token; 67 this.id = id; 68 createdObjects = new AtomicInteger(); 69 id(); 70 sessionRef = new SessionRef(this, id, token); 71 } 72 73 public int compareTo(Session other) { 74 if (this.lastAccess == other.lastAccess) { 75 return 0; 76 } else { 77 return (this.lastAccess < other.lastAccess) ? -1 : 1; 78 } 79 } 80 81 boolean isLive(long currentTime) { 82 return currentTime - lastAccess < MAX_IDLE_TIME; 83 } 84 85 long idInternal() { 86 return id; 87 } 88 89 long id() { 90 if (token.isPresent(this.id) == false) { 91 throw new ProviderException("Token has been removed"); 92 } 93 lastAccess = System.currentTimeMillis(); 94 return id; 95 } 96 97 void addObject() { 98 int n = createdObjects.incrementAndGet(); 99 // XXX update statistics in session manager if n == 1 100 } 101 102 void removeObject() { 103 int n = createdObjects.decrementAndGet(); 104 if (n == 0) { 105 token.sessionManager.demoteObjSession(this); 106 } else if (n < 0) { 107 throw new ProviderException("Internal error: objects created " + n); 108 } 109 } 110 111 boolean hasObjects() { 112 return createdObjects.get() != 0; 113 } 114 115 void close() { 116 if (hasObjects()) { 117 throw new ProviderException( 118 "Internal error: close session with active objects"); 119 } 120 sessionRef.dispose(); 121 } 122 } 123 124 /* 125 * NOTE: Use PhantomReference here and not WeakReference 126 * otherwise the sessions maybe closed before other objects 127 * which are still being finalized. 128 */ 129 final class SessionRef extends PhantomReference<Session> 130 implements Comparable<SessionRef> { 131 132 private static ReferenceQueue<Session> refQueue = 133 new ReferenceQueue<Session>(); 134 135 private static Set<SessionRef> refList = 136 Collections.synchronizedSortedSet(new TreeSet<SessionRef>()); 137 138 static ReferenceQueue<Session> referenceQueue() { 139 return refQueue; 140 } 141 142 static int totalCount() { 143 return refList.size(); 144 } 145 146 private static void drainRefQueueBounded() { 147 while (true) { 148 SessionRef next = (SessionRef) refQueue.poll(); 149 if (next == null) break; 150 next.dispose(); 151 } 152 } 153 154 // handle to the native session 155 private long id; 156 private Token token; 157 158 SessionRef(Session session, long id, Token token) { 159 super(session, refQueue); 160 this.id = id; 161 this.token = token; 162 refList.add(this); 163 // TBD: run at some interval and not every time? 164 drainRefQueueBounded(); 165 } 166 167 void dispose() { 168 refList.remove(this); 169 try { 170 if (token.isPresent(id)) { 171 token.p11.C_CloseSession(id); 172 } 173 } catch (PKCS11Exception e1) { 174 // ignore 175 } catch (ProviderException e2) { 176 // ignore 177 } finally { 178 this.clear(); 179 } 180 } 181 182 public int compareTo(SessionRef other) { 183 if (this.id == other.id) { 184 return 0; 185 } else { 186 return (this.id < other.id) ? -1 : 1; 187 } 188 } 189 }