• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

burningwave / reflection / #81

23 Oct 2023 10:22AM UTC coverage: 74.528%. Remained the same
#81

push

Roberto-Gentili
Releasing new version

790 of 1060 relevant lines covered (74.53%)

0.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

62.67
/src/main/java/org/burningwave/reflection/Cache.java
1
/*
2
 * This file is part of Burningwave Reflection.
3
 *
4
 * Author: Roberto Gentili
5
 *
6
 * Hosted at: https://github.com/burningwave/reflection
7
 *
8
 * --
9
 *
10
 * The MIT License (MIT)
11
 *
12
 * Copyright (c) 2022-2023 Roberto Gentili
13
 *
14
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
15
 * documentation files (the "Software"), to deal in the Software without restriction, including without
16
 * limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
17
 * the Software, and to permit persons to whom the Software is furnished to do so, subject to the following
18
 * conditions:
19
 *
20
 * The above copyright notice and this permission notice shall be included in all copies or substantial
21
 * portions of the Software.
22
 *
23
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
24
 * LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25
 * EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
26
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
27
 * OR OTHER DEALINGS IN THE SOFTWARE.
28
 */
29
package org.burningwave.reflection;
30

31
import java.lang.reflect.Constructor;
32
import java.lang.reflect.Field;
33
import java.lang.reflect.Method;
34
import java.util.Arrays;
35
import java.util.Collection;
36
import java.util.HashSet;
37
import java.util.Map;
38
import java.util.Map.Entry;
39
import java.util.Set;
40
import java.util.concurrent.ConcurrentHashMap;
41
import java.util.function.BiConsumer;
42
import java.util.function.Function;
43
import java.util.function.Supplier;
44

45
import org.burningwave.Synchronizer;
46
import org.burningwave.ThrowingBiConsumer;
47

48

49

50

