View Javadoc
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.audit.request;
18  
19  import java.lang.annotation.Annotation;
20  import java.lang.reflect.Field;
21  import java.util.HashMap;
22  import java.util.Map;
23  import java.util.function.Supplier;
24  
25  import org.apache.logging.log4j.audit.annotation.Chained;
26  import org.apache.logging.log4j.audit.annotation.ChainedSupplier;
27  import org.apache.logging.log4j.audit.annotation.ClientServer;
28  import org.apache.logging.log4j.audit.annotation.HeaderPrefix;
29  import org.apache.logging.log4j.audit.annotation.Local;
30  
31  public class RequestContextMappings {
32  
33      private static final String DEFAULT_HEADER_PREFIX = "request-context-";
34      private final Map<String, RequestContextMapping> mappings = new HashMap<>();
35      private final String headerPrefix;
36  
37      public RequestContextMappings(String fqcn) {
38          this(getClass(fqcn));
39      }
40  
41      private static Class<?> getClass(String fqcn) {
42          if (fqcn == null) {
43              throw new IllegalArgumentException("RequestContext class name cannot be null");
44          }
45          try {
46              return Class.forName(fqcn);
47          } catch (ClassNotFoundException ex) {
48              throw new IllegalArgumentException("Invalid RequestContext class name", ex);
49          }
50      }
51  
52      public RequestContextMappings(Class<?> clazz) {
53          if (clazz == null) {
54              throw new IllegalArgumentException("A RequestContext class must be provided");
55          }
56          Annotation annotation = clazz.getAnnotation(HeaderPrefix.class);
57          this.headerPrefix = annotation != null ? ((HeaderPrefix) annotation).value().toLowerCase() : DEFAULT_HEADER_PREFIX;
58          Field[] fields = clazz.getDeclaredFields();
59          for (Field field : fields) {
60              if (field.getType().equals(String.class)) {
61                  String fieldName;
62                  try {
63                      fieldName = (String) field.get(null);
64                  } catch (IllegalAccessException ex) {
65                      continue;
66                  }
67                  if (fieldName == null) {
68                      continue;
69                  }
70                  annotation = field.getAnnotation(ClientServer.class);
71                  if (annotation != null) {
72                      mappings.put(fieldName.toLowerCase(), new ClientServerMapping(fieldName));
73                      continue;
74                  }
75  
76                  annotation = field.getAnnotation(Local.class);
77                  if (annotation != null) {
78                      mappings.put(fieldName.toLowerCase(), new LocalMapping(fieldName));
79                  }
80              } else if (field.getType().equals(Supplier.class)) {
81                  annotation = field.getAnnotation(Chained.class);
82                  if (annotation != null) {
83                      Chained chained = (Chained) annotation;
84                      try {
85                          @SuppressWarnings("unchecked")
86                          Supplier<String> supplier = (Supplier<String>) field.get(null);
87                          mappings.put(chained.fieldName().toLowerCase(),
88                                  new ChainedMapping(chained.fieldName(), chained.chainedFieldName(), supplier));
89                      } catch (IllegalAccessException ex) {
90                          throw new IllegalArgumentException("Unable to retrieve Supplier for chained field " + chained.fieldName());
91                      }
92                  }
93              }
94          }
95          mappings.entrySet().removeIf(a -> validateChained(a.getValue()));
96      }
97  
98      public RequestContextMapping getMapping(String name) {
99          return mappings.get(name.toLowerCase());
100     }
101 
102     public RequestContextMapping getMappingByHeader(String header) {
103         String hdr = header.toLowerCase();
104         if (hdr.startsWith(headerPrefix)) {
105             return mappings.get(hdr.substring(headerPrefix.length()));
106         }
107         return null;
108     }
109 
110     public String getHeaderPrefix() {
111         return headerPrefix;
112     }
113 
114     private boolean validateChained(RequestContextMapping mapping) {
115         return mapping.getScope() == Scope.CHAIN && !mappings.containsKey(mapping.getChainKey().toLowerCase());
116     }
117 }