1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.audit.generator;
18
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Optional;
23 import java.util.Set;
24
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27 import org.apache.logging.log4j.audit.util.NamingUtils;
28 import org.apache.logging.log4j.catalog.api.Attribute;
29 import org.apache.logging.log4j.catalog.api.CatalogData;
30 import org.apache.logging.log4j.catalog.api.CatalogReader;
31 import org.apache.logging.log4j.catalog.api.Constraint;
32 import org.apache.logging.log4j.catalog.api.ConstraintType;
33 import org.apache.logging.log4j.catalog.api.Event;
34 import org.apache.logging.log4j.catalog.api.EventAttribute;
35 import org.springframework.beans.factory.annotation.Autowired;
36 import org.springframework.beans.factory.annotation.Value;
37 import org.springframework.stereotype.Component;
38
39 @Component
40 public class InterfacesGenerator {
41
42 private static final Logger LOGGER = LogManager.getLogger(InterfacesGenerator.class);
43
44 private static final String CONSTRAINT_IMPORT = "org.apache.logging.log4j.audit.annotation.Constraint";
45 private static final String REQUIRED_IMPORT = "org.apache.logging.log4j.audit.annotation.Required";
46 private static final String CONSTRAINTS_ATTR = ", constraints={";
47 private static final String CONSTRAINT = "@Constraint(constraintType=\"%s\", constraintValue=\"%s\")";
48 private static final String KEY = "key=\"";
49 private static final String REQUIRED_ATTR = "required=true";
50 private static final String REQUIRED = "@Required";
51
52 private static final String REQUEST_CONTEXT_IMPORT = "org.apache.logging.log4j.audit.annotation.RequestContext";
53 private static final String PARENT_IMPORT = "org.apache.logging.log4j.audit.AuditEvent";
54 private static final String MAX_LENGTH_IMPORT = "org.apache.logging.log4j.audit.annotation.MaxLength";
55 private static final String REQCTX_ANN = "@RequestContext(";
56
57 private static final String PARENT_CLASS = "AuditEvent";
58
59 private static final String REQCTX = "ReqCtx_";
60
61 private static final String EVENT_ID = "eventID";
62
63 private static final String EVENT_TYPE = "eventType";
64
65 private static final String TIMESTAMP = "timeStamp";
66
67 private static final String CONTEXT = "context";
68
69 @Autowired
70 private CatalogReader catalogReader;
71
72 @Value("${packageName:org.apache.logging.log4j.audit.event}")
73 private String packageName;
74
75 @Value("${outputDirectory:target/generated-sources/log4j-audit}")
76 private String outputDirectory;
77
78 @Value("${maxKeyLength:32}")
79 private int maxKeyLength;
80
81 @Value("${enterpriseId:18060}")
82 private int enterpriseId;
83
84 @Value("${verbose:false}")
85 private boolean verbose;
86
87 public CatalogReader getCatalogReader() {
88 return catalogReader;
89 }
90
91 public void setCatalogReader(CatalogReader catalogReader) {
92 this.catalogReader = catalogReader;
93 }
94
95 public String getPackageName() {
96 return packageName;
97 }
98
99 public void setPackageName(String packageName) {
100 this.packageName = packageName;
101 }
102
103 public String getOutputDirectory() {
104 return outputDirectory;
105 }
106
107 public void setOutputDirectory(String outputDirectory) {
108 this.outputDirectory = outputDirectory;
109 }
110
111 public void setMaxKeyLength(int maxKeyLength) {
112 this.maxKeyLength = maxKeyLength;
113 }
114
115 public void setEnterpriseId(int enterpriseId) {
116 this.enterpriseId = enterpriseId;
117 }
118
119 public void setVerbose(boolean verbose) {
120 this.verbose = verbose;
121 }
122
123 public void generateSource() throws Exception {
124 boolean errors = false;
125 CatalogData catalogData = catalogReader.read();
126 if (catalogData != null) {
127 List<Event> events = catalogData.getEvents();
128 Map<String, Attribute> requestContextAttrs = new HashMap<>();
129 Map<String, Boolean> requestContextIsRequired = new HashMap<>();
130 Map<String, Attribute> attributes = catalogReader.getAttributes();
131 Map<String, String> importedTypes = new HashMap<>();
132 boolean anyConstraints = false;
133 for (Attribute attribute : attributes.values()) {
134 if (attribute.isRequestContext()) {
135 String name = attribute.getName();
136 if (name.startsWith(REQCTX)) {
137 name = name.substring(REQCTX.length());
138 }
139 requestContextAttrs.put(name, attribute);
140 requestContextIsRequired.put(name, attribute.isRequired());
141 }
142 }
143 for (Event event : events) {
144 String maxLen = Integer.toString(enterpriseId);
145 int maxNameLength = maxKeyLength - maxLen.length() - 1;
146 if (event.getName().length() > maxNameLength) {
147 LOGGER.error("{} exceeds maximum length of {} for an event name", event.getName(), maxNameLength);
148 errors = true;
149 continue;
150 }
151 ClassGenerator classGenerator = new ClassGenerator(
152 NamingUtils.getClassName(event.getName()), outputDirectory);
153 classGenerator.setClass(false);
154 classGenerator.setPackageName(packageName);
155 classGenerator.setParentClassName(PARENT_CLASS);
156 classGenerator.setJavadocComment(event.getDescription());
157 classGenerator.setVerbose(verbose);
158 Set<String> imports = classGenerator.getImports();
159 imports.add(PARENT_IMPORT);
160 StringBuilder annotations = new StringBuilder();
161 imports.add(MAX_LENGTH_IMPORT);
162 annotations.append("@MaxLength(").append(maxKeyLength).append(")");
163
164 List<EventAttribute> eventAttributes = event.getAttributes();
165 boolean anyRequired = false;
166 if (eventAttributes != null) {
167 for (EventAttribute eventAttribute : eventAttributes) {
168 Attribute attribute = attributes.get(eventAttribute.getName());
169 if (attribute == null) {
170 LOGGER.error("Unable to locate attribute name {}", eventAttribute.getName());
171 errors = true;
172 continue;
173 }
174 if (attribute.isRequestContext() && attribute.isRequired()) {
175 String name = eventAttribute.getName();
176 if (name.startsWith(REQCTX)) {
177 name = name.substring(REQCTX.length());
178 }
179 requestContextIsRequired.put(name, Boolean.TRUE);
180 continue;
181 }
182 String name = attribute.getName();
183
184 if (EVENT_ID.equals(name) || EVENT_TYPE.equals(name) || TIMESTAMP.equals(name)) {
185 continue;
186 }
187
188 if (name.indexOf('.') != -1) {
189 name = name.replaceAll("\\.", "");
190 }
191
192 if (name.indexOf('/') != -1) {
193 name = name.replaceAll("/", "");
194 }
195 if (name.length() > maxKeyLength) {
196 LOGGER.error("{} exceeds maximum length of {} for an attribute name", name, maxKeyLength);
197 errors = true;
198 continue;
199 }
200
201 String type = attribute.getDataType().getTypeName();
202
203 MethodDefinition definition = new MethodDefinition("void",
204 NamingUtils.getMutatorName(name));
205 if (!attribute.isRequestContext() && attribute.getDataType().getImportClass() != null) {
206 if (!importedTypes.containsKey(attribute.getDataType().getTypeName())) {
207 importedTypes.put(attribute.getDataType().getTypeName(), attribute.getDataType().getImportClass());
208 }
209 }
210 definition.addParameter(new Parameter(name, type, attribute.getDescription()));
211 definition.setInterface(true);
212 definition.setVisability("public");
213 definition.setJavadocComments(attribute.getDisplayName()
214 + " : " + attribute.getDescription());
215
216 StringBuilder buffer = new StringBuilder();
217 Set<Constraint> constraints = attribute.getConstraints();
218 boolean first = true;
219 if (attribute.isRequired() || eventAttribute.isRequired()) {
220 anyRequired = true;
221 buffer.append(REQUIRED);
222 first = false;
223 }
224 if (constraints != null && constraints.size() > 0) {
225 anyConstraints = true;
226 for (Constraint constraint : constraints) {
227 if (!first) {
228 buffer.append("\n ");
229 }
230 first = false;
231 appendConstraint(constraint, buffer);
232 }
233 }
234 if (buffer.length() > 0) {
235 definition.setAnnotation(buffer.toString());
236 }
237 classGenerator.addMethod(definition);
238
239 }
240 }
241 if (importedTypes.size() > 0) {
242 for (String className : importedTypes.values()) {
243 imports.add(className);
244 }
245 }
246 if (anyRequired) {
247 imports.add(REQUIRED_IMPORT);
248 }
249 boolean firstReqCtx = true;
250 if (requestContextAttrs.size() > 0) {
251 imports.add(REQUEST_CONTEXT_IMPORT);
252 StringBuilder reqCtx = new StringBuilder();
253 for (Map.Entry<String, Attribute> entry : requestContextAttrs.entrySet()) {
254 if (!firstReqCtx) {
255 reqCtx.append(")\n");
256 }
257 firstReqCtx = false;
258 reqCtx.append(REQCTX_ANN);
259 reqCtx.append(KEY).append(entry.getKey()).append("\"");
260 Attribute attrib = entry.getValue();
261 String name = attrib.getName();
262 if (name.startsWith(REQCTX)) {
263 name = name.substring(REQCTX.length());
264 }
265 Boolean isRequired = null;
266 final String attrName = name;
267 if (event.getAttributes() != null) {
268 Optional<EventAttribute> optional = event.getAttributes().stream().filter(a -> attrName.equals(a.getName())).findFirst();
269 if (optional.isPresent()) {
270 isRequired = optional.get().isRequired();
271 }
272 }
273 if ((isRequired != null && isRequired) ||
274 (isRequired == null && requestContextIsRequired.get(name))) {
275 reqCtx.append(", ").append(REQUIRED_ATTR);
276 }
277 Set<Constraint> constraints = entry.getValue().getConstraints();
278 if (constraints != null && constraints.size() > 0) {
279 anyConstraints = true;
280 reqCtx.append(CONSTRAINTS_ATTR);
281 boolean first = true;
282 for (Constraint constraint : constraints) {
283 if (!first) {
284 reqCtx.append(", ");
285 }
286 first = false;
287 appendConstraint(constraint, reqCtx);
288 }
289 reqCtx.append("}");
290
291 }
292 }
293 reqCtx.append(")");
294 if (annotations.length() > 0) {
295 annotations.append("\n");
296 }
297 annotations.append(reqCtx.toString());
298 }
299 if (anyConstraints) {
300 imports.add(CONSTRAINT_IMPORT);
301 }
302 if (annotations.length() > 0) {
303 classGenerator.setAnnotations(annotations.toString());
304 }
305 classGenerator.generate();
306 }
307 }
308 if (errors) {
309 throw new IllegalStateException("Errors were encountered during code generation");
310 }
311 }
312
313 void appendConstraint(Constraint constraint, StringBuilder buffer) {
314 ConstraintType type = constraint.getConstraintType();
315
316
317 buffer.append(String.format(CONSTRAINT, type.getName(), constraint.getValue().replace("\\", "\\\\")));
318 }
319 }