51
class Cache {
52

53
        public final static Cache INSTANCE;
54

55
        static {
56
                INSTANCE = new Cache();
1✔
57
        }
1✔
58

59
        final PathForResources<Constructor<?>[]> uniqueKeyForConstructorsArray;
60
        final PathForResources<Field[]> uniqueKeyForFieldsArray;
61
        final PathForResources<Method[]> uniqueKeyForMethodsArray;
62
        final PathForResources<Collection<Constructor<?>>> uniqueKeyForConstructors;
63
        final PathForResources<Members.Handler.OfExecutable.Box<?>> uniqueKeyForExecutableAndMethodHandle;
64
        final PathForResources<Collection<Field>> uniqueKeyForAllFields;
65
        final PathForResources<Collection<Method>> uniqueKeyForAllMethods;
66

67
        private Cache() {
1✔
68
                uniqueKeyForConstructorsArray = new PathForResources<>();
1✔
69
                uniqueKeyForFieldsArray = new PathForResources<>();
1✔
70
                uniqueKeyForMethodsArray = new PathForResources<>();
1✔
71
                uniqueKeyForConstructors = new PathForResources<>();
1✔
72
                uniqueKeyForExecutableAndMethodHandle = new PathForResources<>();
1✔
73
                uniqueKeyForAllFields = new PathForResources<>();
1✔
74
                uniqueKeyForAllMethods = new PathForResources<>();
1✔
75
        }
1✔
76

77
        void clear(boolean destroyItems, Object... excluded) {
78
                Set<Object> toBeExcluded = (excluded != null) && (excluded.length > 0) ?
1✔
79
                        new HashSet<>(Arrays.asList(excluded)) :
1✔
80
                        null;
81
                Set<Runnable> deepCleaners = new HashSet<>();
1✔
82
                addCleaningTask(deepCleaners, clear(uniqueKeyForConstructorsArray, toBeExcluded, destroyItems));
1✔
83
                addCleaningTask(deepCleaners, clear(uniqueKeyForFieldsArray, toBeExcluded, destroyItems));
1✔
84
                addCleaningTask(deepCleaners, clear(uniqueKeyForMethodsArray, toBeExcluded, destroyItems));
1✔
85
                addCleaningTask(deepCleaners, clear(uniqueKeyForConstructors, toBeExcluded, destroyItems));
1✔
86
                addCleaningTask(deepCleaners, clear(uniqueKeyForExecutableAndMethodHandle, toBeExcluded, destroyItems));
1✔
87
                addCleaningTask(deepCleaners, clear(uniqueKeyForAllFields, toBeExcluded, destroyItems));
1✔
88
                addCleaningTask(deepCleaners, clear(uniqueKeyForAllMethods, toBeExcluded, destroyItems));
1✔
89
                new Thread(() -> {
1✔
90
                        for (Runnable task : deepCleaners) {
1✔
91
                                task.run();
1✔
92
                        }
1✔
93
                }).start();
1✔
94

95
        }
1✔
96

97
        private boolean addCleaningTask(Set<Runnable> tasks, Runnable task) {
98
                if (task != null) {
1✔
99
                        return tasks.add(task);
1✔
100
                }
101
                return false;
×
102
        }
103

104
        private Runnable clear(Object cache, Set<Object> excluded, boolean destroyItems) {
105
                if ((excluded == null) || !excluded.contains(cache)) {
1✔
106
                        if (cache instanceof PathForResources) {
1✔
107
                                return ((PathForResources<?>)cache).clear(destroyItems);
1✔
108
                        }
109
                }
110
                return null;
×
111
        }
112

113
        static class PathForResources<R> {
114
                String instanceId;
115
                BiConsumer<String, R> itemDestroyer;
116
                Long partitionStartLevel;
117
                Map<Long, Map<String, Map<String, R>>> resources;
118
                Function<R, R> sharer;
119

120
                private PathForResources() {
121
                        this(1L, item -> item, null);
1✔
122
                }
1✔
123

124
                private PathForResources(BiConsumer<String, R> itemDestroyer) {
125
                        this(1L, item -> item, itemDestroyer);
×
126
                }
×
127

128
                private PathForResources(Function<R, R> sharer) {
129
                        this(1L, sharer, null);
×
130
                }
×
131

132
                private PathForResources(Function<R, R> sharer, BiConsumer<String, R> itemDestroyer) {
133
                        this(1L, item -> item, itemDestroyer);
×
134
                }
×
135

136
                private PathForResources(Long partitionStartLevel) {
137
                        this(partitionStartLevel, item -> item, null);
×
138
                }
×
139

140
                private PathForResources(Long partitionStartLevel, BiConsumer<String, R> itemDestroyer) {
141
                        this(partitionStartLevel, item -> item, itemDestroyer);
×
142
                }
×
143

144
                private PathForResources(Long partitionStartLevel, Function<R, R> sharer) {
145
                        this(partitionStartLevel, sharer, null);
×
146
                }
×
147

148
                private PathForResources(Long partitionStartLevel, Function<R, R> sharer, BiConsumer<String, R> itemDestroyer) {
1✔
149
                        this.partitionStartLevel = partitionStartLevel;
1✔
150
                        this.sharer = sharer;
1✔
151
                        this.resources = new ConcurrentHashMap<>();
1✔
152
                        this.itemDestroyer = itemDestroyer;
1✔
153
                        this.instanceId = this.toString();
1✔
154
                }
1✔
155

156
                <K, V, E extends Throwable> void deepClear(Map<K,V> map, ThrowingBiConsumer<K, V, E> itemDestroyer) throws E {
157
                        java.util.Iterator<Entry<K, V>> itr = map.entrySet().iterator();
×
158
                        while (itr.hasNext()) {
×
159
                                Entry<K, V> entry = itr.next();
×
160
                                try {
161
                                        itr.remove();
×
162
                                        itemDestroyer.accept(entry.getKey(), entry.getValue());
×
163
                                } catch (Throwable exc) {
×
164

165
                                }
×
166
                        }
×
167
                }
×
168

169
                R get(String path) {
170
                        return getOrUploadIfAbsent(path, null);
1✔
171
                }
172

173
                int getLoadedResourcesCount() {
174
                        return getLoadedResourcesCount(resources);
×
175
                }
176

177
                R getOrUploadIfAbsent(String path, Supplier<R> resourceSupplier) {
178
                        Long occurences = path.chars().filter(ch -> ch == '/').count();
1✔
179
                        Long partitionIndex = occurences > partitionStartLevel? occurences : partitionStartLevel;
1✔
180
                        Map<String, Map<String, R>> partion = retrievePartition(resources, partitionIndex);
1✔
181
                        Map<String, R> nestedPartition = retrievePartition(partion, partitionIndex, path);
1✔
182
                        return getOrUploadIfAbsent(nestedPartition, path, resourceSupplier);
1✔
183
                }
184

185
                R remove(String path, boolean destroy) {
186
                        Long occurences = path.chars().filter(ch -> ch == '/').count();
×
187
                        Long partitionIndex = occurences > partitionStartLevel? occurences : partitionStartLevel;
×
188
                        Map<String, Map<String, R>> partion = retrievePartition(resources, partitionIndex);
×
189
                        Map<String, R> nestedPartition = retrievePartition(partion, partitionIndex, path);
×
190
                        R item =  Synchronizer.INSTANCE.execute(instanceId + "_mutexManagerForLoadedResources_" + path, () -> {
×
191
                                return nestedPartition.remove(path);
×
192
                        });
193
                        if ((itemDestroyer != null) && destroy && (item != null)) {
×
194
                                String finalPath = path;
×
195
                                itemDestroyer.accept(finalPath, item);
×
196
                        }
197
                        return item;
×
198
                }
199

200
                R upload(Map<String, R> loadedResources, String path, Supplier<R> resourceSupplier, boolean destroy) {
201
                        R oldResource = remove(path, destroy);
×
202
                         Synchronizer.INSTANCE.execute(instanceId + "_mutexManagerForLoadedResources_" + path, () -> {
×
203
                                R resourceTemp = resourceSupplier.get();
×
204
                                if (resourceTemp != null) {
×
205
                                        loadedResources.put(path, resourceTemp = sharer.apply(resourceTemp));
×
206
                                }
207
                        });
×
208
                        return oldResource;
×
209
                }
210

211
                R upload(String path, Supplier<R> resourceSupplier, boolean destroy) {
212
                        Long occurences = path.chars().filter(ch -> ch == '/').count();
×
213
                        Long partitionIndex = occurences > partitionStartLevel? occurences : partitionStartLevel;
×
214
                        Map<String, Map<String, R>> partion = retrievePartition(resources, partitionIndex);
×
215
                        Map<String, R> nestedPartition = retrievePartition(partion, partitionIndex, path);
×
216
                        return upload(nestedPartition, path, resourceSupplier, destroy);
×
217
                }
218

219
                void clearResources(Map<Long, Map<String, Map<String, R>>> partitions, boolean destroyItems) {
220
                        for (Entry<Long, Map<String, Map<String, R>>> partition : partitions.entrySet()) {
1✔
221
                                for (Entry<String, Map<String, R>> nestedPartition : partition.getValue().entrySet()) {
1✔
222
                                        if ((itemDestroyer != null) && destroyItems) {
1✔
223
                                                deepClear(nestedPartition.getValue(), (path, resource) -> {
×
224
                                                        this.itemDestroyer.accept(path, resource);
×
225
                                                });
×
226
                                        } else {
227
                                                nestedPartition.getValue().clear();
1✔
228
                                        }
229
                                }
1✔
230
                                partition.getValue().clear();
1✔
231
                        }
1✔
232
                        partitions.clear();
1✔
233
                }
1✔
234

235
                R getOrUploadIfAbsent(Map<String, R> loadedResources, String path, Supplier<R> resourceSupplier) {
236
                        R resource = loadedResources.get(path);
1✔
237
                        if (resource == null) {
1✔
238
                                resource =  Synchronizer.INSTANCE.execute(instanceId + "_mutexManagerForLoadedResources_" + path, () -> {
1✔
239
                                        R resourceTemp = loadedResources.get(path);
1✔
240
                                        if ((resourceTemp == null) && (resourceSupplier != null)) {
1✔
241
                                                resourceTemp = resourceSupplier.get();
1✔
242
                                                if (resourceTemp != null) {
1✔
243
                                                        loadedResources.put(path, resourceTemp = sharer.apply(resourceTemp));
1✔
244
                                                }
245
                                        }
246
                                        return resourceTemp;
1✔
247
                                });
248
                        }
249
                        return resource != null?
1✔
250
                                sharer.apply(resource) :
1✔
251
                                resource;
252
                }
253

254
                Map<String, Map<String, R>> retrievePartition(Map<Long, Map<String, Map<String, R>>> partitionedResources, Long partitionIndex) {
255
                        Map<String, Map<String, R>> resources = partitionedResources.get(partitionIndex);
1✔
256
                        if (resources == null) {
1✔
257
                                resources = Synchronizer.INSTANCE.execute(instanceId + "_mutexManagerForPartitionedResources_" + partitionIndex.toString(), () -> {
1✔
258
                                        Map<String, Map<String, R>> resourcesTemp = partitionedResources.get(partitionIndex);
1✔
259
                                        if (resourcesTemp == null) {
1✔
260
                                                partitionedResources.put(partitionIndex, resourcesTemp = new ConcurrentHashMap<>());
1✔
261
                                        }
262
                                        return resourcesTemp;
1✔
263
                                });
264
                        }
265
                        return resources;
1✔
266
                }
267

268
                Map<String, R> retrievePartition(Map<String, Map<String, R>> partion, Long partitionIndex, String path) {
269
                        String partitionKey = "/";
1✔
270
                        if (partitionIndex > 1) {
1✔
271
                                partitionKey = path.substring(0, path.lastIndexOf("/"));
1✔
272
                                partitionKey = partitionKey.substring(partitionKey.lastIndexOf("/") + 1);
1✔
273
                        }
274
                        Map<String, R> innerPartion = partion.get(partitionKey);
1✔
275
                        if (innerPartion == null) {
1✔
276
                                String finalPartitionKey = partitionKey;
1✔
277
                                innerPartion = Synchronizer.INSTANCE.execute(instanceId + "_mutexManagerForPartitions_" + finalPartitionKey, () -> {
1✔
278
                                        Map<String, R> innerPartionTemp = partion.get(finalPartitionKey);
1✔
279
                                        if (innerPartionTemp == null) {
1✔
280
                                                partion.put(finalPartitionKey, innerPartionTemp = new ConcurrentHashMap<>());
1✔
281
                                        }
282
                                        return innerPartionTemp;
1✔
283
                                });
284
                        }
285
                        return innerPartion;
1✔
286
                }
287

288
                private Runnable clear(boolean destroyItems) {
289
                        Map<Long, Map<String, Map<String, R>>> partitions;
290
                        synchronized (this.resources) {
1✔
291
                                partitions = this.resources;
1✔
292
                                this.resources = new ConcurrentHashMap<>();
1✔
293
                        }
1✔
294
                        return () ->
1✔
295
                                clearResources(partitions, destroyItems);
1✔
296
                }
297

298
                private int getLoadedResourcesCount(Map<Long, Map<String, Map<String, R>>> resources) {
299
                        int count = 0;
×
300
                        for (Map.Entry<Long, Map<String, Map<String, R>>> partition : resources.entrySet()) {
×
301
                                for (Map.Entry<String, Map<String, R>> innerPartition : partition.getValue().entrySet()) {
×
302
                                        count += innerPartition.getValue().size();
×
303
                                }
×
304
                        }
×
305
                        return count;
×
306
                }
307
        }
308

309
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc