-
Notifications
You must be signed in to change notification settings - Fork 532
refactor: split ClassUtils into metadata resolvers #1099
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
nkuprins
wants to merge
20
commits into
apache:main
Choose a base branch
from
nkuprins:refactor/extract-classutils
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
20 commits
Select commit
Hold shift + click to select a range
e6131b4
refactor: split ClassUtils into cache resolvers
nkuprins d89e3e8
chore: document the new internal helpers
nkuprins da5b743
chore: rename MemoryCache to InMemoryCache
nkuprins e867ad5
refactor: select the cache tier from the holder
nkuprins f015bea
refactor: deprecate the public cache fields
nkuprins f2474f0
chore: improve comments
nkuprins d60781f
refactor: let each cache tier own its storage
nkuprins fa7bee1
refactor: let callers pick the thread map type
nkuprins 8cb236f
chore: clarify the cache view contracts
nkuprins 90ee3f5
test: cover the content cache views
nkuprins 237c809
test: assert thread-local clearing by behaviour
nkuprins bd9b639
refactor: clear the memory tier through its field
nkuprins f0b8b8a
chore: document the scope of cache clearing
nkuprins 85d3180
refactor: build the cache view once
nkuprins 3eaf78b
chore: align the content resolver method names
nkuprins 0aa9e8f
test: cover detaching the thread-local cache
nkuprins 09a3551
refactor: fix the in-memory cache to a concurrent map
nkuprins fa83af8
refactor: simplify the thread-local cache init
nkuprins 26780cc
chore: clarify the content property map name
nkuprins 8fd1b03
chore: use generic spreadsheet wording in javadoc
nkuprins File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
473 changes: 59 additions & 414 deletions
473
fesod-sheet/src/main/java/org/apache/fesod/sheet/util/ClassUtils.java
Large diffs are not rendered by default.
Oops, something went wrong.
132 changes: 132 additions & 0 deletions
132
fesod-sheet/src/main/java/org/apache/fesod/sheet/util/MetadataCacheStrategy.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,132 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you 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.apache.fesod.sheet.util; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.Map; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| import java.util.function.Function; | ||
| import java.util.function.Supplier; | ||
| import org.apache.fesod.common.util.MapUtils; | ||
| import org.apache.fesod.sheet.enums.CacheLocationEnum; | ||
|
|
||
| /** | ||
| * Internal helper used by {@link MetadataCaches} for caching resolved metadata, one implementation per | ||
| * {@link CacheLocationEnum} constant. | ||
| * <p> | ||
| * Not intended for direct use; use {@link ClassUtils} as the primary entry point instead. | ||
| * </p> | ||
| * | ||
| * @param <K> the cache key | ||
| * @param <V> the cached metadata | ||
| */ | ||
| interface MetadataCacheStrategy<K, V> { | ||
|
|
||
| V get(K key, Function<K, V> mappingFunction); | ||
|
|
||
| /** | ||
| * Drops the cached entries visible to the calling thread: every entry for a shared cache, only the | ||
| * calling thread's own for a per-thread one. {@code ThreadLocal}-backed implementations must detach the entry from | ||
| * the thread ({@link ThreadLocal#remove()}) rather than merely empty the map they hold, otherwise | ||
| * pooled threads keep the entry forever. | ||
| */ | ||
| void clear(); | ||
|
|
||
| /** | ||
| * The cache is kept until the app is stopped or {@link #clear()} is called. | ||
| */ | ||
| class InMemoryCache<K, V> implements MetadataCacheStrategy<K, V> { | ||
|
|
||
| private final ConcurrentHashMap<K, V> cache = new ConcurrentHashMap<>(); | ||
|
|
||
| private final Map<K, V> view = Collections.unmodifiableMap(cache); | ||
|
|
||
| @Override | ||
| public V get(K key, Function<K, V> mappingFunction) { | ||
| return cache.computeIfAbsent(key, mappingFunction); | ||
| } | ||
|
|
||
| @Override | ||
| public void clear() { | ||
| cache.clear(); | ||
| } | ||
|
|
||
| /** | ||
| * Read-only view of the cached entries. | ||
| */ | ||
| Map<K, V> view() { | ||
| return view; | ||
| } | ||
|
|
||
| /** | ||
| * The live map, only for the deprecated public cache fields on {@link ClassUtils}; remove with them. | ||
| */ | ||
| ConcurrentHashMap<K, V> backingMap() { | ||
| return cache; | ||
| } | ||
| } | ||
|
bengbengbalabalabeng marked this conversation as resolved.
|
||
|
|
||
| /** | ||
| * The cache will be stored in {@code ThreadLocal}, and will be cleared when the spreadsheet | ||
| * read and write is completed. | ||
| */ | ||
| class ThreadLocalCache<K, V> implements MetadataCacheStrategy<K, V> { | ||
|
|
||
| private final ThreadLocal<Map<K, V>> cache; | ||
|
|
||
| ThreadLocalCache() { | ||
| this(MapUtils::newHashMap); | ||
| } | ||
|
|
||
| /** | ||
| * Creates each thread's map with {@code mapFactory}, to allow map types other than the default | ||
| * {@link java.util.HashMap}. The factory must return a new map on every call. | ||
| */ | ||
| ThreadLocalCache(Supplier<Map<K, V>> mapFactory) { | ||
| this.cache = ThreadLocal.withInitial(mapFactory); | ||
| } | ||
|
|
||
| @Override | ||
| public V get(K key, Function<K, V> mappingFunction) { | ||
| return cache.get().computeIfAbsent(key, mappingFunction); | ||
| } | ||
|
bengbengbalabalabeng marked this conversation as resolved.
|
||
|
|
||
| @Override | ||
| public void clear() { | ||
| cache.remove(); | ||
| } | ||
| } | ||
|
|
||
| /** | ||
| * No caching. It may lose some of performance. | ||
| */ | ||
| class NoOpCache<K, V> implements MetadataCacheStrategy<K, V> { | ||
|
|
||
| @Override | ||
| public V get(K key, Function<K, V> mappingFunction) { | ||
| return mappingFunction.apply(key); | ||
| } | ||
|
|
||
| @Override | ||
| public void clear() { | ||
| // nothing is cached | ||
| } | ||
| } | ||
| } | ||
97 changes: 97 additions & 0 deletions
97
fesod-sheet/src/main/java/org/apache/fesod/sheet/util/MetadataCaches.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,97 @@ | ||
| /* | ||
| * Licensed to the Apache Software Foundation (ASF) under one | ||
| * or more contributor license agreements. See the NOTICE file | ||
| * distributed with this work for additional information | ||
| * regarding copyright ownership. The ASF licenses this file | ||
| * to you 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.apache.fesod.sheet.util; | ||
|
|
||
| import java.util.Collections; | ||
| import java.util.EnumMap; | ||
| import java.util.Map; | ||
| import java.util.concurrent.ConcurrentHashMap; | ||
| import java.util.function.Function; | ||
| import org.apache.fesod.sheet.enums.CacheLocationEnum; | ||
| import org.apache.fesod.sheet.metadata.ConfigurationHolder; | ||
|
|
||
| /** | ||
| * Internal helper used by {@link SheetHeadFieldResolver} and {@link SheetContentPropertyResolver} for | ||
| * caching resolved metadata in the tier the current read or write is configured for. | ||
| * <p> | ||
| * Not intended for direct use; use {@link ClassUtils} as the primary entry point instead. | ||
| * </p> | ||
| * | ||
| * @param <K> the cache key | ||
| * @param <V> the cached metadata | ||
| */ | ||
| final class MetadataCaches<K, V> { | ||
|
|
||
| private final MetadataCacheStrategy.InMemoryCache<K, V> inMemoryCache = new MetadataCacheStrategy.InMemoryCache<>(); | ||
|
|
||
| private final Map<CacheLocationEnum, MetadataCacheStrategy<K, V>> byLocation; | ||
|
|
||
| MetadataCaches() { | ||
| Map<CacheLocationEnum, MetadataCacheStrategy<K, V>> strategies = new EnumMap<>(CacheLocationEnum.class); | ||
| strategies.put(CacheLocationEnum.MEMORY, inMemoryCache); | ||
| strategies.put(CacheLocationEnum.THREAD_LOCAL, new MetadataCacheStrategy.ThreadLocalCache<>()); | ||
| strategies.put(CacheLocationEnum.NONE, new MetadataCacheStrategy.NoOpCache<>()); | ||
| this.byLocation = Collections.unmodifiableMap(strategies); | ||
| } | ||
|
|
||
| /** | ||
| * Read-only view of the {@link CacheLocationEnum#MEMORY} cache. | ||
| */ | ||
| Map<K, V> memoryView() { | ||
| return inMemoryCache.view(); | ||
| } | ||
|
|
||
| /** | ||
| * The live {@link CacheLocationEnum#MEMORY} map, only for the deprecated public cache fields on | ||
| * {@link ClassUtils}; remove with them. | ||
| */ | ||
| ConcurrentHashMap<K, V> memoryBackingMap() { | ||
| return inMemoryCache.backingMap(); | ||
| } | ||
|
|
||
| /** | ||
| * Caches in the tier {@code configurationHolder} is configured for. | ||
| */ | ||
| V get(ConfigurationHolder configurationHolder, K key, Function<K, V> mappingFunction) { | ||
| CacheLocationEnum cacheLocation = | ||
| configurationHolder.globalConfiguration().getFiledCacheLocation(); | ||
| return at(cacheLocation).get(key, mappingFunction); | ||
| } | ||
|
|
||
| void clearThreadLocal() { | ||
| at(CacheLocationEnum.THREAD_LOCAL).clear(); | ||
| } | ||
|
|
||
| void clearInMemory() { | ||
| inMemoryCache.clear(); | ||
| } | ||
|
|
||
| /** | ||
| * Looks up the strategy configured for {@code cacheLocation}, failing loudly when a | ||
| * {@link CacheLocationEnum} constant has no strategy registered for it. | ||
| */ | ||
| private MetadataCacheStrategy<K, V> at(CacheLocationEnum cacheLocation) { | ||
| MetadataCacheStrategy<K, V> strategy = byLocation.get(cacheLocation); | ||
| if (strategy == null) { | ||
| throw new UnsupportedOperationException("unsupported enum"); | ||
| } | ||
| return strategy; | ||
| } | ||
| } |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.