| /* |
| * 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<Session> { |
| |
| // 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<Session> |
| implements Comparable<SessionRef> { |
| |
| private static ReferenceQueue<Session> refQueue = |
| new ReferenceQueue<Session>(); |
| |
| private static Set<SessionRef> refList = |
| Collections.synchronizedSortedSet(new TreeSet<SessionRef>()); |
| |
| static ReferenceQueue<Session> 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; |
| } |
| } |
| } |