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.catalog;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  import java.util.concurrent.ConcurrentHashMap;
24  
25  import com.fasterxml.jackson.core.JsonFactory;
26  import com.fasterxml.jackson.core.JsonParser;
27  import com.fasterxml.jackson.databind.ObjectMapper;
28  
29  import org.apache.logging.log4j.LogManager;
30  import org.apache.logging.log4j.Logger;
31  import org.apache.logging.log4j.audit.exception.AuditException;
32  import org.apache.logging.log4j.audit.util.NamingUtils;
33  import org.apache.logging.log4j.catalog.api.Attribute;
34  import org.apache.logging.log4j.catalog.api.CatalogData;
35  import org.apache.logging.log4j.catalog.api.Event;
36  import org.apache.logging.log4j.catalog.api.CatalogReader;
37  import org.apache.logging.log4j.catalog.api.EventAttribute;
38  
39  import static java.util.Collections.emptyList;
40  import static org.apache.logging.log4j.catalog.api.constant.Constants.DEFAULT_CATALOG;
41  
42  /**
43   *
44   */
45  public class CatalogManagerImpl implements CatalogManager {
46  
47      private static Logger logger = LogManager.getLogger(CatalogManagerImpl.class);
48  
49      private volatile Map<String, Map<String, CatalogInfo>> infoMap;
50  
51      private Map<String, Attribute> requestContextAttributes = new HashMap<>();
52  
53      protected final Map<String, Map<String, Attribute>> attributeMap = new ConcurrentHashMap<>();
54  
55      private static final String REQCTX = "ReqCtx_";
56  
57      protected CatalogData catalogData;
58  
59      public CatalogManagerImpl(CatalogReader catalogReader) {
60          try {
61              infoMap = initializeData(catalogReader);
62          } catch (Exception ex) {
63              throw new AuditException("Unable to initialize catalog data", ex);
64          }
65      }
66  
67      protected Map<String, Map<String, CatalogInfo>> getInfoMap() {
68          return infoMap;
69      }
70  
71      @Override
72      public Event getEvent(String eventName, String catalogId) {
73          CatalogInfo info = getCatalogInfo(eventName, catalogId);
74          return info != null ? info.event : null;
75      }
76  
77      @Override
78      public List<String> getRequiredContextAttributes(String eventName, String catalogId) {
79          Map<String, CatalogInfo> catalogMap = infoMap.get(catalogId == null ? DEFAULT_CATALOG : catalogId);
80          return catalogMap != null ? catalogMap.get(eventName).requiredContextAttributes : null;
81      }
82  
83      @Override
84      public Map<String, Attribute> getAttributes(String eventName, String catalogId) {
85          Event event = getEvent(eventName, catalogId);
86          if (event == null) {
87              logger.warn("The event named {} could not be found in catalog {}", eventName, catalogId);
88              return null;
89          }
90          List<EventAttribute> eventAttributes = event.getAttributes() == null ? emptyList() : event.getAttributes();
91          Map<String, Attribute> attributes = new HashMap<>(eventAttributes.size());
92          for (EventAttribute eventAttribute : eventAttributes) {
93              Attribute attr = getAttribute(eventAttribute.getName(), event.getCatalogId());
94              if (attr != null) {
95                  attributes.put(attr.getName(), attr);
96              }
97          }
98          return attributes;
99      }
100 
101     @Override
102     public List<String> getAttributeNames(String eventName, String catalogId) {
103         return infoMap.get(catalogId == null ? DEFAULT_CATALOG : catalogId).get(eventName).attributeNames;
104     }
105 
106     @Override
107     public Attribute getAttribute(String name) {
108         Map<String, Attribute> attrMap = attributeMap.get(DEFAULT_CATALOG);
109         return attrMap != null ? attrMap.get(name) : null;
110     }
111 
112     public Attribute getAttribute(String name, String catalogId) {
113         Map<String, Attribute> attrMap = attributeMap.get(catalogId);
114         if (attrMap == null || !attrMap.containsKey(name)) {
115             attrMap = attributeMap.get(DEFAULT_CATALOG);
116         }
117         return attrMap != null ? attrMap.get(name) : null;
118     }
119 
120     @Override
121     public Map<String, Attribute> getRequestContextAttributes() {
122         return requestContextAttributes;
123     }
124 
125     private CatalogInfo getCatalogInfo(String eventName, String catalogId) {
126         Map<String, CatalogInfo> defaultCatalog = infoMap.get(DEFAULT_CATALOG);
127         Map<String, CatalogInfo> catalog = catalogId != null ? infoMap.get(catalogId) : null;
128         return catalog != null && catalog.containsKey(eventName) ? catalog.get(eventName) :
129                 defaultCatalog.get(eventName);
130     }
131 
132     private Map<String, Map<String, CatalogInfo>> initializeData(CatalogReader catalogReader) throws Exception {
133         JsonFactory factory = new JsonFactory();
134         factory.enable(JsonParser.Feature.ALLOW_COMMENTS);
135         ObjectMapper mapper = new ObjectMapper(factory);
136 
137         String catalog = catalogReader.readCatalog();
138         catalogData = mapper.readValue(catalog, CatalogData.class);
139 
140         if (catalogData.getAttributes() != null) {
141             for (Attribute attr : catalogData.getAttributes()) {
142                 if (attr.isRequestContext()) {
143                     requestContextAttributes.put(attr.getName(), attr);
144                 }
145                 Map<String, Attribute> attrMap = attributeMap.get(attr.getCatalogId());
146                 if (attrMap == null) {
147                     attrMap = new HashMap<>();
148                     attributeMap.put(attr.getCatalogId(), attrMap);
149                 }
150                 attrMap.put(attr.getName(), attr);
151             }
152         }
153 
154         Map<String, Map<String, CatalogInfo>> map = new HashMap<>();
155         map.put(DEFAULT_CATALOG, new HashMap<>());
156         for (Event event : catalogData.getEvents()) {
157             addEntry(map, event);
158         }
159         return map;
160     }
161 
162     protected void addEntry(Map<String, Map<String, CatalogInfo>> map, Event event) {
163         CatalogInfo info = new CatalogInfo();
164         info.event = event;
165         String catalogId = event.getCatalogId();
166         if (catalogId != null && catalogId.length() > 0 && !map.containsKey(catalogId)) {
167             map.put(catalogId, new HashMap<>());
168         }
169         List<String> required = new ArrayList<>();
170         List<String> names = new ArrayList<>();
171         info.attributes = new HashMap<>(names.size());
172         if (event.getAttributes() != null) {
173             for (EventAttribute eventAttribute : event.getAttributes()) {
174                 String name = eventAttribute.getName();
175                 Attribute attribute = getAttribute(name, event.getCatalogId());
176                 if (attribute != null) {
177                     info.attributes.put(name, attribute);
178                     if (name.indexOf('.') != -1) {
179                         name = name.replaceAll("\\.", "");
180                     }
181                     if (name.indexOf('/') != -1) {
182                         name = name.replaceAll("/", "");
183                     }
184                     if (attribute.isRequestContext()) {
185                         if (attribute.isRequired()) {
186                             if (name.startsWith(REQCTX)) {
187                                 name = name.substring(REQCTX.length());
188                             }
189                             required.add(name);
190                         }
191                     } else {
192                         names.add(name);
193                     }
194                 } else {
195                     throw new IllegalStateException("Attribute " + name + " is not defined");
196                 }
197             }
198         }
199         info.requiredContextAttributes = required;
200         info.attributeNames = names;
201         Map<String, CatalogInfo> catalogMap = catalogId == null ?
202                 map.get(DEFAULT_CATALOG) : map.get(catalogId);
203         catalogMap.put(NamingUtils.getFieldName(event.getName()), info);
204     }
205 
206     protected class CatalogInfo {
207         private Event event;
208 
209         private List<String> requiredContextAttributes;
210 
211         private List<String> attributeNames;
212 
213         private Map<String, Attribute> attributes;
214     }
215 }