diff --git a/doc/MANAGEMENT.md b/doc/MANAGEMENT.md index b8c56804..b106589d 100644 --- a/doc/MANAGEMENT.md +++ b/doc/MANAGEMENT.md @@ -8,8 +8,9 @@ MayaaではJMX(Java Management Extensions)を経由して内部状態の監視 | ObjectName | 説明 | |------------------------------------------------------------|-----| -| org.seasar.mayaa:type=CacheControl,name=SpecificationCache | Page および Template のビルド結果のキャッシュ | +| org.seasar.mayaa:type=CacheControl,name=specificationCache | Page および Template のビルド結果のキャッシュ | | org.seasar.mayaa:type=CacheControl,name=CompiledScript | スクリプトのコンパイル結果のキャッシュ | +| org.seasar.mayaa:type=CacheControl,name=SourceCompiledScript | ソーススクリプトコンパイル結果のキャッシュ | | org.seasar.mayaa:type=CacheControl,name=JspTagPool | JSPタグライブラリのインスタンスプール | | org.seasar.mayaa:type=CacheControl,name=PrefixAwareName | 名前空間付きの属性名のキャッシュ | | org.seasar.mayaa:type=MayaaEngine | Mayaa全体の挙動 | @@ -22,13 +23,22 @@ MayaaではJMX(Java Management Extensions)を経由して内部状態の監視 |-------------|----------------------------------------------|---------| | ClassName | キャッシュの実装クラス名。 | | | CurrentSize | 現在キャッシュとして保持されているオブジェクト数。 | | +| RequestCount | キャッシュ参照回数。 | | | HitCount | キャッシュヒットした回数。 | | +| HitRate | キャッシュヒット率。 | | | MissCount | キャッシュミスした回数。 | | +| MissRate | キャッシュミス率。 | | +| LoadSuccessCount | ロード成功回数。 | | +| LoadFailureCount | ロード失敗回数。 | | +| TotalLoadTime | ロード時間合計(ナノ秒)。 | | +| EvictionCount | 追い出し回数。 | | +| StatsEnabled | 統計取得が有効かどうか。 | | +| MaximumSizeManageable | 最大保持数の動的変更に対応しているか。 | | | RetainSize | キャッシュから追い出さずに保持する最大オブジェクト数。 | O | | 操作名 | 説明 | |-------------|----------------------------------------------| -| (なし) | | +| invalidateAll | 全エントリを破棄する。 | #### 属性・操作 (type=CacheControl) diff --git a/pom.xml b/pom.xml index 9f8ee4ef..6b841c1c 100644 --- a/pom.xml +++ b/pom.xml @@ -479,16 +479,16 @@ + + com.github.ben-manes.caffeine + caffeine + 2.9.3 + commons-beanutils commons-beanutils 1.11.0 - - commons-collections - commons-collections - 3.2.2 - commons-logging commons-logging diff --git a/src-api/org/seasar/mayaa/engine/Engine.java b/src-api/org/seasar/mayaa/engine/Engine.java index 1ddda907..c99aa185 100644 --- a/src-api/org/seasar/mayaa/engine/Engine.java +++ b/src-api/org/seasar/mayaa/engine/Engine.java @@ -106,4 +106,9 @@ public interface Engine extends ParameterAware { */ void destroy(); + /** + * Engineを初期化します。 + */ + void reset(); + } diff --git a/src-api/org/seasar/mayaa/management/CacheControlMXBean.java b/src-api/org/seasar/mayaa/management/CacheControlMXBean.java index abdff819..db02e0c9 100644 --- a/src-api/org/seasar/mayaa/management/CacheControlMXBean.java +++ b/src-api/org/seasar/mayaa/management/CacheControlMXBean.java @@ -32,7 +32,17 @@ public interface CacheControlMXBean { int getRetainSize(); void setRetainSize(int retainSize); long getCurrentSize(); + long getRequestCount(); long getHitCount(); + double getHitRate(); long getMissCount(); + double getMissRate(); + long getLoadSuccessCount(); + long getLoadFailureCount(); + long getTotalLoadTime(); + long getEvictionCount(); + boolean isStatsEnabled(); + boolean isMaximumSizeManageable(); + void invalidateAll(); String getClassName(); } \ No newline at end of file diff --git a/src-impl/org/seasar/mayaa/impl/MayaaServlet.java b/src-impl/org/seasar/mayaa/impl/MayaaServlet.java index 1e92a8d8..7bfa7c9a 100644 --- a/src-impl/org/seasar/mayaa/impl/MayaaServlet.java +++ b/src-impl/org/seasar/mayaa/impl/MayaaServlet.java @@ -29,7 +29,6 @@ import org.seasar.mayaa.impl.engine.processor.JspProcessor; import org.seasar.mayaa.impl.provider.ProviderUtil; import org.seasar.mayaa.impl.util.ObjectUtil; -import org.seasar.mayaa.impl.util.ReferenceCache; import org.seasar.mayaa.impl.util.StringUtil; /** @@ -99,7 +98,6 @@ protected void initAutoPageBuilder() { } public void destroy() { - ReferenceCache.finishThreads(); AutoPageBuilder.INSTANCE.destroy(); ProviderUtil.getEngine().destroy(); JspProcessor.clear(); diff --git a/src-impl/org/seasar/mayaa/impl/builder/TemplateBuilderImpl.java b/src-impl/org/seasar/mayaa/impl/builder/TemplateBuilderImpl.java index f01c383d..a52d5d5e 100644 --- a/src-impl/org/seasar/mayaa/impl/builder/TemplateBuilderImpl.java +++ b/src-impl/org/seasar/mayaa/impl/builder/TemplateBuilderImpl.java @@ -324,7 +324,6 @@ protected void walkTreeOptimizeNode( } else { if (processor instanceof ElementProcessor == false && processor instanceof AttributeProcessor == false) { - //Namespace root = NamespaceImpl.getInstance("/null\n"); processor.getOriginalNode().setParentSpace(null); processor.getInjectedNode().setParentSpace(null); //System.out.println(processor.getClass().getName()); diff --git a/src-impl/org/seasar/mayaa/impl/cycle/AbstractServiceCycle.java b/src-impl/org/seasar/mayaa/impl/cycle/AbstractServiceCycle.java index da0634dd..b7b19793 100644 --- a/src-impl/org/seasar/mayaa/impl/cycle/AbstractServiceCycle.java +++ b/src-impl/org/seasar/mayaa/impl/cycle/AbstractServiceCycle.java @@ -15,13 +15,9 @@ */ package org.seasar.mayaa.impl.cycle; -import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.Map; - -import org.apache.commons.collections.map.AbstractReferenceMap; -import org.apache.commons.collections.map.ReferenceMap; import org.seasar.mayaa.cycle.ServiceCycle; import org.seasar.mayaa.cycle.scope.AttributeScope; import org.seasar.mayaa.cycle.script.CompiledScript; @@ -47,9 +43,6 @@ public abstract class AbstractServiceCycle private static final long serialVersionUID = -4084527796306356704L; - @SuppressWarnings("unchecked") - private static final Map _scriptCache = - Collections.synchronizedMap(new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true)); private AttributeScope _page; private NodeTreeWalker _originalNode; private NodeTreeWalker _injectedNode; @@ -81,13 +74,8 @@ public void load(String systemID, String encoding) { } protected CompiledScript getScript(String systemID, String encoding) { - CompiledScript script = (CompiledScript) _scriptCache.get(systemID); - if (script != null) { - return script; - } + ApplicationSourceDescriptor appSource = new ApplicationSourceDescriptor(); - ApplicationSourceDescriptor appSource = - new ApplicationSourceDescriptor(); if (systemID.startsWith("/") == false) { appSource.setRoot(ApplicationSourceDescriptor.WEB_INF); } @@ -103,9 +91,7 @@ protected CompiledScript getScript(String systemID, String encoding) { } ScriptEnvironment env = ProviderUtil.getScriptEnvironment(); - script = env.compile(source, encoding); - _scriptCache.put(systemID, script); - return script; + return env.compile(source, encoding); } public Iterator iterateAttributeScope() { diff --git a/src-impl/org/seasar/mayaa/impl/cycle/script/rhino/ScriptEnvironmentImpl.java b/src-impl/org/seasar/mayaa/impl/cycle/script/rhino/ScriptEnvironmentImpl.java index ff61b889..0b2aaf76 100644 --- a/src-impl/org/seasar/mayaa/impl/cycle/script/rhino/ScriptEnvironmentImpl.java +++ b/src-impl/org/seasar/mayaa/impl/cycle/script/rhino/ScriptEnvironmentImpl.java @@ -16,7 +16,6 @@ package org.seasar.mayaa.impl.cycle.script.rhino; import java.util.Map; - import org.mozilla.javascript.Context; import org.mozilla.javascript.NativeArray; import org.mozilla.javascript.Scriptable; @@ -38,19 +37,27 @@ import org.seasar.mayaa.impl.management.CacheControllerRegistry; import org.seasar.mayaa.impl.util.ObjectUtil; import org.seasar.mayaa.impl.util.StringUtil; -import org.seasar.mayaa.impl.util.WeakValueHashMap; import org.seasar.mayaa.source.SourceDescriptor; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + /** * Rhino用のスクリプト環境。 * * @author Masataka Kurihara (Gluegent, Inc.) */ public class ScriptEnvironmentImpl extends AbstractScriptEnvironment { - private static final int DEFAULT_HARD_SIZE = 128; private static Scriptable _standardObjects; - private WeakValueHashMap scriptCache = new WeakValueHashMap<>(DEFAULT_HARD_SIZE); + private static final Cache _scriptCache = Caffeine.newBuilder() + .maximumSize(10_000) + .recordStats() + .build(); + private static final Cache _sourceScriptCache = Caffeine.newBuilder() + .maximumSize(1_000) + .recordStats() + .build(); private static final boolean CONSTRAINT_GLOBAL_PROPERTY_DEFINE = true; @@ -61,7 +68,10 @@ public class ScriptEnvironmentImpl extends AbstractScriptEnvironment { public ScriptEnvironmentImpl() { super(); - CacheControllerRegistry.registerCacheController("CompiledScript", scriptCache); + _scriptCache.invalidateAll(); + _sourceScriptCache.invalidateAll(); + CacheControllerRegistry.registerCacheController("CompiledScript", _scriptCache); + CacheControllerRegistry.registerCacheController("SourceCompiledScript", _sourceScriptCache); } protected CompiledScript compile( @@ -73,21 +83,16 @@ protected CompiledScript compile( if (scriptBlock.isLiteral()) { return new LiteralScript(text); } else { - CompiledScript script = (CompiledScript) scriptCache.get(text); - if (script == null) { - synchronized (scriptCache) { - script = (CompiledScript) scriptCache.get(text); - if (script == null) { - if (_useGetterScriptEmulation) { - script = GetterScriptFactory.create(text, position, offsetLine); - } - if (script == null) { - script = new TextCompiledScriptImpl(text, position, offsetLine); - } - scriptCache.put(text, script); - } - } - } + CompiledScript script = (CompiledScript) _scriptCache.get(text, key -> { + CompiledScript s = null; + if (_useGetterScriptEmulation) { + s = GetterScriptFactory.create(text, position, offsetLine); + } + if (s == null) { + s = new TextCompiledScriptImpl(text, position, offsetLine); + } + return s; + }); return script; } } @@ -104,12 +109,18 @@ protected String getSourceMimeType(SourceDescriptor source) { return application.getMimeType(systemID); } - public CompiledScript compile( - SourceDescriptor source, String encoding) { + public CompiledScript compile(SourceDescriptor source, String encoding) { if (source == null) { throw new IllegalArgumentException(); } - return new SourceCompiledScriptImpl(source, encoding); + if (source.exists() == false) { + return null; + } + String cacheKey = source.getSystemID() + "\n" + (encoding == null ? "" : encoding); + CompiledScript script = _sourceScriptCache.get(cacheKey, key -> { + return new SourceCompiledScriptImpl(source, encoding); + }); + return script; } protected static Scriptable getStandardObjects() { @@ -252,14 +263,6 @@ private boolean conversionRequires(Object scriptObject, Class expectedClass) return (scriptObject instanceof Scriptable); } - public void setScriptCacheSize(int cacheSize) { - scriptCache.setHardSize(cacheSize); - } - - public int getScriptCacheSize() { - return scriptCache.getHardSize(); - } - static void setWrapFactory(WrapFactory wrap) { _wrap = wrap; } @@ -287,11 +290,6 @@ public void setParameter(String name, String value) { throw new IllegalParameterValueException(getClass(), name); } _useGetterScriptEmulation = ObjectUtil.booleanValue(value, false); - } else if ("cacheSize".equals(name)) { - if (StringUtil.isEmpty(value)) { - throw new IllegalParameterValueException(getClass(), name); - } - setScriptCacheSize(Integer.parseInt(value)); } super.setParameter(name, value); } diff --git a/src-impl/org/seasar/mayaa/impl/engine/EngineImpl.java b/src-impl/org/seasar/mayaa/impl/engine/EngineImpl.java index de0d54e5..9bc4ca0d 100644 --- a/src-impl/org/seasar/mayaa/impl/engine/EngineImpl.java +++ b/src-impl/org/seasar/mayaa/impl/engine/EngineImpl.java @@ -27,6 +27,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicReference; import java.util.regex.Pattern; @@ -59,6 +60,9 @@ import org.seasar.mayaa.impl.util.StringUtil; import org.seasar.mayaa.source.SourceDescriptor; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + /** * @author Masataka Kurihara (Gluegent, Inc) */ @@ -79,7 +83,12 @@ public class EngineImpl extends NonSerializableParameterAwareImpl implements Eng private transient AtomicReference _defaultSpecification = new AtomicReference<>(null); private transient ErrorHandler _errorHandler; - private transient SpecificationCache _specCache; + private final Cache _specCache = Caffeine.newBuilder() + .expireAfterWrite(10, TimeUnit.MINUTES) + .maximumSize(10_000) + .recordStats() + .build(); + private transient SerializeExecutor _serializeExecutor; /** Engineが破棄されていればtrue。破棄後はサービスを保証しない。 */ private volatile boolean _destroyed = false; @@ -103,6 +112,7 @@ public class EngineImpl extends NonSerializableParameterAwareImpl implements Eng public EngineImpl() { EngineRegistory.registerEngine(this); + CacheControllerRegistry.registerCacheController("specificationCache", _specCache); CycleUtil.registVariableFactory(CONST_IMPL.DEFAULT_SPECIFICATION_KEY, new DefaultCycleLocalInstantiator(){ /** * デフォルトSpecificationをCycleに貼り付ける。 @@ -139,7 +149,7 @@ public ErrorHandler getErrorHandler() { } public Specification findSpecificationFromCache(String systemID) { - return getCache().get(systemID); + return _specCache.getIfPresent(systemID); } /** @@ -457,14 +467,6 @@ protected void dump(ServiceCycle cycle) { dump.printSource(page); } - protected SpecificationCache getCache() { - if (_specCache == null) { - _specCache = new SpecificationCache(_surviveLimit); - CacheControllerRegistry.registerCacheController("SpecificationCache", _specCache); - } - return _specCache; - } - private static interface SpecificationGenerator { Class getInstantiator(SourceDescriptor source); void initialize(Specification instance); @@ -480,7 +482,7 @@ protected Specification createSpecificationInstance(String systemID, if (spec != null) { if (spec.isDeprecated() == false) { if (registerCache) { - getCache().add(spec); + _specCache.put(spec.getSystemID(), spec); } return spec; } @@ -515,7 +517,7 @@ protected Specification createSpecificationInstance(String systemID, throw e; } if (registerCache) { - getCache().add(spec); + _specCache.put(spec.getSystemID(), spec); } return spec; } @@ -577,14 +579,15 @@ protected String getSuffixSeparator() { return EngineUtil.getEngineSetting(CONST_IMPL.SUFFIX_SEPARATOR, "$"); } - public synchronized void destroy() { + public void destroy() { _destroyed = true; - if (_specCache != null) { - _specCache.release(); - } disableSerialize(); } + public void reset() { + _specCache.invalidateAll(); + } + protected void finalize() throws Throwable { destroy(); } diff --git a/src-impl/org/seasar/mayaa/impl/engine/SpecificationCache.java b/src-impl/org/seasar/mayaa/impl/engine/SpecificationCache.java deleted file mode 100644 index 45837948..00000000 --- a/src-impl/org/seasar/mayaa/impl/engine/SpecificationCache.java +++ /dev/null @@ -1,237 +0,0 @@ -/* - * 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.List; -import java.util.Map; -import java.util.concurrent.atomic.AtomicLong; - -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -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; - protected AtomicLong hitCount = new AtomicLong(); - protected AtomicLong missCount = new AtomicLong(); - - public SpecificationCache(int surviveLimit) { - _surviveLimit = surviveLimit; - if (surviveLimit > 0 && !EngineUtil.isInSecureWeb()) { - _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 int size() { - return _specifications.size(); - } - - public long getHitCount() { - return hitCount.get(); - } - - public long getMissCount() { - return missCount.get(); - } - - private void countCacheHit(boolean hit) { - // 閾値を超えたらリセット(厳密さは求めていないためhit/missの同期は特に行わない) - final long THRESHOLD = Long.MAX_VALUE; - if (hit) { - if (hitCount.incrementAndGet() >= THRESHOLD) { - hitCount.set(0); - missCount.set(0); - } - } - else { - if (missCount.incrementAndGet() >= THRESHOLD) { - missCount.set(0); - hitCount.set(0); - } - } - } - - public Specification get(String systemID) { - Specification spec = _get(systemID); - countCacheHit(spec != null); - return spec; - } - - private 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 Integer.valueOf(++_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 (ReferSpecification refer : _specifications.values()) { - if (refer.requestRelease()) { - if (releaseItems == null) { - releaseItems = new ArrayList<>(); - } - releaseItems.add(refer); - } - } - if (releaseItems != null) { - for (ReferSpecification refer : releaseItems) { - 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*/ - } - } - } - -} - diff --git a/src-impl/org/seasar/mayaa/impl/engine/processor/ElementProcessor.java b/src-impl/org/seasar/mayaa/impl/engine/processor/ElementProcessor.java index b9e7b5cd..735786fe 100644 --- a/src-impl/org/seasar/mayaa/impl/engine/processor/ElementProcessor.java +++ b/src-impl/org/seasar/mayaa/impl/engine/processor/ElementProcessor.java @@ -69,14 +69,7 @@ public Object create(Object[] params) { new DefaultCycleLocalInstantiator() { public Object create(Object[] params) { SpecificationNode originalNode = (SpecificationNode) params[0]; - Namespace currentNS = SpecificationUtil.createNamespace(); - currentNS.setParentSpace(originalNode.getParentSpace()); - for (Iterator it = originalNode.iteratePrefixMapping(false); it.hasNext();) { - PrefixMapping prefixMapping = it.next(); - currentNS.addPrefixMapping(prefixMapping.getPrefix(), prefixMapping.getNamespaceURI()); - } - currentNS.setDefaultNamespaceURI(originalNode.getDefaultNamespaceURI()); - return currentNS; + return SpecificationUtil.copyNamespace(originalNode); }}); } diff --git a/src-impl/org/seasar/mayaa/impl/engine/processor/FormatNumberProcessor.java b/src-impl/org/seasar/mayaa/impl/engine/processor/FormatNumberProcessor.java index 09038a1e..aff0aa57 100644 --- a/src-impl/org/seasar/mayaa/impl/engine/processor/FormatNumberProcessor.java +++ b/src-impl/org/seasar/mayaa/impl/engine/processor/FormatNumberProcessor.java @@ -17,11 +17,6 @@ import java.text.DecimalFormat; import java.text.NumberFormat; -import java.util.Collections; -import java.util.Map; - -import org.apache.commons.collections.map.AbstractReferenceMap; -import org.apache.commons.collections.map.ReferenceMap; import org.seasar.mayaa.cycle.ServiceCycle; import org.seasar.mayaa.engine.Page; import org.seasar.mayaa.engine.processor.ProcessStatus; @@ -30,6 +25,9 @@ import org.seasar.mayaa.impl.util.ObjectUtil; import org.seasar.mayaa.impl.util.collection.AbstractSoftReferencePool; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + /** * 数値を指定フォーマットで文字列に変換して出力するプロセッサ。 * 内部的には{@link DecimalFormat}。 @@ -40,9 +38,9 @@ public class FormatNumberProcessor extends TemplateProcessorSupport { private static final long serialVersionUID = 5513075315121041838L; - @SuppressWarnings("unchecked") - private static Map _formatPools = - Collections.synchronizedMap(new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true)); + private static final Cache _formatPools = Caffeine.newBuilder() + .softValues() + .build(); private ProcessorProperty _value; private ProcessorProperty _default; @@ -100,14 +98,9 @@ private String format(ProcessorProperty property) { } protected NumberFormatPool getFormatPool() { - synchronized (_formatPools) { - NumberFormatPool pool = _formatPools.get(_pattern); - if (pool == null) { - pool = new NumberFormatPool(_pattern); - _formatPools.put(_pattern, pool); - } - return pool; - } + return _formatPools.get(_pattern, key -> { + return new NumberFormatPool(key); + }); } // support class diff --git a/src-impl/org/seasar/mayaa/impl/engine/processor/JspProcessor.java b/src-impl/org/seasar/mayaa/impl/engine/processor/JspProcessor.java index 52090496..8a2f8de0 100644 --- a/src-impl/org/seasar/mayaa/impl/engine/processor/JspProcessor.java +++ b/src-impl/org/seasar/mayaa/impl/engine/processor/JspProcessor.java @@ -39,8 +39,6 @@ import jakarta.servlet.jsp.tagext.TryCatchFinally; import jakarta.servlet.jsp.tagext.VariableInfo; -import org.apache.commons.collections.map.AbstractReferenceMap; -import org.apache.commons.collections.map.ReferenceMap; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.seasar.mayaa.cycle.CycleWriter; @@ -70,6 +68,9 @@ import org.seasar.mayaa.impl.util.StringUtil; import org.seasar.mayaa.impl.util.collection.AbstractSoftReferencePool; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + /** * @author Koji Suga (Gluegent, Inc.) * @author Masataka Kurihara (Gluegent, Inc.) @@ -85,9 +86,9 @@ public class JspProcessor extends TemplateProcessorSupport private static final PageContext _pageContext = new PageContextImpl(); - @SuppressWarnings("unchecked") - private static final Map _tagPools = - Collections.synchronizedMap(new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true)); + private static final Cache _tagPools = Caffeine.newBuilder() + .softValues() + .build(); static { CacheControllerRegistry.registerCacheController("JspTagPool", _tagPools); } @@ -120,7 +121,7 @@ public Object create(Object owner, Object[] params) { private boolean _forceBodySkip; public static void clear() { - _tagPools.clear(); + _tagPools.invalidateAll(); } public static boolean isSupportClass(Class test) { @@ -192,12 +193,9 @@ protected String getAttributesKey() { protected TagPool getTagPool() { synchronized (_tagPools) { String key = _tagClass.getName() + getAttributesKey(); - TagPool pool = (TagPool) _tagPools.get(key); - if (pool == null) { - pool = new TagPool(_tagClass); - _tagPools.put(key, pool); - } - return pool; + return _tagPools.get(key, k ->{ + return new TagPool(_tagClass); + }); } } diff --git a/src-impl/org/seasar/mayaa/impl/engine/specification/NamespaceImpl.java b/src-impl/org/seasar/mayaa/impl/engine/specification/NamespaceImpl.java index 6408eb48..2c66eeac 100644 --- a/src-impl/org/seasar/mayaa/impl/engine/specification/NamespaceImpl.java +++ b/src-impl/org/seasar/mayaa/impl/engine/specification/NamespaceImpl.java @@ -31,7 +31,6 @@ import org.seasar.mayaa.engine.specification.PrefixMapping; import org.seasar.mayaa.engine.specification.URI; import org.seasar.mayaa.impl.CONST_IMPL; -import org.seasar.mayaa.impl.util.ReferenceCache; import org.seasar.mayaa.impl.util.StringUtil; /** @@ -43,39 +42,11 @@ public class NamespaceImpl implements Namespace { private static final Log LOG = LogFactory.getLog(NamespaceImpl.class); - private static ReferenceCache _cache = - new ReferenceCache<>(Namespace.class); - - public static Namespace getInstance(Namespace namespace) { - return getInstance(serialize(namespace)); - } - - public static Namespace getInstance(String serializeKey) { - synchronized(_cache) { - // キャッシュされたシングルトンを返す - for (Iterator it = _cache.iterator(); it.hasNext(); ) { - NamespaceImpl space = (NamespaceImpl) it.next(); - if (space.getSerializeKey().equals(serializeKey)) { - return space; - } - } - Namespace namespace = deserialize(serializeKey); - if (_cache.contains(namespace) == false) { - _cache.add(namespace); - } - return namespace; - } - } - public static Namespace copyOf(Namespace namespace) { if (namespace == null) { return namespace; } - Namespace parent = namespace.getParentSpace(); - if (parent != null) { - String key = serialize(parent); - parent = getInstance(key); - } + Namespace parent = copyOf(namespace.getParentSpace()); NamespaceImpl result = new NamespaceImpl(); result.setParentSpace(parent); for (Iterator it = namespace.iteratePrefixMapping(false); it.hasNext(); ) { @@ -314,11 +285,6 @@ protected static NamespaceImpl deserialize(String serializeData) { if (line.startsWith("/")) { line = line.substring("/".length()); if (current != null) { - synchronized(_cache) { - if (_cache.contains(current) == false) { - _cache.add(current); - } - } parent = current; } current = new NamespaceImpl(); diff --git a/src-impl/org/seasar/mayaa/impl/engine/specification/PrefixAwareNameImpl.java b/src-impl/org/seasar/mayaa/impl/engine/specification/PrefixAwareNameImpl.java index 07554afa..21f33d14 100644 --- a/src-impl/org/seasar/mayaa/impl/engine/specification/PrefixAwareNameImpl.java +++ b/src-impl/org/seasar/mayaa/impl/engine/specification/PrefixAwareNameImpl.java @@ -15,39 +15,31 @@ */ package org.seasar.mayaa.impl.engine.specification; -import java.util.Collections; -import java.util.Map; - -import org.apache.commons.collections.map.AbstractReferenceMap; -import org.apache.commons.collections.map.ReferenceMap; import org.seasar.mayaa.engine.specification.PrefixAwareName; import org.seasar.mayaa.engine.specification.QName; import org.seasar.mayaa.impl.management.CacheControllerRegistry; import org.seasar.mayaa.impl.util.StringUtil; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + /** * @author Masataka Kurihara (Gluegent, Inc.) */ public class PrefixAwareNameImpl implements PrefixAwareName { - private static final long serialVersionUID = 7623241470182328291L; + private static final long serialVersionUID = -8898891078217203404L; - @SuppressWarnings("unchecked") - private static Map _cache = - Collections.synchronizedMap(new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true)); + private static final Cache _cache = Caffeine.newBuilder() + .softValues() + .build(); + static { CacheControllerRegistry.registerCacheController("PrefixAwareName", _cache); } public static PrefixAwareName getInstance(QName qName, String prefix) { String key = forPrefixAwareNameString(qName, prefix); - PrefixAwareName result; - synchronized (_cache) { - result = _cache.get(key); - if (result == null) { - result = new PrefixAwareNameImpl(qName, prefix); - _cache.put(key, result); - } - } + PrefixAwareName result = _cache.get(key, k -> new PrefixAwareNameImpl(qName, prefix) ); return result; } diff --git a/src-impl/org/seasar/mayaa/impl/engine/specification/PrefixMappingImpl.java b/src-impl/org/seasar/mayaa/impl/engine/specification/PrefixMappingImpl.java index d1137dcb..1eff09b4 100644 --- a/src-impl/org/seasar/mayaa/impl/engine/specification/PrefixMappingImpl.java +++ b/src-impl/org/seasar/mayaa/impl/engine/specification/PrefixMappingImpl.java @@ -16,23 +16,21 @@ package org.seasar.mayaa.impl.engine.specification; import java.io.Serializable; -import java.util.Collections; -import java.util.Map; - -import org.apache.commons.collections.map.AbstractReferenceMap; -import org.apache.commons.collections.map.ReferenceMap; import org.seasar.mayaa.engine.specification.PrefixMapping; import org.seasar.mayaa.engine.specification.URI; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + /** * @author Masataka Kurihara (Gluegent, Inc.) */ public class PrefixMappingImpl implements PrefixMapping, Serializable { private static final long serialVersionUID = -7627574345551562433L; - @SuppressWarnings("unchecked") - private static volatile Map _cache = - Collections.synchronizedMap(new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true)); + private static final Cache _cache = Caffeine.newBuilder() + .softValues() + .build(); public static PrefixMapping getInstance(String prefix, URI namespaceURI) { if (namespaceURI == null) { @@ -48,12 +46,9 @@ public static PrefixMapping getInstance(String prefix, URI namespaceURI) { String key = forPrefixMappingString(prefix, namespaceURI); - PrefixMapping result = _cache.get(key); - if (result == null) { - result = new PrefixMappingImpl(prefix, namespaceURI); - _cache.put(key, result); - } - return result; + return _cache.get(key, k -> { + return new PrefixMappingImpl(prefix, namespaceURI); + }); } @@ -126,9 +121,6 @@ public int hashCode() { return toString().hashCode(); } - public static int keptSize() { - return _cache.size(); - } private Object readResolve() { return getInstance(_prefix, _namespaceURI); } diff --git a/src-impl/org/seasar/mayaa/impl/engine/specification/QNameImpl.java b/src-impl/org/seasar/mayaa/impl/engine/specification/QNameImpl.java index 1d3dc4c3..1f5d88f5 100644 --- a/src-impl/org/seasar/mayaa/impl/engine/specification/QNameImpl.java +++ b/src-impl/org/seasar/mayaa/impl/engine/specification/QNameImpl.java @@ -16,52 +16,38 @@ package org.seasar.mayaa.impl.engine.specification; import java.io.Serializable; -import java.util.Collections; -import java.util.Map; import java.util.Objects; - -import org.apache.commons.collections.ReferenceMap; -import org.apache.commons.collections.map.AbstractReferenceMap; import org.seasar.mayaa.engine.specification.QName; import org.seasar.mayaa.engine.specification.URI; import org.seasar.mayaa.impl.CONST_IMPL; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + /** * @author Masataka Kurihara (Gluegent, Inc.) * @author Taro Kato (Gluegent, Inc.) */ -public class QNameImpl implements QName, CONST_IMPL, Serializable { +public final class QNameImpl implements QName, CONST_IMPL, Serializable { private static final long serialVersionUID = 2143966034062341815L; - @SuppressWarnings("unchecked") - private static volatile Map _cache = - Collections.synchronizedMap(new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.SOFT, true)); + private static final Cache _cache = Caffeine.newBuilder() + .softValues() + .build(); public static QName getInstance(String localName) { return getInstance(URI_MAYAA, localName); } public static QName getInstance(String namespaceURI, String localName) { - URI uri = URIImpl.getInstance(namespaceURI); - return getInstance(uri, localName); + String key = forQNameString(namespaceURI, localName); + QName result = _cache.get(key, k -> new QNameImpl(URIImpl.getInstance(namespaceURI), localName)); + return result; } public static QName getInstance(URI namespaceURI, String localName) { - // undeploy時に_cacheが消されたあとアクセスされる場合がある - if (_cache == null) { - return null; - } - - String key = forQNameString(namespaceURI, localName); - - // 一時的に重複しても問題ないので速度を優先する。(synchronizeを外した) - QName result = _cache.get(key); - if (result == null) { - result = new QNameImpl(namespaceURI, localName); - _cache.put(key, result); - } - return result; + return getInstance(namespaceURI.getValue(), localName); } private URI _namespaceURI; @@ -94,8 +80,7 @@ public String getLocalName() { * @param localName ローカル名 * @return "{URI}localName"形式の文字列 */ - private static String forQNameString(URI namespaceURI, String localName) { - String namespace = namespaceURI.getValue(); + private static String forQNameString(String namespace, String localName) { int namespaceLength = namespace.length(); int localNameLength = localName.length(); char[] buffer = new char[namespaceLength + localNameLength + 2]; @@ -107,7 +92,7 @@ private static String forQNameString(URI namespaceURI, String localName) { } public String toString() { - return forQNameString(getNamespaceURI(), getLocalName()); + return forQNameString(_namespaceURI.getValue(), _localName); } @Override diff --git a/src-impl/org/seasar/mayaa/impl/engine/specification/SpecificationUtil.java b/src-impl/org/seasar/mayaa/impl/engine/specification/SpecificationUtil.java index 0de00615..957f93cf 100644 --- a/src-impl/org/seasar/mayaa/impl/engine/specification/SpecificationUtil.java +++ b/src-impl/org/seasar/mayaa/impl/engine/specification/SpecificationUtil.java @@ -252,7 +252,6 @@ public static Namespace createNamespace() { public static Namespace getFixedNamespace(Namespace original) { return original; - //return NamespaceImpl.getInstance(original); } public static QName createQName(String localName) { diff --git a/src-impl/org/seasar/mayaa/impl/engine/specification/URIImpl.java b/src-impl/org/seasar/mayaa/impl/engine/specification/URIImpl.java index e5b9785d..ecdb6b43 100644 --- a/src-impl/org/seasar/mayaa/impl/engine/specification/URIImpl.java +++ b/src-impl/org/seasar/mayaa/impl/engine/specification/URIImpl.java @@ -15,66 +15,44 @@ */ package org.seasar.mayaa.impl.engine.specification; -import java.util.Iterator; - import org.seasar.mayaa.engine.specification.URI; -import org.seasar.mayaa.impl.util.ReferenceCache; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; /** * @author Taro Kato (Gluegent, Inc.) */ -public class URIImpl implements URI { +public final class URIImpl implements URI { private static final long serialVersionUID = 7985133276316017754L; - private static ReferenceCache _cache = new ReferenceCache<>(URIImpl.class); + private static final Cache _cache = Caffeine.newBuilder() + .softValues() + .build(); + public static URI NULL_NS_URI = new URIImpl(""); public static URI getInstance(String uri) { if (uri == null || uri.isEmpty()) { return NULL_NS_URI; } - // undeploy時に_cacheが消されたあとアクセスされる場合がある - if (_cache == null) { - return null; - } - for (Iterator it = _cache.iterator(); it.hasNext(); ) { - URIImpl namespaceURI = (URIImpl)it.next(); - if (namespaceURI.equals(uri)) { - return namespaceURI; - } - } - return new URIImpl(uri); + URIImpl result = _cache.get(uri, k -> new URIImpl(uri)); + return result; } private String _value; - private int _hashCode; private URIImpl() { // for serialize } private URIImpl(String uri) { - setValue(uri); + _value = uri; } public String getValue() { return _value; } - public void setValue(String uri) { - if (uri == null) { - throw new IllegalArgumentException(); - } - if (_value != null) { - _cache.remove(this); - } - _value = uri; - if (_cache.contains(this) == false) { - _cache.add(this); - } - _hashCode = (getClass().getName() + "|" + _value).hashCode(); - } - public String toString() { return _value; } @@ -93,9 +71,10 @@ public boolean equals(Object obj) { } public int hashCode() { - return _hashCode; + return _value.hashCode(); } + // serializable private Object readResolve() { return getInstance(_value); } diff --git a/src-impl/org/seasar/mayaa/impl/management/CacheControllerRegistry.java b/src-impl/org/seasar/mayaa/impl/management/CacheControllerRegistry.java index d6a0bf0b..be07e5a3 100644 --- a/src-impl/org/seasar/mayaa/impl/management/CacheControllerRegistry.java +++ b/src-impl/org/seasar/mayaa/impl/management/CacheControllerRegistry.java @@ -15,144 +15,113 @@ */ package org.seasar.mayaa.impl.management; -import java.util.Map; - import javax.management.MalformedObjectNameException; import javax.management.ObjectName; -import org.apache.commons.collections.map.ReferenceMap; -import org.seasar.mayaa.impl.engine.SpecificationCache; -import org.seasar.mayaa.impl.util.WeakValueHashMap; import org.seasar.mayaa.management.CacheControlMXBean; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Policy; + /** * * @since 1.2 * @author Watanabe, Mitsutaka */ public class CacheControllerRegistry { - - public static void registerCacheController(String controllerName, final Map map) { + public static void registerCacheController(String controllerName, final Cache cache) { CacheControlMXBean mbean = new CacheControlMXBean(){ + private Policy.Eviction getEvictionPolicy() { + return cache.policy().eviction().orElse(null); + } + @Override public String getClassName() { - return map.getClass().getName(); + return cache.getClass().getName(); } @Override public int getRetainSize() { - throw new UnsupportedOperationException("Managing retain size is not supported"); + Policy.Eviction eviction = getEvictionPolicy(); + if (eviction == null) { + return -1; + } + long maximum = eviction.getMaximum(); + return maximum > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) maximum; } @Override public void setRetainSize(int retainSize) { - throw new UnsupportedOperationException("Managing retain size is not supported"); + if (retainSize < 0) { + throw new IllegalArgumentException("retainSize must be >= 0"); + } + Policy.Eviction eviction = getEvictionPolicy(); + if (eviction == null) { + throw new UnsupportedOperationException("Managing retain size is not supported"); + } + eviction.setMaximum(retainSize); } @Override public long getCurrentSize() { - return map.size(); - } - @Override - public long getHitCount() { - throw new UnsupportedOperationException("Collecting cache hit count is not supported"); + return cache.estimatedSize(); } @Override - public long getMissCount() { - throw new UnsupportedOperationException("Collecting cache miss count is not supported"); + public long getRequestCount() { + return cache.stats().requestCount(); } - }; - JMXUtil.register(mbean, makeObjectName(controllerName)); - } - public static void registerCacheController(String controllerName, final SpecificationCache specificationCache) { - CacheControlMXBean mbean = new CacheControlMXBean(){ - @Override - public String getClassName() { - return specificationCache.getClass().getName(); - } - @Override - public int getRetainSize() { - throw new UnsupportedOperationException("Managing retain size is not supported"); - } - @Override - public void setRetainSize(int retainSize) { - throw new UnsupportedOperationException("Managing retain size is not supported"); - } @Override - public long getCurrentSize() { - return specificationCache.size(); + public long getHitCount() { + return cache.stats().hitCount(); } @Override - public long getHitCount() { - return specificationCache.getHitCount(); + public double getHitRate() { + return cache.stats().hitRate(); } @Override public long getMissCount() { - return specificationCache.getMissCount(); + return cache.stats().missCount(); } - }; - JMXUtil.register(mbean, makeObjectName(controllerName)); - } - public static void registerCacheController(String controllerName, final WeakValueHashMap weakValueHashMap) { - CacheControlMXBean mbean = new CacheControlMXBean(){ - @Override - public String getClassName() { - return weakValueHashMap.getClass().getName(); - } - @Override - public int getRetainSize() { - return weakValueHashMap.getHardSize(); - } @Override - public void setRetainSize(int retainSize) { - weakValueHashMap.setHardSize(retainSize); - } - @Override - public long getCurrentSize() { - return weakValueHashMap.size(); + public double getMissRate() { + return cache.stats().missRate(); } @Override - public long getHitCount() { - return weakValueHashMap.getHitCount(); + public long getLoadSuccessCount() { + return cache.stats().loadSuccessCount(); } @Override - public long getMissCount() { - return weakValueHashMap.getMissCount(); + public long getLoadFailureCount() { + return cache.stats().loadFailureCount(); } - }; - JMXUtil.register(mbean, makeObjectName(controllerName)); - } - public static void registerCacheController(String controllerName, final ReferenceMap referenceMap) { - CacheControlMXBean mbean = new CacheControlMXBean(){ @Override - public String getClassName() { - return referenceMap.getClass().getName(); - } - @Override - public int getRetainSize() { - throw new UnsupportedOperationException("Managing retain size is not supported"); + public long getTotalLoadTime() { + return cache.stats().totalLoadTime(); } + @Override - public void setRetainSize(int retainSize) { - throw new UnsupportedOperationException("Managing retain size is not supported"); + public long getEvictionCount() { + return cache.stats().evictionCount(); } + @Override - public long getCurrentSize() { - return referenceMap.size(); + public boolean isStatsEnabled() { + return true; } + @Override - public long getHitCount() { - throw new UnsupportedOperationException("Collecting cache hit count is not supported"); + public boolean isMaximumSizeManageable() { + return getEvictionPolicy() != null; } @Override - public long getMissCount() { - throw new UnsupportedOperationException("Collecting cache miss count is not supported"); + public void invalidateAll() { + cache.invalidateAll(); } }; JMXUtil.register(mbean, makeObjectName(controllerName)); diff --git a/src-impl/org/seasar/mayaa/impl/standalone/FileSearchRenderer.java b/src-impl/org/seasar/mayaa/impl/standalone/FileSearchRenderer.java index 1235de97..517401a6 100644 --- a/src-impl/org/seasar/mayaa/impl/standalone/FileSearchRenderer.java +++ b/src-impl/org/seasar/mayaa/impl/standalone/FileSearchRenderer.java @@ -42,7 +42,6 @@ import org.seasar.mayaa.impl.provider.ProviderUtil; import org.seasar.mayaa.impl.source.ApplicationSourceDescriptor; import org.seasar.mayaa.impl.source.SourceHolderFactory; -import org.seasar.mayaa.impl.util.ReferenceCache; import org.seasar.mayaa.impl.util.StringUtil; import org.seasar.mayaa.source.SourceHolder; @@ -222,7 +221,6 @@ public void start() { } public void destroy() { - ReferenceCache.finishThreads(); ProviderUtil.getEngine().destroy(); JspProcessor.clear(); diff --git a/src-impl/org/seasar/mayaa/impl/util/DateFormatPool.java b/src-impl/org/seasar/mayaa/impl/util/DateFormatPool.java index 107d8887..de65ecfe 100644 --- a/src-impl/org/seasar/mayaa/impl/util/DateFormatPool.java +++ b/src-impl/org/seasar/mayaa/impl/util/DateFormatPool.java @@ -17,24 +17,22 @@ import java.text.DateFormat; import java.text.SimpleDateFormat; -import java.util.Collections; import java.util.Locale; -import java.util.Map; import java.util.TimeZone; import java.util.Locale.Category; -import org.apache.commons.collections.map.AbstractReferenceMap; -import org.apache.commons.collections.map.ReferenceMap; import org.seasar.mayaa.impl.util.collection.AbstractSoftReferencePool; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + /** * @author Taro Kato (Gluegent, Inc.) */ public class DateFormatPool { - - @SuppressWarnings("unchecked") - private static Map _formatPools = - Collections.synchronizedMap(new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true)); + private static final Cache _formatPools = Caffeine.newBuilder() + .softValues() + .build(); private DateFormatPool() { throw new UnsupportedOperationException(); @@ -50,11 +48,9 @@ private static String makeKey(String formatPattern, Locale locale) { public static DateFormat borrowFormat(String formatPattern, Locale locale) { String key = makeKey(formatPattern, locale); - Pool pool = _formatPools.get(key); - if (pool == null) { - pool = new Pool(formatPattern, locale); - _formatPools.put(key, pool); - } + Pool pool = _formatPools.get(key, k -> { + return new Pool(formatPattern, locale); + }); return pool.borrowFormat(); } @@ -65,7 +61,7 @@ public static void returnFormat(DateFormat object) { SimpleLocaleDateFormat format = (SimpleLocaleDateFormat) object; String key = makeKey(format.toPattern(), format.getLocale()); - Pool pool = (Pool) _formatPools.get(key); + Pool pool = _formatPools.getIfPresent(key); if (pool != null) { pool.returnFormat(format); } diff --git a/src-impl/org/seasar/mayaa/impl/util/ReferenceCache.java b/src-impl/org/seasar/mayaa/impl/util/ReferenceCache.java deleted file mode 100644 index 84ddfe63..00000000 --- a/src-impl/org/seasar/mayaa/impl/util/ReferenceCache.java +++ /dev/null @@ -1,283 +0,0 @@ -/* - * 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 { - - 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); - } - - private ArrayList> _list = new ArrayList<>(); - protected volatile boolean _liveSweepMonitor; - protected SweepListener _sweepBeginListener; - protected ReferenceQueue _queue; - - private Class _elementType; - private int _referenceType; - private String _name; - protected Map, Object> _labelReferenceMap; - - 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 { - Reference ref = _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(T 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, T element) { - check(element); - _list.add(index, createReference(element)); - } - - public boolean add(T o) { - check(o); - return _list.add(createReference(o)); - } - - public int indexOf(T o) { - if (o != null) { - for (int i = 0; i < _list.size(); i++) { - if (((Reference) _list.get(i)).get().equals(o)) { - return i; - } - } - } - return -1; - } - - public boolean remove(T o) { - if (o != null) { - int index = indexOf(o); - if (index >= 0) { - _list.remove(index); - return true; - } - } - return false; - } - - public boolean contains(T o) { - if (o != null) { - int index = indexOf(o); - if (index >= 0) { - return true; - } - } - return false; - } - - public Iterator iterator() { - return new ReferenceCacheIterator(_list); - } - - protected void finalize() throws Throwable { - _liveSweepMonitor = false; - } - - // support class - - /** - * 解放されてヌルになったアイテムをパックしながら有効な アイテムを返すイテレータ - * - * @author Taro Kato (Gluegent, Inc.) - */ - protected static class ReferenceCacheIterator implements Iterator { - - private int _index; - private T _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 = _list.get(_index); - _next = ref.get(); - if (_next == null) { - _list.remove(_index); - } - } - } - return true; - } - - public T next() { - if (_next == null && hasNext() == false) { - throw new NoSuchElementException(); - } - T ret = _next; - _next = null; - return ret; - } - - public void remove() { - throw new UnsupportedOperationException(); - } - - } - -} diff --git a/src-impl/org/seasar/mayaa/impl/util/StringUtil.java b/src-impl/org/seasar/mayaa/impl/util/StringUtil.java index 1ca249aa..e2ee3c4d 100644 --- a/src-impl/org/seasar/mayaa/impl/util/StringUtil.java +++ b/src-impl/org/seasar/mayaa/impl/util/StringUtil.java @@ -19,24 +19,23 @@ import java.io.IOException; import java.io.InputStream; import java.text.MessageFormat; -import java.util.Collections; -import java.util.Map; import java.util.Properties; -import org.apache.commons.collections.map.AbstractReferenceMap; -import org.apache.commons.collections.map.ReferenceMap; import org.seasar.mayaa.impl.cycle.script.ScriptUtil; import org.seasar.mayaa.impl.knowledge.HTMLKnowledge; import org.seasar.mayaa.impl.source.ClassLoaderSourceDescriptor; +import com.github.benmanes.caffeine.cache.Cache; +import com.github.benmanes.caffeine.cache.Caffeine; + /** * @author Masataka Kurihara (Gluegent, Inc.) */ public final class StringUtil { - @SuppressWarnings("unchecked") - private static Map _propFiles = - Collections.synchronizedMap(new ReferenceMap(AbstractReferenceMap.SOFT, AbstractReferenceMap.SOFT, true)); + private static final Cache _propFiles = Caffeine.newBuilder() + .softValues() + .build(); private static final String[] ZERO = new String[0]; private StringUtil() { @@ -496,25 +495,24 @@ public static String getMessage(Class clazz, int index, protected static String getMessage( Class clazz, int index, String[] params) { Package key = clazz.getPackage(); - Properties properties = _propFiles.get(key); - if (properties == null) { - ClassLoaderSourceDescriptor source = - new ClassLoaderSourceDescriptor(); + Properties properties = _propFiles.get(key, k -> { + ClassLoaderSourceDescriptor source = new ClassLoaderSourceDescriptor(); source.setSystemID("message.properties"); source.setNeighborClass(clazz); - properties = new Properties(); - _propFiles.put(key, properties); + Properties prop = new Properties(); if (source.exists()) { InputStream stream = source.getInputStream(); try { - properties.load(stream); + prop.load(stream); } catch (IOException e) { throw new RuntimeException(e); } finally { IOUtil.close(stream); } } - } + return prop; + }); + String className = ObjectUtil.getSimpleClassName(clazz); StringBuilder propertyName = new StringBuilder(className); if (index > 0) { diff --git a/src-impl/org/seasar/mayaa/impl/util/WeakValueHashMap.java b/src-impl/org/seasar/mayaa/impl/util/WeakValueHashMap.java deleted file mode 100644 index 7cb34ae9..00000000 --- a/src-impl/org/seasar/mayaa/impl/util/WeakValueHashMap.java +++ /dev/null @@ -1,239 +0,0 @@ -/* - * 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.util.Iterator; -import java.util.LinkedHashMap; -import java.util.Map; - -import org.apache.commons.collections.map.ReferenceMap; -import org.apache.commons.collections.map.AbstractReferenceMap; - -/** - * オブジェクトキャッシュ用に指定した件数を強参照で保持して残りは弱参照で保持するマップ。 - * 強参照で保持する件数を超えた場合はLRUで弱参照に移動させる。 - * - * @author Watanabe, Mitsutaka (Re-implemented) - */ -public class WeakValueHashMap { - - /** 強参照で保持させるデフォルトの件数。 */ - private static final int DEFAULT_HARD_SIZE = 128; - - /** キャッシュレコードを強参照で保持するマップ。指定した件数を超える場合はLRUで破棄する。 */ - private final LruHashMap _hardReferenceLruMap; - - /** - * キャッシュレコードを弱参照で保持するマップ。LRUで追い出されたレコードを保持する。 - * このマップに保持されているレコードが再び参照された場合は改めて強参照のマップに移動させる。 - */ - @SuppressWarnings("unchecked") - private final Map> _weakReferenceMap = new ReferenceMap(AbstractReferenceMap.HARD, AbstractReferenceMap.WEAK, true); - - - /** 同期化オブジェクト */ - private final Object _mutex = new Object(); - - private long _droppedCount = 0; - private long _pulledUpCount = 0; - private long _maxCountOfDroppedRecord = 0; - private long hitCount = 0; - private long missCount = 0; - - private void resetCountersIfNecessary() { - final long THRESHOLD = Long.MAX_VALUE; - if (hitCount >= THRESHOLD || missCount >= THRESHOLD) { - hitCount = 0; - missCount = 0; - _droppedCount = 0; - _pulledUpCount = 0; - _maxCountOfDroppedRecord = 0; - } - } - - /** - * 参照カウンタ付きのキャッシュレコード保持 - */ - private static class CountedReference { - private final V referent; - private int count = 0; - - CountedReference(V referent) { - this.referent = referent; - } - - V get() { - return referent; - } - - int getCount() { - return count; - } - - void countUp() { - ++count; - } - } - - private class LruHashMap extends LinkedHashMap> { - - private static final long serialVersionUID = 8437986358875751211L; - - private int maxSize; - - public LruHashMap(int maxSize) { - super(maxSize, 0.90f, true); - this.maxSize = maxSize; - } - - /** - * 強参照で保持するキャッシュサイズを設定する。現在保持している強参照のキャッシュレコードよりも - * 小さい値を指定した場合は内部的に removeEldestEntry を呼び出して弱参照マップへ移行させる。 - * - * @param maxSize 強参照で保持するキャッシュサイズ - */ - public void setMaxSize(int maxSize) { - this.maxSize = maxSize; - if (size() > maxSize) { - synchronized (_mutex) { - Iterator>> itr = entrySet().iterator(); - while (itr.hasNext()) { - if (removeEldestEntry(itr.next())) { - itr.remove(); - } else { - break; - } - } - } - } - } - - /** - * @return the maxSize - */ - public int getMaxSize() { - return maxSize; - } - - /** - * LinkedHashMapの削除判定を行う。強参照で保持する件数を超える場合は、弱参照のマップへ移動させる。 - * 呼び出し元で synchronized ブロックで囲まれているため個別に動機化は必要ない。 - */ - @Override - protected boolean removeEldestEntry(Map.Entry> eldest) { - if (size() > maxSize) { - synchronized (_mutex) { - _weakReferenceMap.put(eldest.getKey(), eldest.getValue()); - ++_droppedCount; - _maxCountOfDroppedRecord = Math.max(_maxCountOfDroppedRecord, eldest.getValue().getCount()); - } - return true; - } - return false; - } - } - - public WeakValueHashMap() { - this(DEFAULT_HARD_SIZE); - } - - public WeakValueHashMap(int hardSize) { - _hardReferenceLruMap = new LruHashMap(hardSize); - } - - public long getHitCount() { - return hitCount; - } - - public long getMissCount() { - return missCount; - } - - public V get(K key) { - synchronized (_mutex) { - resetCountersIfNecessary(); - - CountedReference ref = _hardReferenceLruMap.get(key); - if (ref != null) { - ref.countUp(); - hitCount++; - return ref.get(); - } - - // assert(ref == null) - ref = _weakReferenceMap.get(key); - if (ref != null) { - ref.countUp(); - ++_pulledUpCount; - _hardReferenceLruMap.put(key, ref); - _weakReferenceMap.remove(key); - hitCount++; - return ref.get(); - } - - missCount++; - return null; - } - } - - /** - * putは比較的コストが高くても良いとする。 - * - * @param key キャッシュキー - * @param value 値 - */ - public void put(K key, V value) { - CountedReference r = new CountedReference(value); - synchronized (_mutex) { - _hardReferenceLruMap.put(key, r); - if (_weakReferenceMap.remove(key) != null) { - // 弱参照のマップに含まれていた場合はPullUpされたことになる。 - // ただし値自体は変わっている可能性がある。 - ++_pulledUpCount; - } - } - } - - public int size() { - synchronized (_mutex) { - return _weakReferenceMap.size() + _hardReferenceLruMap.size(); - } - } - - public void setHardSize(int hardSize) { - synchronized (_mutex) { - _hardReferenceLruMap.setMaxSize(hardSize); - } - } - - public int getHardSize() { - synchronized (_mutex) { - return _hardReferenceLruMap.getMaxSize(); - } - } - - public long getDroppedCount() { - return _droppedCount; - } - - public long getPullUpCount() { - return _pulledUpCount; - } - - public long getMaxCountOfDroppedRecord() { - return _maxCountOfDroppedRecord; - } -} diff --git a/src/test/java/org/seasar/mayaa/functional/EngineTestBase.java b/src/test/java/org/seasar/mayaa/functional/EngineTestBase.java index 3826dd07..e90e235a 100644 --- a/src/test/java/org/seasar/mayaa/functional/EngineTestBase.java +++ b/src/test/java/org/seasar/mayaa/functional/EngineTestBase.java @@ -233,6 +233,7 @@ public void setup() throws SecurityException, IOException { FactoryFactory.setContext(servletContext); engine = ProviderUtil.getEngine(); + engine.reset(); // デフォルトのエラーハンドラを無効化して内部の例外でJUnitを失敗させる。 engine.setErrorHandler(new NullErrorHandler()); diff --git a/src/test/java/org/seasar/mayaa/impl/cycle/script/rhino/ScriptEnvironmentTest.java b/src/test/java/org/seasar/mayaa/impl/cycle/script/rhino/ScriptEnvironmentTest.java index 62412aee..dfd57127 100644 --- a/src/test/java/org/seasar/mayaa/impl/cycle/script/rhino/ScriptEnvironmentTest.java +++ b/src/test/java/org/seasar/mayaa/impl/cycle/script/rhino/ScriptEnvironmentTest.java @@ -62,26 +62,6 @@ public void setup() { } - @Test - public void testキャッシュサイズの指定() { - // setupメソッドで実行することをいったんリセットして本メソッド内で実行 - init(); - - // -- Given - // * ./WEB-INF/org.seasar.mayaa.provider.ServiceProvider 内の - // scriptEnvironment cacheSize に 256 が設定されている - FactoryFactory.setContext(new MockServletContext(this.getClass().getPackage().getName().replace('.', '/'))); - - // -- When - // ファクトリーを経由してScriptEnvironmentインスタンスを生成する - // ServiceProviderのインスタンスを取得することでScriptEnvironmentを作成する。 - testee = (ScriptEnvironmentImpl) ProviderUtil.getScriptEnvironment(); - - // -- Then - // スクリプトのキャッシュ設定値が256に設定されている。 - assertEquals(256, testee.getScriptCacheSize()); - } - CompiledScript runScript(String script) { final PositionAware position = new Position("/1", 1); @@ -150,8 +130,6 @@ CompiledScript runScript(String script) { final PositionAware position = new Position("/1", 1); - testee.setScriptCacheSize(1); - final int threadsCount = 100; final Random random = new Random(); final CountDownLatch latch = new CountDownLatch(threadsCount); diff --git a/src/test/java/org/seasar/mayaa/impl/util/WeakValueHashMapTest.java b/src/test/java/org/seasar/mayaa/impl/util/WeakValueHashMapTest.java deleted file mode 100644 index cd9ec10e..00000000 --- a/src/test/java/org/seasar/mayaa/impl/util/WeakValueHashMapTest.java +++ /dev/null @@ -1,157 +0,0 @@ -/* - * 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 static org.junit.jupiter.api.Assertions.assertEquals; - -import org.junit.jupiter.api.Test; - -public class WeakValueHashMapTest { - - @Test - public void 引数なしコンストラクタの場合はデフォルトサイズの128で作成される() { - WeakValueHashMap testee = new WeakValueHashMap<>(); - - assertEquals(128, testee.getHardSize()); - } - - @Test - public void 指定したサイズを基準に強参照と弱参照が移動する() { - WeakValueHashMap testee = new WeakValueHashMap<>(2); - - testee.put("key1", "val1"); - testee.put("key2", "val2"); - assertEquals(0, testee.getDroppedCount()); - assertEquals(0, testee.getPullUpCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - // key3 を追加することで key1 が弱参照になる(ドロップする)。 - // key1は参照されていないため getMaxCountOfDroppedRecord()は0のまま。 - testee.put("key3", "val3"); - assertEquals(1, testee.getDroppedCount()); - assertEquals(0, testee.getPullUpCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - // key3 は現在は強参照のため参照してもDroppedカウントもPullUpカウントは上がらない。 - assertEquals("val3", testee.get("key3")); - assertEquals(1, testee.getDroppedCount()); - assertEquals(0, testee.getPullUpCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - // key4 を追加することで key2 が弱参照になる - testee.put("key4", "val4"); - assertEquals(2, testee.getDroppedCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - // key3 はまだ強参照のため参照してもDroppedカウントもPullUpカウントは上がらない。 - assertEquals(testee.get("key3"), "val3"); - assertEquals(2, testee.getDroppedCount()); - assertEquals(0, testee.getPullUpCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - // key1 は弱参照から取得されるため key3 がドロップし、key1がプルアップされる。 - assertEquals("val1", testee.get("key1")); - assertEquals(3, testee.getDroppedCount()); - assertEquals(1, testee.getPullUpCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - // key1 は現在は強参照のため参照してもDroppedカウントもPullUpカウントは上がらない。 - assertEquals("val1", testee.get("key1")); - assertEquals(3, testee.getDroppedCount()); - assertEquals(1, testee.getPullUpCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - // key2 は弱参照から取得されるため key3 がドロップし、key2がプルアップされる。 - // key3は2回参照されたため getMaxCountOfDroppedRecord() が2を返却する。 - assertEquals("val2", testee.get("key2")); - assertEquals(4, testee.getDroppedCount()); - assertEquals(2, testee.getPullUpCount()); - assertEquals(2, testee.getMaxCountOfDroppedRecord()); - - assertEquals("val4", testee.get("key4")); - assertEquals(5, testee.getDroppedCount()); - assertEquals(3, testee.getPullUpCount()); - assertEquals(2, testee.getMaxCountOfDroppedRecord()); - } - - @Test - public void 弱参照のレコードが削除される() throws InterruptedException { - WeakValueHashMap testee = new WeakValueHashMap<>(2); - - - testee.put("key1", new String("val1")); - assertEquals(1, testee.size()); - - testee.put("key2", new String("val2")); - assertEquals(2, testee.size()); - - for (int i = 3; i < 10; ++i) { - testee.put("key" + i, new String("val" + i)); - assertEquals(i, testee.size()); - } - - System.gc(); - Thread.sleep(2); // GCのスレッドに処理を明け渡して完了を少し待つ - - assertEquals(2, testee.size()); - } - - @Test - public void 指定したサイズを変更しても追従する() { - WeakValueHashMap testee = new WeakValueHashMap<>(2); - - assertEquals(2, testee.getHardSize()); - - testee.put("key1", "val1"); - testee.put("key2", "val2"); - testee.put("key3", "val3"); - testee.put("key4", "val4"); - - assertEquals(2, testee.getDroppedCount()); - assertEquals(0, testee.getPullUpCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - testee.setHardSize(3); - - testee.put("key1", "val1"); - assertEquals(2, testee.getDroppedCount()); // ドロップしない - assertEquals(1, testee.getPullUpCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - testee.put("key2", "val2"); - assertEquals(3, testee.getDroppedCount()); // key3がドロップする - assertEquals(2, testee.getPullUpCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - testee.setHardSize(2); - - // 減らした時点でkey4がドロップする - assertEquals(4, testee.getDroppedCount()); - - // key1はまだ強参照のままなのでドロップしない - testee.get("key1"); - assertEquals(4, testee.getDroppedCount()); - - // key3は弱参照からプルアップされ、key2がドロップする - testee.get("key3"); - assertEquals(5, testee.getDroppedCount()); - assertEquals(3, testee.getPullUpCount()); - assertEquals(0, testee.getMaxCountOfDroppedRecord()); - - } - -} - diff --git a/src/test/java/org/seasar/mayaa/regressions/issue14/Issue14ReproductionTest.java b/src/test/java/org/seasar/mayaa/regressions/issue14/Issue14ReproductionTest.java deleted file mode 100644 index 243c0b49..00000000 --- a/src/test/java/org/seasar/mayaa/regressions/issue14/Issue14ReproductionTest.java +++ /dev/null @@ -1,118 +0,0 @@ -/* - * 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.regressions.issue14; - -import static org.junit.jupiter.api.Assertions.fail; - -import java.util.NoSuchElementException; -import java.util.Random; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.atomic.AtomicBoolean; - -import org.junit.jupiter.api.Test; -import org.seasar.mayaa.impl.util.WeakValueHashMap; - -/** - * https://github.com/seasarorg/mayaa/issues/14 を元に {@code WeakValueHashMap} で {@link NoSuchElementException} が - * 発生する問題の再現を確認するコードとしてJavaに焼き直したもの。 - * 再現するとJUnitとして失敗する。 - */ -public class Issue14ReproductionTest { - - static final int cacheSize = 3; // Mayaaの実装と同じ - - static final AtomicBoolean foundNSEE = new AtomicBoolean(false); - static final WeakValueHashMap l = new WeakValueHashMap<>(cacheSize); - - public static void main(final String[] args) throws InterruptedException { - final Issue14ReproductionTest testee = new Issue14ReproductionTest(); - testee.testReproduceTrial(); - } - - /** - * repdocudeMainを何回か試行して再現確認を行う。 - */ - @Test - public void testReproduceTrial() throws InterruptedException { - final int THREAD_COUNT = 20; - final int TRIAL_COUNT = 1000; - - for (int trialId = 0; trialId < TRIAL_COUNT && !foundNSEE.get(); trialId++) { - if (reproduceMain(trialId, THREAD_COUNT)) { - fail("Reproduced! Trial:" + trialId); - } - } - } - - - /** - * NoSuchElementExceptioの再現の主処理。 - * 複数のスレッドで同時に WeakValueHashMapを参照する。 - * - * @param trialId 試行番号 - * @param threadsCount 同時実行するスレッド数 - * @return NoSuchElementExceptionが発生したら(再現したら) true - * @throws InterruptedException スレッド待ち合わせ処理が中断した場合 - */ - boolean reproduceMain(final int trialId, final int threadsCount) throws InterruptedException { - final CountDownLatch latch = new CountDownLatch(threadsCount); - - Random random = new Random(trialId); - - Thread[] threads = new Thread[threadsCount]; - for (int requestId = 0; requestId < threadsCount; requestId++) { - final String id = "" + random.nextInt(10); - if (random.nextInt(100) == 0) { - System.gc(); - // System.out.println("gc---------"); - } - // if (requestId % 100 == 0) { - // System.out.println(l.getMaxCountOfDroppedRecord()); - // } - - threads[requestId] = new Thread() { - public void run() { - try { - String key = "ITEM:" + id; - - // 一斉に動いた方がぶつかりやすいので待ち合わせる - latch.countDown(); - latch.await(); - - l.put(key, id); - l.get(key); - } catch (final NoSuchElementException e) { - e.printStackTrace(System.err); - foundNSEE.set(true); - } catch (InterruptedException e) { - e.printStackTrace(); - } - } - }; - threads[requestId].start(); - } - - // スレッドの終了を待ち合わせる - for (int requestId = 0; requestId < threadsCount; requestId++) { - threads[requestId].join(); - } - - // 結果の判定 - return foundNSEE.get(); - } - - -}