001/* 002// This software is subject to the terms of the Eclipse Public License v1.0 003// Agreement, available at the following URL: 004// http://www.eclipse.org/legal/epl-v10.html. 005// You must accept the terms of that agreement to use this software. 006// 007// Copyright (C) 2012-2012 Pentaho and others 008// All Rights Reserved. 009*/ 010package mondrian.util; 011 012import mondrian.olap.Util; 013 014import java.lang.ref.*; 015import java.util.*; 016import java.util.concurrent.TimeUnit; 017 018/** 019 * An expiring reference is a subclass of {@link SoftReference} 020 * which pins the reference in memory until a certain timeout 021 * is reached. After that, the reference is free to be garbage 022 * collected if needed. 023 * 024 * <p>The timeout value must be provided as a String representing 025 * both the time value and the time unit. For example, 1 second is 026 * represented as "1s". Valid time units are [d, h, m, s, ms], 027 * representing respectively days, hours, minutes, seconds and 028 * milliseconds. 029 */ 030public class ExpiringReference<T> extends SoftReference<T> { 031 T hardRef; 032 long expiry = Long.MIN_VALUE; 033 034 /** 035 * A Timer object to execute what we need to do. 036 */ 037 private static final Timer timer = 038 new Timer( 039 "mondrian.util.ExpiringReference$timer", 040 true); 041 042 /** 043 * Creates an expiring reference. 044 * @param ref The referent. 045 * @param timeout The timeout to enforce, in minutes. 046 * If timeout is equal or less than 0, this means a hard reference. 047 */ 048 public ExpiringReference(T ref, String timeout) { 049 super(ref); 050 setTimer(ref, timeout); 051 } 052 053 private synchronized void setTimer(T referent, String timeoutString) { 054 Pair<Long, TimeUnit> pair = 055 Util.parseInterval(timeoutString, null); 056 final long timeout = pair.right.toMillis(pair.left); 057 058 if (timeout == Long.MIN_VALUE 059 && expiry != Long.MIN_VALUE) 060 { 061 // Reference was accessed through get(). 062 // Don't reset the expiry if it is active. 063 return; 064 } 065 066 if (timeout == 0) { 067 // Permanent ref mode. 068 expiry = Long.MAX_VALUE; 069 // Set the reference 070 this.hardRef = referent; 071 return; 072 } 073 074 if (timeout > 0) { 075 // A timeout must be enforced. 076 long newExpiry = 077 System.currentTimeMillis() + timeout; 078 079 if (newExpiry > expiry) { 080 expiry = newExpiry; 081 } 082 083 // Set the reference 084 this.hardRef = referent; 085 086 // Schedule for cleanup. 087 timer.schedule( 088 getTask(), 089 timeout + 10); 090 091 // Notice the 1001 value above. This to make sure that when 092 // the timer fires, the cleanup takes place. Else, undefined 093 // behavior happens. 094 return; 095 } 096 097 // Timeout is < 0. Act as a regular soft ref. 098 this.hardRef = null; 099 } 100 101 TimerTask getTask() { 102 return new TimerTask() { 103 public void run() { 104 if (expiry <= System.currentTimeMillis()) { 105 hardRef = null; 106 } 107 } 108 }; 109 } 110 111 public synchronized T get() { 112 return get(Long.MIN_VALUE + "ms"); 113 } 114 115 public synchronized T get(String timeout) { 116 final T weakRef = super.get(); 117 118 if (weakRef != null) { 119 // This object is still alive but was cleaned. 120 // set a new TimerTask. 121 setTimer(weakRef, timeout); 122 } 123 124 return weakRef; 125 } 126} 127// End ExpiringReference.java