/*
* Copyright 2004-2012 the Seasar Foundation and the Others.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package org.seasar.mayaa.impl.util;
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NoSuchElementException;
/**
* リファレンスキャッシュクラス。
*
* @author Taro Kato (Gluegent, Inc.)
*/
public class ReferenceCache extends ArrayList {
private static final long serialVersionUID = -6808020476640514389L;
public static final int SOFT = 0;
public static final int WEAK = 1;
/** 全スレッド共通の活動可能フラグ */
static boolean _alive = true;
/**
* ReferenceCacheで活動している全スレッドを止める。
*/
public static void finishThreads() {
_alive = false;
}
/**
* GC対象としてマークされ解放されるオブジェクトを
* 通知する。オブジェクトは解放済みになるので、
* あらかじめ対象オブジェクトに名前を付けて、
* 解放時にはその名前で通知される。
*
* @author Taro Kato (Gluegent, Inc.)
*/
public static interface SweepListener {
/**
* オブジェクトにラベルを付ける。
* @param referent 解放監視対象オブジェクト
* @return オブジェクトを識別するためのラベル。
* referentと参照依存関係のあるオブジェクトを返してはならない。
*/
Object labeling(Object referent);
/**
* labering時のreferentが解放された際に呼び出される。
* laberingで返したlabelオブジェクトが渡される。
* @param monitor リファレンスキャッシュ
* @param label オブジェクトに対応付けていたラベル
*/
void sweepFinish(ReferenceCache monitor, Object label);
}
protected volatile boolean _liveSweepMonitor;
protected SweepListener _sweepBeginListener;
protected ReferenceQueue _queue;
private Class _elementType;
private int _referenceType;
private String _name;
protected Map _labelReferenceMap;
public ReferenceCache() {
this(Object.class, SOFT, null);
}
public ReferenceCache(Class elementType) {
this(elementType, SOFT, null);
}
public ReferenceCache(Class elementType, int referenceType) {
this(elementType, SOFT, null);
}
public ReferenceCache(Class elementType,
int referenceType, SweepListener listener) {
if (referenceType != SOFT && referenceType != WEAK) {
throw new IllegalArgumentException();
}
if (elementType == null) {
throw new IllegalArgumentException();
}
_elementType = elementType;
_referenceType = referenceType;
_sweepBeginListener = listener;
}
private void check(Object element) {
if (element == null
|| _elementType.isAssignableFrom(element.getClass()) == false) {
throw new IllegalArgumentException();
}
}
public void setName(String name) {
_name = name;
}
public String getName() {
return _name;
}
protected synchronized void sweepMonitorStart() {
if (_liveSweepMonitor) {
return;
}
ThreadGroup topThreadGroup;
for (topThreadGroup = Thread.currentThread().getThreadGroup()
; topThreadGroup.getParent() != null
; topThreadGroup = topThreadGroup.getParent()) {
/* no operation */
}
_labelReferenceMap = new HashMap();
_queue = new ReferenceQueue();
new Thread(topThreadGroup, "ReferenceCache Sweep Monitor") {
{
setPriority(Thread.MIN_PRIORITY);
setDaemon(true);
_liveSweepMonitor = true;
}
public void run() {
while(_liveSweepMonitor && _alive) {
try {
PhantomReference ref =
(PhantomReference) _queue.remove(1);
if (ref != null) {
Object label = _labelReferenceMap.get(ref);
_labelReferenceMap.remove(ref);
_sweepBeginListener.sweepFinish(
ReferenceCache.this, label);
}
} catch(InterruptedException e) {
// no operation
}
}
}
}.start();
}
protected Reference createReference(Object referent) {
if (_sweepBeginListener != null) {
sweepMonitorStart();
Object labelObject = _sweepBeginListener.labeling(referent);
if (labelObject == null || labelObject == referent) {
labelObject = referent.toString();
}
PhantomReference ref = new PhantomReference(referent, _queue);
_labelReferenceMap.put(ref, labelObject);
}
switch(_referenceType) {
case SOFT:
return new SoftReference(referent);
case WEAK:
return new WeakReference(referent);
}
throw new IllegalStateException();
}
public void add(int index, Object element) {
check(element);
super.add(index, createReference(element));
}
public boolean add(Object o) {
check(o);
return super.add(createReference(o));
}
public int indexOf(Object o) {
if (o != null) {
for (int i = 0; i < size(); i++) {
if (((Reference) get(i)).get().equals(o)) {
return i;
}
}
}
return -1;
}
public boolean remove(Object o) {
if (o != null) {
int index = indexOf(o);
if (index >= 0) {
remove(index);
return true;
}
}
return false;
}
public Iterator iterator() {
return new ReferenceCacheIterator(this);
}
protected void finalize() throws Throwable {
_liveSweepMonitor = false;
super.finalize();
}
// support class
/**
* 解放されてヌルになったアイテムをパックしながら有効な
* アイテムを返すイテレータ
* @author Taro Kato (Gluegent, Inc.)
*/
protected static class ReferenceCacheIterator implements Iterator {
private int _index;
private Object _next;
private List _list;
public ReferenceCacheIterator(List list) {
if (list == null) {
throw new IllegalArgumentException();
}
_list = list;
_index = list.size();
}
public boolean hasNext() {
if (_next != null) {
return true;
}
while (_next == null) {
_index--;
if (_index < 0) {
return false;
}
synchronized (_list) {
Reference ref = (Reference) _list.get(_index);
_next = ref.get();
if (_next == null) {
_list.remove(_index);
}
}
}
return true;
}
public Object next() {
if (_next == null && hasNext() == false) {
throw new NoSuchElementException();
}
Object ret = _next;
_next = null;
return ret;
}
public void remove() {
throw new UnsupportedOperationException();
}
}
}