--- /dev/null 2017-01-18 09:30:05.425422781 -0800 +++ new/src/jdk.crypto.cryptoki/share/classes/sun/security/pkcs11/Session.java 2017-01-18 23:07:19.139885470 -0800 @@ -0,0 +1,189 @@ +/* + * Copyright (c) 2003, 2011, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package sun.security.pkcs11; + +import java.lang.ref.*; +import java.util.*; +import java.util.concurrent.atomic.AtomicInteger; + +import java.security.*; + +import sun.security.pkcs11.wrapper.*; + +/** + * A session object. Sessions are obtained via the SessionManager, + * see there for details. Most code will only ever need one method in + * this class, the id() method to obtain the session id. + * + * @author Andreas Sterbenz + * @since 1.5 + */ +final class Session implements Comparable { + + // time after which to close idle sessions, in milliseconds (3 minutes) + private final static long MAX_IDLE_TIME = 3 * 60 * 1000; + + // token instance + final Token token; + + // session id + private final long id; + + // number of objects created within this session + private final AtomicInteger createdObjects; + + // time this session was last used + // not synchronized/volatile for performance, so may be unreliable + // this could lead to idle sessions being closed early, but that is harmless + private long lastAccess; + + private final SessionRef sessionRef; + + Session(Token token, long id) { + this.token = token; + this.id = id; + createdObjects = new AtomicInteger(); + id(); + sessionRef = new SessionRef(this, id, token); + } + + public int compareTo(Session other) { + if (this.lastAccess == other.lastAccess) { + return 0; + } else { + return (this.lastAccess < other.lastAccess) ? -1 : 1; + } + } + + boolean isLive(long currentTime) { + return currentTime - lastAccess < MAX_IDLE_TIME; + } + + long idInternal() { + return id; + } + + long id() { + if (token.isPresent(this.id) == false) { + throw new ProviderException("Token has been removed"); + } + lastAccess = System.currentTimeMillis(); + return id; + } + + void addObject() { + int n = createdObjects.incrementAndGet(); + // XXX update statistics in session manager if n == 1 + } + + void removeObject() { + int n = createdObjects.decrementAndGet(); + if (n == 0) { + token.sessionManager.demoteObjSession(this); + } else if (n < 0) { + throw new ProviderException("Internal error: objects created " + n); + } + } + + boolean hasObjects() { + return createdObjects.get() != 0; + } + + void close() { + if (hasObjects()) { + throw new ProviderException( + "Internal error: close session with active objects"); + } + sessionRef.dispose(); + } +} + +/* + * NOTE: Use PhantomReference here and not WeakReference + * otherwise the sessions maybe closed before other objects + * which are still being finalized. + */ +final class SessionRef extends PhantomReference + implements Comparable { + + private static ReferenceQueue refQueue = + new ReferenceQueue(); + + private static Set refList = + Collections.synchronizedSortedSet(new TreeSet()); + + static ReferenceQueue referenceQueue() { + return refQueue; + } + + static int totalCount() { + return refList.size(); + } + + private static void drainRefQueueBounded() { + while (true) { + SessionRef next = (SessionRef) refQueue.poll(); + if (next == null) break; + next.dispose(); + } + } + + // handle to the native session + private long id; + private Token token; + + SessionRef(Session session, long id, Token token) { + super(session, refQueue); + this.id = id; + this.token = token; + refList.add(this); + // TBD: run at some interval and not every time? + drainRefQueueBounded(); + } + + void dispose() { + refList.remove(this); + try { + if (token.isPresent(id)) { + token.p11.C_CloseSession(id); + } + } catch (PKCS11Exception e1) { + // ignore + } catch (ProviderException e2) { + // ignore + } finally { + this.clear(); + } + } + + public int compareTo(SessionRef other) { + if (this.id == other.id) { + return 0; + } else { + return (this.id < other.id) ? -1 : 1; + } + } +}