Skip to content

Commit d4829f9

Browse files
committed
Extract class PluginIndex
1 parent e3ced8b commit d4829f9

File tree

4 files changed

+121
-119
lines changed

4 files changed

+121
-119
lines changed
Lines changed: 16 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -17,47 +17,39 @@
1717
package org.apache.logging.log4j.plugins.processor;
1818

1919
import static org.junit.jupiter.api.Assertions.assertEquals;
20+
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
2021

21-
import java.io.IOException;
22-
import java.util.Arrays;
2322
import java.util.List;
24-
import java.util.Map;
25-
import java.util.Objects;
26-
import org.apache.logging.log4j.plugins.model.PluginCache;
2723
import org.apache.logging.log4j.plugins.model.PluginEntry;
24+
import org.apache.logging.log4j.plugins.model.PluginIndex;
2825
import org.junit.jupiter.api.Test;
2926
import org.junitpioneer.jupiter.Issue;
3027

31-
public class PluginCacheTest {
28+
public class PluginIndexTest {
3229

3330
@Test
3431
@Issue("https://issues.apache.org/jira/browse/LOG4J2-2735")
35-
public void testOutputIsReproducibleWhenInputOrderingChanges() throws IOException {
36-
final PluginCache cacheA = new PluginCache();
37-
createCategory(cacheA, "one", Arrays.asList("bravo", "alpha", "charlie"));
38-
createCategory(cacheA, "two", Arrays.asList("alpha", "charlie", "bravo"));
39-
assertEquals(cacheA.getAllNamespaces().size(), 2);
40-
assertEquals(cacheA.getAllNamespaces().get("one").size(), 3);
41-
assertEquals(cacheA.getAllNamespaces().get("two").size(), 3);
42-
final PluginCache cacheB = new PluginCache();
43-
createCategory(cacheB, "two", Arrays.asList("bravo", "alpha", "charlie"));
44-
createCategory(cacheB, "one", Arrays.asList("alpha", "charlie", "bravo"));
45-
assertEquals(cacheB.getAllNamespaces().size(), 2);
46-
assertEquals(cacheB.getAllNamespaces().get("one").size(), 3);
47-
assertEquals(cacheB.getAllNamespaces().get("two").size(), 3);
48-
assertEquals(Objects.toString(cacheA.getAllNamespaces()), Objects.toString(cacheB.getAllNamespaces()));
32+
public void testOutputIsReproducibleWhenInputOrderingChanges() {
33+
final PluginIndex indexA = new PluginIndex();
34+
createNamespace(indexA, "one", List.of("bravo", "alpha", "charlie"));
35+
createNamespace(indexA, "two", List.of("alpha", "charlie", "bravo"));
36+
assertEquals(6, indexA.size());
37+
final PluginIndex indexB = new PluginIndex();
38+
createNamespace(indexB, "two", List.of("bravo", "alpha", "charlie"));
39+
createNamespace(indexB, "one", List.of("alpha", "charlie", "bravo"));
40+
assertEquals(6, indexB.size());
41+
assertIterableEquals(indexA, indexB);
4942
}
5043

51-
private void createCategory(final PluginCache cache, final String categoryName, final List<String> entryNames) {
52-
final Map<String, PluginEntry> category = cache.getNamespace(categoryName);
44+
private void createNamespace(final PluginIndex index, final String namespace, final List<String> entryNames) {
5345
for (String entryName : entryNames) {
5446
final PluginEntry entry = PluginEntry.builder()
5547
.setKey(entryName)
5648
.setName(entryName)
5749
.setClassName("com.example.Plugin")
58-
.setNamespace(categoryName)
50+
.setNamespace(namespace)
5951
.get();
60-
category.put(entryName, entry);
52+
index.add(entry);
6153
}
6254
}
6355
}

log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/model/PluginCache.java

Lines changed: 0 additions & 91 deletions
This file was deleted.
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to you under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.logging.log4j.plugins.model;
18+
19+
import java.util.AbstractCollection;
20+
import java.util.Collection;
21+
import java.util.Iterator;
22+
import java.util.Map;
23+
import java.util.TreeMap;
24+
import java.util.function.Consumer;
25+
import org.jspecify.annotations.NullMarked;
26+
27+
@NullMarked
28+
public class PluginIndex extends AbstractCollection<PluginEntry> {
29+
private final Map<String, Map<String, PluginEntry>> index = new TreeMap<>();
30+
31+
@Override
32+
public void forEach(Consumer<? super PluginEntry> action) {
33+
for (var namespace : index.values()) {
34+
for (var pluginEntry : namespace.values()) {
35+
action.accept(pluginEntry);
36+
}
37+
}
38+
}
39+
40+
@Override
41+
public Iterator<PluginEntry> iterator() {
42+
return index.values().stream()
43+
.map(Map::values)
44+
.flatMap(Collection::stream)
45+
.iterator();
46+
}
47+
48+
@Override
49+
public int size() {
50+
return index.values().stream().mapToInt(Map::size).sum();
51+
}
52+
53+
@Override
54+
public boolean add(PluginEntry entry) {
55+
return getOrCreateNamespace(entry.namespace()).putIfAbsent(entry.key(), entry) == null;
56+
}
57+
58+
@Override
59+
public boolean isEmpty() {
60+
return index.isEmpty();
61+
}
62+
63+
@Override
64+
public boolean contains(Object o) {
65+
return o instanceof PluginEntry entry
66+
&& index.containsKey(entry.namespace())
67+
&& index.get(entry.namespace()).containsKey(entry.key())
68+
&& index.get(entry.namespace()).get(entry.key()).equals(entry);
69+
}
70+
71+
@Override
72+
public void clear() {
73+
index.clear();
74+
}
75+
76+
private Map<String, PluginEntry> getOrCreateNamespace(String namespace) {
77+
return index.computeIfAbsent(namespace, ignored -> new TreeMap<>());
78+
}
79+
}

log4j-plugins/src/main/java/org/apache/logging/log4j/plugins/model/PluginRegistry.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020

2121
import aQute.bnd.annotation.Cardinality;
2222
import aQute.bnd.annotation.spi.ServiceConsumer;
23+
import java.io.BufferedInputStream;
24+
import java.io.DataInputStream;
2325
import java.io.IOException;
2426
import java.net.URL;
2527
import java.text.DecimalFormat;
@@ -101,24 +103,44 @@ private void loadPlugins(final ClassLoader classLoader, final Namespaces namespa
101103

102104
private Namespaces decodeCacheFiles(final ClassLoader classLoader) {
103105
final long startTime = System.nanoTime();
104-
final PluginCache cache = new PluginCache();
106+
final PluginIndex index = new PluginIndex();
105107
try {
106108
final Enumeration<URL> resources = classLoader.getResources(PLUGIN_CACHE_FILE);
107109
if (resources == null) {
108110
LOGGER.info("Plugin preloads not available from class loader {}", classLoader);
109111
} else {
110-
cache.loadCacheFiles(resources);
112+
while (resources.hasMoreElements()) {
113+
final URL url = resources.nextElement();
114+
try (final DataInputStream in = new DataInputStream(new BufferedInputStream(url.openStream()))) {
115+
final int count = in.readInt();
116+
for (int i = 0; i < count; i++) {
117+
final var builder = PluginEntry.builder().setNamespace(in.readUTF());
118+
final int entries = in.readInt();
119+
for (int j = 0; j < entries; j++) {
120+
// Must always read all parts of the entry, even if not adding, so that the stream
121+
// progresses
122+
final var entry = builder.setKey(in.readUTF())
123+
.setClassName(in.readUTF())
124+
.setName(in.readUTF())
125+
.setPrintable(in.readBoolean())
126+
.setDeferChildren(in.readBoolean())
127+
.get();
128+
index.add(entry);
129+
}
130+
}
131+
}
132+
}
111133
}
112134
} catch (final IOException ioe) {
113135
LOGGER.warn("Unable to preload plugins", ioe);
114136
}
115137
final Namespaces namespaces = new Namespaces();
116138
final AtomicInteger pluginCount = new AtomicInteger();
117-
cache.getAllNamespaces().forEach((key, outer) -> outer.values().forEach(entry -> {
139+
index.forEach(entry -> {
118140
final PluginType<?> type = new PluginType<>(entry, classLoader);
119141
namespaces.add(type);
120142
pluginCount.incrementAndGet();
121-
}));
143+
});
122144
reportLoadTime(classLoader, startTime, pluginCount);
123145
return namespaces;
124146
}

0 commit comments

Comments
 (0)