1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.logging.log4j.core.config.plugins.processor;
19
20 import java.io.IOException;
21 import java.io.OutputStream;
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.Locale;
26 import java.util.Map;
27 import java.util.Objects;
28 import java.util.Set;
29 import javax.annotation.processing.AbstractProcessor;
30 import javax.annotation.processing.Messager;
31 import javax.annotation.processing.RoundEnvironment;
32 import javax.annotation.processing.SupportedAnnotationTypes;
33 import javax.lang.model.SourceVersion;
34 import javax.lang.model.element.Element;
35 import javax.lang.model.element.ElementVisitor;
36 import javax.lang.model.element.TypeElement;
37 import javax.lang.model.util.Elements;
38 import javax.lang.model.util.SimpleElementVisitor7;
39 import javax.tools.Diagnostic.Kind;
40 import javax.tools.FileObject;
41 import javax.tools.StandardLocation;
42
43 import org.apache.logging.log4j.core.config.plugins.Plugin;
44 import org.apache.logging.log4j.core.config.plugins.PluginAliases;
45 import org.apache.logging.log4j.util.Strings;
46
47
48
49
50 @SupportedAnnotationTypes("org.apache.logging.log4j.core.config.plugins.*")
51 public class PluginProcessor extends AbstractProcessor {
52
53
54
55
56
57
58
59 public static final String PLUGIN_CACHE_FILE =
60 "META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat";
61
62 private final PluginCache pluginCache = new PluginCache();
63
64 @Override
65 public SourceVersion getSupportedSourceVersion() {
66 return SourceVersion.latest();
67 }
68
69 @Override
70 public boolean process(final Set<? extends TypeElement> annotations, final RoundEnvironment roundEnv) {
71 Messager messager = processingEnv.getMessager();
72 messager.printMessage(Kind.NOTE, "Processing Log4j annotations");
73 try {
74 final Set<? extends Element> elements = roundEnv.getElementsAnnotatedWith(Plugin.class);
75 if (elements.isEmpty()) {
76 messager.printMessage(Kind.NOTE, "No elements to process");
77 return false;
78 }
79 collectPlugins(elements);
80 writeCacheFile(elements.toArray(new Element[elements.size()]));
81 messager.printMessage(Kind.NOTE, "Annotations processed");
82 return true;
83 } catch (final IOException e) {
84 e.printStackTrace();
85 error(e.getMessage());
86 return false;
87 } catch (final Exception ex) {
88 ex.printStackTrace();
89 error(ex.getMessage());
90 return false;
91 }
92 }
93
94 private void error(final CharSequence message) {
95 processingEnv.getMessager().printMessage(Kind.ERROR, message);
96 }
97
98 private void collectPlugins(final Iterable<? extends Element> elements) {
99 final Elements elementUtils = processingEnv.getElementUtils();
100 final ElementVisitor<PluginEntry, Plugin> pluginVisitor = new PluginElementVisitor(elementUtils);
101 final ElementVisitor<Collection<PluginEntry>, Plugin> pluginAliasesVisitor = new PluginAliasesElementVisitor(
102 elementUtils);
103 for (final Element element : elements) {
104 final Plugin plugin = element.getAnnotation(Plugin.class);
105 if (plugin == null) {
106 continue;
107 }
108 final PluginEntry entry = element.accept(pluginVisitor, plugin);
109 final Map<String, PluginEntry> category = pluginCache.getCategory(entry.getCategory());
110 category.put(entry.getKey(), entry);
111 final Collection<PluginEntry> entries = element.accept(pluginAliasesVisitor, plugin);
112 for (final PluginEntry pluginEntry : entries) {
113 category.put(pluginEntry.getKey(), pluginEntry);
114 }
115 }
116 }
117
118 private void writeCacheFile(final Element... elements) throws IOException {
119 final FileObject fileObject = processingEnv.getFiler().createResource(StandardLocation.CLASS_OUTPUT, Strings.EMPTY,
120 PLUGIN_CACHE_FILE, elements);
121 try (final OutputStream out = fileObject.openOutputStream()) {
122 pluginCache.writeCache(out);
123 }
124 }
125
126
127
128
129 private static class PluginElementVisitor extends SimpleElementVisitor7<PluginEntry, Plugin> {
130
131 private final Elements elements;
132
133 private PluginElementVisitor(final Elements elements) {
134 this.elements = elements;
135 }
136
137 @Override
138 public PluginEntry visitType(final TypeElement e, final Plugin plugin) {
139 Objects.requireNonNull(plugin, "Plugin annotation is null.");
140 final PluginEntry entry = new PluginEntry();
141 entry.setKey(plugin.name().toLowerCase(Locale.US));
142 entry.setClassName(elements.getBinaryName(e).toString());
143 entry.setName(Plugin.EMPTY.equals(plugin.elementType()) ? plugin.name() : plugin.elementType());
144 entry.setPrintable(plugin.printObject());
145 entry.setDefer(plugin.deferChildren());
146 entry.setCategory(plugin.category());
147 return entry;
148 }
149 }
150
151
152
153
154 private static class PluginAliasesElementVisitor extends SimpleElementVisitor7<Collection<PluginEntry>, Plugin> {
155
156 private final Elements elements;
157
158 private PluginAliasesElementVisitor(final Elements elements) {
159 super(Collections.<PluginEntry> emptyList());
160 this.elements = elements;
161 }
162
163 @Override
164 public Collection<PluginEntry> visitType(final TypeElement e, final Plugin plugin) {
165 final PluginAliases aliases = e.getAnnotation(PluginAliases.class);
166 if (aliases == null) {
167 return DEFAULT_VALUE;
168 }
169 final Collection<PluginEntry> entries = new ArrayList<>(aliases.value().length);
170 for (final String alias : aliases.value()) {
171 final PluginEntry entry = new PluginEntry();
172 entry.setKey(alias.toLowerCase(Locale.US));
173 entry.setClassName(elements.getBinaryName(e).toString());
174 entry.setName(Plugin.EMPTY.equals(plugin.elementType()) ? alias : plugin.elementType());
175 entry.setPrintable(plugin.printObject());
176 entry.setDefer(plugin.deferChildren());
177 entry.setCategory(plugin.category());
178 entries.add(entry);
179 }
180 return entries;
181 }
182 }
183 }