/*
* 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.engine;
import java.lang.ref.SoftReference;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.seasar.mayaa.ParameterAware;
import org.seasar.mayaa.engine.specification.Specification;
import org.seasar.mayaa.impl.util.ReferenceCache;
/**
* @author Taro Kato (Gluegent, Inc.)
*/
public class SpecificationCache {
static final Log LOG = LogFactory.getLog(SpecificationCache.class);
/** 終了処理後かどうか。終了処理後は追加処理をしない。 */
protected static volatile boolean _sweepThreadAlive = true;
protected int _surviveLimit;
//@GuardedBy(this)
protected Map _specifications = new HashMap();
protected ReferenceCache _gcChecker;
protected SoftReference _gabage;
public SpecificationCache(int surviveLimit) {
_surviveLimit = surviveLimit;
if (surviveLimit > 0 && !ParameterAware.IS_SECURE_WEB) {
_gcChecker = new ReferenceCache(Object.class,
ReferenceCache.SOFT, new GCReceiver());
postNewGabage();
}
}
protected static void relaseThread() {
_sweepThreadAlive = false;
}
protected void postNewGabage() {
Object gabage = new Object();
_gabage = new SoftReference(gabage);
_gcChecker.add(gabage);
}
public boolean contains(String systemID) {
synchronized(this) {
return _specifications.containsKey(systemID);
}
}
public Specification get(String systemID) {
if (systemID == null) {
throw new IllegalArgumentException();
}
ReferSpecification refer;
synchronized(this) {
refer = (ReferSpecification) _specifications.get(systemID);
}
if (refer == null) {
return null;
}
Specification result = refer.getSpecification();
if (refer.isDeprecated()) {
return null;
}
return result;
}
public void add(Specification specification) {
if (specification == null) {
throw new IllegalArgumentException();
}
synchronized(this) {
Specification old = get(specification.getSystemID());
if (old != null) {
if (old == specification) {
return;
}
}
ReferSpecification refer = new ReferSpecification(specification);
_specifications.put(specification.getSystemID(), refer);
}
}
public void release() {
synchronized(this) {
_specifications = null;
relaseThread();
}
}
// support class
private class ReferSpecification {
private Specification _specification;
private volatile int _survivingCount;
volatile boolean _deprecated;
public ReferSpecification(Specification specification) {
if (specification == null) {
throw new IllegalArgumentException();
}
_specification = specification;
}
public Specification getSpecification() {
// 参照されたのでリセット
_survivingCount = 0;
return _specification;
}
public boolean isDeprecated() {
if (_deprecated == false) {
if (_specification.isDeprecated()) {
_deprecated = true;
}
}
return _deprecated;
}
public boolean requestRelease() {
_survivingCount++;
if (_survivingCount > _surviveLimit) {
_survivingCount = 0;
_deprecated = true;
return true;
}
return false;
}
}
private class GCReceiver implements ReferenceCache.SweepListener {
private volatile int _receiveCount = 0;
protected GCReceiver() {
// do nothing.
}
public Object labeling(Object referent) {
return new Integer(++_receiveCount);
}
public void sweepFinish(ReferenceCache monitor, Object label) {
synchronized (SpecificationCache.this) {
if (_specifications == null ||_sweepThreadAlive == false) {
return;
}
if (LOG.isDebugEnabled()) {
LOG.debug("remove " + label +"th time."
+ " free:" + Runtime.getRuntime().freeMemory()
+ " / total:" + Runtime.getRuntime().totalMemory());
}
List releaseItems = null;
for (Iterator it = _specifications.values().iterator()
; it.hasNext(); ) {
ReferSpecification refer = (ReferSpecification) it.next();
if (refer.requestRelease()) {
if (releaseItems == null) {
releaseItems = new ArrayList();
}
releaseItems.add(refer);
}
}
if (releaseItems != null) {
for (Iterator it = releaseItems.iterator()
; it.hasNext(); ) {
ReferSpecification refer =
(ReferSpecification) it.next();
Specification spec = refer.getSpecification();
_specifications.remove(spec.getSystemID());
if (LOG.isDebugEnabled()) {
LOG.debug("remove " + label +"th time. "
+ spec.getSystemID() + " remove from cache");
}
}
}
postNewGabage(); /*gabage polling next*/
}
}
}
}