You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@logging.apache.org by rg...@apache.org on 2018/03/14 14:51:54 UTC

[48/49] logging-log4j-audit git commit: Rename war project to editor

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/CategoryController.java
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/CategoryController.java b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/CategoryController.java
new file mode 100644
index 0000000..578b70c
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/CategoryController.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.catalog.controller;
+
+import javax.annotation.PostConstruct;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.catalog.api.Category;
+import org.apache.logging.log4j.catalog.jpa.model.CategoryModel;
+import org.apache.logging.log4j.catalog.jpa.service.CategoryService;
+import org.apache.logging.log4j.catalog.jpa.converter.CategoryConverter;
+import org.apache.logging.log4j.catalog.jpa.converter.CategoryModelConverter;
+import org.modelmapper.ModelMapper;
+import org.modelmapper.TypeToken;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Catalog Category controller
+ */
+
+@RequestMapping(value = "/api/categories")
+@RestController
+public class CategoryController {
+    private static final Logger LOGGER = LogManager.getLogger();
+
+    private ModelMapper modelMapper = new ModelMapper();
+
+    @Autowired
+    private CategoryService categoryService;
+
+
+    @Autowired
+    private CategoryModelConverter categoryModelConverter;
+
+    @Autowired
+    private CategoryConverter categoryConverter;
+
+    @PostConstruct
+    public void init() {
+        modelMapper.addConverter(categoryModelConverter);
+    }
+
+    @PostMapping(value = "/list")
+    public ResponseEntity<Map<String, Object>> categoryList(@RequestParam(value="jtStartIndex", required=false) Integer startIndex,
+                                                            @RequestParam(value="jtPageSize", required=false) Integer pageSize,
+                                                            @RequestParam(value="jtSorting", required=false) String sorting) {
+        Type listType = new TypeToken<List<Category>>() {}.getType();
+        Map<String, Object> response = new HashMap<>();
+        try {
+            List<Category> categories = null;
+            if (startIndex == null || pageSize == null) {
+                categories = modelMapper.map(categoryService.getCategories(), listType);
+            } else {
+                int startPage = 0;
+                if (startIndex > 0) {
+                    startPage = startIndex / pageSize;
+                }
+                String sortColumn = "name";
+                String sortDirection = "ASC";
+                if (sorting != null) {
+                    String[] sortInfo = sorting.split(" ");
+                    sortColumn = sortInfo[0];
+                    if (sortInfo.length > 0) {
+                        sortDirection = sortInfo[1];
+                    }
+                }
+                categories = modelMapper.map(categoryService.getCategories(startPage, pageSize, sortColumn, sortDirection), listType);
+            }
+            if (categories == null) {
+                categories = new ArrayList<>();
+            }
+            response.put("Result", "OK");
+            response.put("Records", categories);
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/create")
+    public ResponseEntity<Map<String, Object>> createCategory(@RequestBody Category category) {
+        Map<String, Object> response = new HashMap<>();
+        try {
+            CategoryModel model = categoryConverter.convert(category);
+            model = categoryService.saveCategory(model);
+            response.put("Result", "OK");
+            response.put("Records", categoryModelConverter.convert(model));
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/update")
+    public ResponseEntity<Map<String, Object>> updateCategory(@RequestBody Category category) {
+        Map<String, Object> response = new HashMap<>();
+        try {
+            CategoryModel model = categoryConverter.convert(category);
+            model = categoryService.saveCategory(model);
+            response.put("Result", "OK");
+            response.put("Records", categoryModelConverter.convert(model));
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/delete")
+    public ResponseEntity<Map<String, Object>> deleteCategory(@RequestBody Category category) {
+        Map<String, Object> response = new HashMap<>();
+        try {
+            categoryService.deleteCategory(category.getId());
+            response.put("Result", "OK");
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/ConstraintController.java
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/ConstraintController.java b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/ConstraintController.java
new file mode 100644
index 0000000..ef8f290
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/ConstraintController.java
@@ -0,0 +1,102 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.catalog.controller;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.catalog.api.Attribute;
+import org.apache.logging.log4j.catalog.api.Constraint;
+import org.apache.logging.log4j.catalog.jpa.model.ConstraintModel;
+import org.apache.logging.log4j.catalog.jpa.service.ConstraintService;
+import org.modelmapper.ModelMapper;
+import org.modelmapper.TypeToken;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Constraint controller
+ */
+
+@RequestMapping(value = "/api/constraints")
+@RestController
+public class ConstraintController {
+    private static final Logger LOGGER = LogManager.getLogger();
+
+    private ModelMapper modelMapper = new ModelMapper();
+
+    @Autowired
+    private ConstraintService constraintService;
+
+    @PostMapping(value = "/list")
+    public ResponseEntity<Map<String, Object>> attributeList(@RequestParam("attributeId") Long attributeId) {
+        Type listType = new TypeToken<List<Attribute>>() {}.getType();
+        Map<String, Object> response = new HashMap<>();
+        try {
+            List<Attribute> attributes = modelMapper.map(constraintService.getConstraints(), listType);
+            if (attributes == null) {
+                attributes = new ArrayList<>();
+            }
+            response.put("Result", "OK");
+            response.put("Records", attributes);
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @GetMapping(value = "/types")
+    public ResponseEntity<Set<String>> getConstraintTypes() {
+        return new ResponseEntity<>(constraintService.getConstraintTypes(), HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/constraint")
+    public ResponseEntity<Long> addConstraint(@RequestBody Constraint constraint) {
+        ConstraintModel model = modelMapper.map(constraint, ConstraintModel.class);
+        model = constraintService.saveConstraint(model);
+        return new ResponseEntity<>(model.getId(), HttpStatus.CREATED);
+    }
+
+    @PutMapping(value = "/constraint/{id}")
+    public ResponseEntity<Long> updateConstraint(@RequestParam Long id, @RequestBody Constraint constraint) {
+        ConstraintModel model = modelMapper.map(constraint, ConstraintModel.class);
+        model.setId(id);
+        model = constraintService.saveConstraint(model);
+        return new ResponseEntity<>(model.getId(), HttpStatus.OK);
+    }
+
+    @DeleteMapping(value = "/constraint/{id}")
+    public ResponseEntity<?> deleteConstraint(@RequestParam Long id) {
+        constraintService.deleteConstraint(id);
+        return new ResponseEntity<>(HttpStatus.NO_CONTENT);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/EventController.java
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/EventController.java b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/EventController.java
new file mode 100644
index 0000000..0dd8295
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/EventController.java
@@ -0,0 +1,176 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.catalog.controller;
+
+import javax.annotation.PostConstruct;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.catalog.api.Event;
+import org.apache.logging.log4j.catalog.jpa.model.EventModel;
+import org.apache.logging.log4j.catalog.jpa.service.EventService;
+import org.apache.logging.log4j.catalog.jpa.converter.EventConverter;
+import org.apache.logging.log4j.catalog.jpa.converter.EventModelConverter;
+import org.modelmapper.ModelMapper;
+import org.modelmapper.TypeToken;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * Catalog Product controller
+ */
+
+@RequestMapping(value = "/api/events")
+@RestController
+public class EventController {
+    private static final Logger LOGGER = LogManager.getLogger();
+
+    private ModelMapper modelMapper = new ModelMapper();
+
+    @Autowired
+    private EventService eventService;
+
+    @Autowired
+    private EventModelConverter eventModelConverter;
+
+    @Autowired
+    private EventConverter eventConverter;
+
+    @PostConstruct
+    public void init() {
+        modelMapper.addConverter(eventModelConverter);
+    }
+
+    @PostMapping(value = "/list")
+    public ResponseEntity<Map<String, Object>> eventList(@RequestParam(value="jtStartIndex", required=false) Integer startIndex,
+                                                         @RequestParam(value="jtPageSize", required=false) Integer pageSize,
+                                                         @RequestParam(value="jtSorting", required=false) String sorting) {
+        Type listType = new TypeToken<List<Event>>() {}.getType();
+        Map<String, Object> response = new HashMap<>();
+        try {
+            List<Event> events = null;
+            if (startIndex == null || pageSize == null) {
+                events = modelMapper.map(eventService.getEvents(), listType);
+            } else {
+                int startPage = 0;
+                if (startIndex > 0) {
+                    startPage = startIndex / pageSize;
+                }
+                String sortColumn = "name";
+                String sortDirection = "ASC";
+                if (sorting != null) {
+                    String[] sortInfo = sorting.split(" ");
+                    sortColumn = sortInfo[0];
+                    if (sortInfo.length > 0) {
+                        sortDirection = sortInfo[1];
+                    }
+                }
+                events = modelMapper.map(eventService.getEvents(startPage, pageSize, sortColumn, sortDirection), listType);
+            }
+            if (events == null) {
+                events = new ArrayList<>();
+            }
+            response.put("Result", "OK");
+            response.put("Records", events);
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+            response.put("Message", ex.getMessage());
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/create")
+    public ResponseEntity<Map<String, Object>> createEvent(@RequestBody Event event) {
+        Map<String, Object> response = new HashMap<>();
+        try {
+            EventModel model = eventConverter.convert(event);
+            event = eventModelConverter.convert(eventService.saveEvent(model));
+            response.put("Result", "OK");
+            response.put("Records", event);
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+            response.put("Message", ex.getMessage());
+            LOGGER.warn("Unable to create event named {}", event.getName(), ex);
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/update")
+    public ResponseEntity<Map<String, Object>> updateEvent(@RequestBody Event event) {
+        Map<String, Object> response = new HashMap<>();
+        try {
+            EventModel model = eventConverter.convert(event);
+            event = eventModelConverter.convert(eventService.saveEvent(model));
+            response.put("Result", "OK");
+            response.put("Records", event);
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+            response.put("Message", ex.getMessage());
+            LOGGER.warn("Unable to update event named {}", event.getName(), ex);
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/delete")
+    public ResponseEntity<Map<String, Object>> deleteEvent(@RequestBody Long eventId) {
+        Map<String, Object> response = new HashMap<>();
+        try {
+            eventService.deleteEvent(eventId);
+            response.put("Result", "OK");
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+            response.put("Message", ex.getMessage());
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/attributes/list")
+    public ResponseEntity<Map<String, Object>> attributeList(@RequestParam("eventId") Long eventId) {
+        Map<String, Object> response = new HashMap<>();
+        try {
+            Optional<EventModel> optional = eventService.getEvent(eventId);
+            if (optional.isPresent()) {
+                Event event = eventModelConverter.convert(optional.get());
+                response.put("Result", "OK");
+                if (event != null && event.getAttributes() != null) {
+                    response.put("Records", event.getAttributes());
+                } else {
+                    response.put("Records", new ArrayList<>());
+                }
+            } else {
+                response.put("Result", "OK");
+                response.put("Records", new ArrayList<>());
+
+            }
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/ProductController.java
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/ProductController.java b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/ProductController.java
new file mode 100644
index 0000000..d566519
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/ProductController.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.catalog.controller;
+
+import javax.annotation.PostConstruct;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.catalog.api.Product;
+import org.apache.logging.log4j.catalog.jpa.model.ProductModel;
+import org.apache.logging.log4j.catalog.jpa.service.ProductService;
+import org.apache.logging.log4j.catalog.jpa.converter.ProductConverter;
+import org.apache.logging.log4j.catalog.jpa.converter.ProductModelConverter;
+import org.modelmapper.ModelMapper;
+import org.modelmapper.TypeToken;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Catalog Product controller
+ */
+
+@RequestMapping(value = "/api/products")
+@RestController
+public class ProductController {
+    private static final Logger LOGGER = LogManager.getLogger();
+
+    private ModelMapper modelMapper = new ModelMapper();
+
+    @Autowired
+    private ProductService productService;
+
+    @Autowired
+    private ProductModelConverter productModelConverter;
+
+    @Autowired
+    private ProductConverter productConverter;
+
+    @PostConstruct
+    public void init() {
+        modelMapper.addConverter(productModelConverter);
+    }
+
+    @PostMapping(value = "/list")
+    public ResponseEntity<Map<String, Object>> productList(@RequestParam(value="jtStartIndex", required=false) Integer startIndex,
+                                                           @RequestParam(value="jtPageSize", required=false) Integer pageSize,
+                                                           @RequestParam(value="jtSorting", required=false) String sorting) {
+        Type listType = new TypeToken<List<Product>>() {}.getType();
+        Map<String, Object> response = new HashMap<>();
+        try {
+            List<Product> products = null;
+            if (startIndex == null || pageSize == null) {
+                products = modelMapper.map(productService.getProducts(), listType);
+            } else {
+                int startPage = 0;
+                if (startIndex > 0) {
+                    startPage = startIndex / pageSize;
+                }
+                String sortColumn = "name";
+                String sortDirection = "ASC";
+                if (sorting != null) {
+                    String[] sortInfo = sorting.split(" ");
+                    sortColumn = sortInfo[0];
+                    if (sortInfo.length > 0) {
+                        sortDirection = sortInfo[1];
+                    }
+                }
+                products = modelMapper.map(productService.getProducts(startPage, pageSize, sortColumn, sortDirection), listType);
+            }
+            if (products == null) {
+                products = new ArrayList<>();
+            }
+            response.put("Result", "OK");
+            response.put("Records", products);
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/create")
+    public ResponseEntity<Map<String, Object>> createProduct(@RequestBody Product product) {
+        Map<String, Object> response = new HashMap<>();
+        try {
+            ProductModel model = productConverter.convert(product);
+            model = productService.saveProduct(model);
+            response.put("Result", "OK");
+            response.put("Records", productModelConverter.convert(model));
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/update")
+    public ResponseEntity<Map<String, Object>> updateProduct(@RequestBody Product product) {
+        Map<String, Object> response = new HashMap<>();
+        try {
+            ProductModel model = productConverter.convert(product);
+            model = productService.saveProduct(model);
+            response.put("Result", "OK");
+            response.put("Records", productModelConverter.convert(model));
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+
+    @PostMapping(value = "/delete")
+    public ResponseEntity<Map<String, Object>> deleteProduct(@RequestBody Product product) {
+        Map<String, Object> response = new HashMap<>();
+        try {
+            productService.deleteProduct(product.getId());
+            response.put("Result", "OK");
+        } catch (Exception ex) {
+            response.put("Result", "FAILURE");
+        }
+        return new ResponseEntity<>(response, HttpStatus.OK);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/RestResponseEntityExceptionHandler.java
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/RestResponseEntityExceptionHandler.java b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/RestResponseEntityExceptionHandler.java
new file mode 100644
index 0000000..b420b16
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/controller/RestResponseEntityExceptionHandler.java
@@ -0,0 +1,55 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.catalog.controller;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.data.rest.webmvc.support.ExceptionMessage;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.web.bind.annotation.ControllerAdvice;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.servlet.mvc.method.annotation.ResponseEntityExceptionHandler;
+
+@ControllerAdvice
+public class RestResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
+    private static final Logger LOGGER = LogManager.getLogger();
+
+    @ExceptionHandler({ Exception.class })
+    @ResponseBody
+    public ResponseEntity<?> handleAnyException(Exception e) {
+        return errorResponse(e, HttpStatus.INTERNAL_SERVER_ERROR);
+    }
+
+    protected ResponseEntity<ExceptionMessage> errorResponse(Throwable throwable,
+                                                             HttpStatus status) {
+        if (null != throwable) {
+            LOGGER.error("error caught: " + throwable.getMessage(), throwable);
+            return response(new ExceptionMessage(throwable), status);
+        } else {
+            LOGGER.error("unknown error caught in RESTController, {}", status);
+            return response(null, status);
+        }
+    }
+
+    protected <T> ResponseEntity<T> response(T body, HttpStatus status) {
+        LOGGER.debug("Responding with a status of {}", status);
+        return new ResponseEntity<>(body, new HttpHeaders(), status);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/interceptor/RequestContextHeaderInterceptor.java
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/interceptor/RequestContextHeaderInterceptor.java b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/interceptor/RequestContextHeaderInterceptor.java
new file mode 100644
index 0000000..520fb1a
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/interceptor/RequestContextHeaderInterceptor.java
@@ -0,0 +1,41 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.catalog.interceptor;
+
+import java.io.IOException;
+
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpRequest;
+import org.springframework.http.client.ClientHttpRequestExecution;
+import org.springframework.http.client.ClientHttpRequestInterceptor;
+import org.springframework.http.client.ClientHttpResponse;
+
+/**
+ * Creates a List of Headers containing the keys and values in the RequestContext that have a mapping indicating
+ * they should be propogated to the service being called.
+ *
+ * This class is designed to be used by Spring as part of the REST Template configuration.
+ *
+ */
+public class RequestContextHeaderInterceptor implements ClientHttpRequestInterceptor {
+
+    @Override
+    public ClientHttpResponse intercept(HttpRequest httpRequest, byte[] body,
+                                        ClientHttpRequestExecution clientHttpRequestExecution) throws IOException {
+        return clientHttpRequestExecution.execute(httpRequest, body);
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/security/LocalAuthorizationInterceptor.java
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/security/LocalAuthorizationInterceptor.java b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/security/LocalAuthorizationInterceptor.java
new file mode 100644
index 0000000..d3521d5
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/security/LocalAuthorizationInterceptor.java
@@ -0,0 +1,54 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.catalog.security;
+
+
+import org.apache.http.HttpStatus;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;
+
+public class LocalAuthorizationInterceptor extends HandlerInterceptorAdapter {
+    private static final Logger LOGGER = LogManager.getLogger();
+    private final String token;
+
+    public LocalAuthorizationInterceptor(String token) {
+        this.token = token;
+    }
+
+    @Override
+    public boolean preHandle(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Object handler) throws Exception {
+        LOGGER.traceEntry();
+        try {
+            if (request.getServletPath().startsWith("/swagger")) {
+                return true;
+            }
+
+            String authHeader = request.getHeader("Authorization");
+            if (authHeader == null || !authHeader.equals(token)) {
+                LOGGER.error("Authorization value of " + authHeader + " does not match expected value of " + token);
+                response.sendError(HttpStatus.SC_UNAUTHORIZED);
+                return false;
+            }
+
+            return true;
+        } finally {
+            LOGGER.traceExit();
+        }
+
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/service/CatalogInitializer.java
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/service/CatalogInitializer.java b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/service/CatalogInitializer.java
new file mode 100644
index 0000000..5e5eb35
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/java/org/apache/logging/log4j/catalog/service/CatalogInitializer.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+package org.apache.logging.log4j.catalog.service;
+
+import javax.annotation.PostConstruct;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.apache.logging.log4j.catalog.api.dao.CatalogDao;
+import org.apache.logging.log4j.catalog.api.Attribute;
+import org.apache.logging.log4j.catalog.api.CatalogData;
+import org.apache.logging.log4j.catalog.api.Category;
+import org.apache.logging.log4j.catalog.api.Event;
+import org.apache.logging.log4j.catalog.api.Product;
+import org.apache.logging.log4j.catalog.jpa.converter.AttributeConverter;
+import org.apache.logging.log4j.catalog.jpa.converter.CategoryConverter;
+import org.apache.logging.log4j.catalog.jpa.converter.EventConverter;
+import org.apache.logging.log4j.catalog.jpa.converter.ProductConverter;
+import org.apache.logging.log4j.catalog.jpa.model.AttributeModel;
+import org.apache.logging.log4j.catalog.jpa.model.CategoryModel;
+import org.apache.logging.log4j.catalog.jpa.model.EventModel;
+import org.apache.logging.log4j.catalog.jpa.model.ProductModel;
+import org.apache.logging.log4j.catalog.jpa.service.AttributeService;
+import org.apache.logging.log4j.catalog.jpa.service.CategoryService;
+import org.apache.logging.log4j.catalog.jpa.service.EventService;
+import org.apache.logging.log4j.catalog.jpa.service.ProductService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Component;
+
+/**
+ *
+ */
+@Component
+public class CatalogInitializer {
+    private static final Logger logger = LogManager.getLogger(CatalogInitializer.class);
+
+    @Autowired
+    AttributeService attributeService;
+
+    @Autowired
+    EventService eventService;
+
+    @Autowired
+    CategoryService categoryService;
+
+    @Autowired
+    ProductService productService;
+
+    @Autowired
+    CatalogDao catalogDao;
+
+    @Autowired
+    AttributeConverter attributeConverter;
+
+    @Autowired
+    EventConverter eventConverter;
+
+    @Autowired
+    CategoryConverter categoryConverter;
+
+    @Autowired
+    ProductConverter productConverter;
+
+    @PostConstruct
+    private void initialize() {
+        logger.debug("Performing initialization");
+        CatalogData catalogData = catalogDao.read();
+
+        logger.debug("Loading attributes");
+        List<AttributeModel> attributeModels = new ArrayList<>();
+        for (Attribute attribute : catalogData.getAttributes()) {
+            AttributeModel model = attributeConverter.convert(attribute);
+            attributeService.saveAttribute(model);
+            attributeModels.add(model);
+        }
+        Map<String, EventModel> eventMap = new HashMap<>();
+        logger.debug("Loading events");
+        for (Event event : catalogData.getEvents()) {
+            logger.debug("Processing Event: {}", event);
+            EventModel model = eventConverter.convert(event);
+            logger.debug("Created EventModel: {} ", model);
+            eventMap.put(event.getName(), model);
+            eventService.saveEvent(model);
+        }
+        logger.debug("Loading categories");
+        for (Category category : catalogData.getCategories()) {
+            CategoryModel model = categoryConverter.convert(category);
+            categoryService.saveCategory(model);
+        }
+        logger.debug("loading products");
+        for (Product product : catalogData.getProducts()) {
+            ProductModel model = productConverter.convert(product);
+            productService.saveProduct(model);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/resources/log4j2.xml
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/resources/log4j2.xml b/log4j-catalog/log4j-catalog-editor/src/main/resources/log4j2.xml
new file mode 100644
index 0000000..dbc8075
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/resources/log4j2.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache license, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the license for the specific language governing permissions and
+  ~ limitations under the license.
+  -->
+<Configuration status="ERROR">
+    <properties>
+        <property name="LOG_DIR">${sys:catalina.home:-.}/logs/AuditCatalog</property>
+    </properties>
+    <Appenders>
+        <Console name="Console" target="SYSTEM_OUT">
+            <PatternLayout pattern="%d{ABSOLUTE} %-5level # %class.%method %m%n" />
+        </Console>
+
+        <RollingFile name="log4j" fileName="${LOG_DIR}/log4j.txt" filePattern="${LOG_DIR}/archive/log4j.txt.%d{yyyyMMdd_HH}-%i">
+            <PatternLayout pattern="%d [%t] %-5p %C{1.}.%M:%L - %m%n"/>
+            <Policies>
+                <SizeBasedTriggeringPolicy size="30 MB"/>
+            </Policies>
+            <DefaultRolloverStrategy min="1" max="20"/>
+        </RollingFile>
+    </Appenders>
+    <Loggers>
+        <Logger name="org.apache.logging.log4j.catalog" level="WARN" additivity="false">
+            <AppenderRef ref="log4j"/>
+        </Logger>
+        <Root level="DEBUG">
+            <AppenderRef ref="log4j" />
+        </Root>
+    </Loggers>
+</Configuration>

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/resources/sql/hsql/schema.sql
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/resources/sql/hsql/schema.sql b/log4j-catalog/log4j-catalog-editor/src/main/resources/sql/hsql/schema.sql
new file mode 100644
index 0000000..772d65b
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/resources/sql/hsql/schema.sql
@@ -0,0 +1,121 @@
+/*
+ * Copyright 2001-2005 The Apache Software Foundation.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+CREATE TABLE EVENT_ATTRIBUTE
+(
+  ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
+  CATALOG_ID VARCHAR(64) NOT NULL,
+  NAME VARCHAR(64) NOT NULL,
+  DISPLAY_NAME VARCHAR(64) NOT NULL,
+  DESCRIPTION VARCHAR(1024),
+  DATATYPE VARCHAR(64),
+  INDEXED CHAR,
+  SORTABLE CHAR,
+  REQUIRED CHAR,
+  REQUEST_CONTEXT CHAR
+);
+
+CREATE UNIQUE INDEX ATTRIBUTE_NAME ON EVENT_ATTRIBUTE(NAME);
+
+CREATE TABLE ATTRIBUTE_EXAMPLES
+(
+  ATTRIBUTE_ID BIGINT NOT NULL,
+  EXAMPLE VARCHAR(64),
+  FOREIGN KEY (ATTRIBUTE_ID) REFERENCES EVENT_ATTRIBUTE(ID),
+);
+
+CREATE TABLE ATTRIBUTE_ALIASES
+(
+  ATTRIBUTE_ID BIGINT NOT NULL,
+  ALIAS VARCHAR(64),
+  FOREIGN KEY (ATTRIBUTE_ID) REFERENCES EVENT_ATTRIBUTE(ID),
+);
+
+CREATE TABLE ATTRIBUTE_CONSTRAINT
+(
+  ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
+  ATTRIBUTE_ID BIGINT NOT NULL,
+  CONSTRAINT_TYPE VARCHAR(32) NOT NULL,
+  VALUE VARCHAR(256),
+  FOREIGN KEY (ATTRIBUTE_ID) REFERENCES EVENT_ATTRIBUTE(ID),
+);
+
+CREATE TABLE CATALOG_EVENT
+(
+  ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
+  CATALOG_ID VARCHAR(64) NOT NULL,
+  NAME VARCHAR(64) NOT NULL,
+  DISPLAY_NAME VARCHAR(64) NOT NULL,
+  DESCRIPTION VARCHAR(1024)
+);
+
+CREATE UNIQUE INDEX EVENT_NAME ON CATALOG_EVENT(NAME);
+
+CREATE TABLE EVENT_ALIASES
+(
+  EVENT_ID BIGINT NOT NULL,
+  ALIAS VARCHAR(64) NOT NULL,
+  FOREIGN KEY  (EVENT_ID) REFERENCES CATALOG_EVENT(ID),
+);
+
+CREATE TABLE EVENT_ATTRIBUTES
+(
+  ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
+  EVENT_ID BIGINT NOT NULL,
+  ATTRIBUTE_ID BIGINT NOT NULL,
+  IS_REQUIRED CHAR,
+  FOREIGN KEY (EVENT_ID) REFERENCES CATALOG_EVENT(ID),
+  FOREIGN KEY (ATTRIBUTE_ID) REFERENCES EVENT_ATTRIBUTE(ID)
+);
+
+CREATE TABLE CATALOG_CATEGORY
+(
+  ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
+  CATALOG_ID VARCHAR(64) NOT NULL,
+  NAME VARCHAR(64) NOT NULL,
+  DISPLAY_NAME VARCHAR(64) NOT NULL,
+  DESCRIPTION VARCHAR(1024)
+
+);
+
+CREATE UNIQUE INDEX CATEGORY_NAME ON CATALOG_CATEGORY(NAME);
+
+CREATE TABLE CATEGORY_EVENTS
+(
+  CATEGORY_ID BIGINT NOT NULL,
+  EVENT_ID BIGINT NOT NULL,
+  FOREIGN KEY (CATEGORY_ID) REFERENCES CATALOG_CATEGORY(ID),
+  FOREIGN KEY (EVENT_ID) REFERENCES CATALOG_EVENT(ID)
+);
+
+CREATE TABLE CATALOG_PRODUCT
+(
+  ID BIGINT GENERATED BY DEFAULT AS IDENTITY(START WITH 1, INCREMENT BY 1) PRIMARY KEY,
+  CATALOG_ID VARCHAR(64) NOT NULL,
+  NAME VARCHAR(64) NOT NULL,
+  DISPLAY_NAME VARCHAR(64) NOT NULL,
+  DESCRIPTION VARCHAR(1024)
+
+);
+
+CREATE UNIQUE INDEX PRODUCT_NAME ON CATALOG_PRODUCT(NAME);
+
+CREATE TABLE PRODUCT_EVENTS
+(
+  PRODUCT_ID BIGINT NOT NULL,
+  EVENT_ID BIGINT NOT NULL,
+  FOREIGN KEY (PRODUCT_ID) REFERENCES CATALOG_PRODUCT(ID),
+  FOREIGN KEY (EVENT_ID) REFERENCES CATALOG_EVENT(ID)
+);
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/attributes.html
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/attributes.html b/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/attributes.html
new file mode 100644
index 0000000..af6f007
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/attributes.html
@@ -0,0 +1,44 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache license, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the license for the specific language governing permissions and
+  ~ limitations under the license.
+  -->
+<html>
+<head>
+    <title>Audit Catalog - Attributes</title>
+
+    <!-- Include one of jTable styles. -->
+    <link href="js/jtable.2.4.0/themes/metro/blue/jtable.min.css" rel="stylesheet" type="text/css" />
+    <!-- Include app styles. -->
+    <link href="css/app.css" rel="stylesheet" type="text/css" />
+
+    <!-- Include jTable script file. -->
+    <script src="js/jquery-3.2.1.min.js" type="text/javascript"></script>
+    <script src="js/jquery-ui-1.12.1/jquery-ui.min.js" type="text/javascript"></script>
+    <script src="js/jtable.2.4.0/jquery.jtable.js" type="text/javascript"></script>
+    <!-- Include app script files. -->
+    <script src="js/app.js" type="text/javascript"></script>
+    <script src="js/attributes.js" type="text/javascript"></script>
+
+</head>
+<body>
+
+    <div th:insert="~{template :: hdr}"></div>
+
+    <div id="AttributesTableContainer" class="log4j-table-container"></div>
+
+    <div th:insert="~{template :: ftr}"></div>
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/categories.html
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/categories.html b/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/categories.html
new file mode 100644
index 0000000..7a6baff
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/categories.html
@@ -0,0 +1,43 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache license, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the license for the specific language governing permissions and
+  ~ limitations under the license.
+  -->
+<html>
+<head>
+    <title>Audit Catalog - categories</title>
+
+    <!-- Include one of jTable styles. -->
+    <link href="js/jtable.2.4.0/themes/metro/blue/jtable.min.css" rel="stylesheet" type="text/css" />
+    <!-- Include app styles. -->
+    <link href="css/app.css" rel="stylesheet" type="text/css" />
+
+    <!-- Include jTable script file. -->
+    <script src="js/jquery-3.2.1.min.js" type="text/javascript"></script>
+    <script src="js/jquery-ui-1.12.1/jquery-ui.min.js" type="text/javascript"></script>
+    <script src="js/jtable.2.4.0/jquery.jtable.js" type="text/javascript"></script>
+    <!-- Include app script files. -->
+    <script src="js/app.js" type="text/javascript"></script>
+    <script src="js/categories.js" type="text/javascript"></script>
+
+</head>
+<body>
+
+    <div th:insert="~{template :: hdr}"></div>
+
+    <div id="CategoriesTableContainer" class="log4j-table-container"></div>
+
+    <div th:insert="~{template :: ftr}"></div>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/events.html
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/events.html b/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/events.html
new file mode 100644
index 0000000..c83bf71
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/events.html
@@ -0,0 +1,44 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache license, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the license for the specific language governing permissions and
+  ~ limitations under the license.
+  -->
+<html>
+<head>
+    <title>Audit Catalog - Events</title>
+
+    <!-- Include one of jTable styles. -->
+    <link href="js/jtable.2.4.0/themes/metro/blue/jtable.min.css" rel="stylesheet" type="text/css" />
+    <!-- Include app styles. -->
+    <link href="css/app.css" rel="stylesheet" type="text/css" />
+
+    <!-- Include jTable script file. -->
+    <script src="js/jquery-3.2.1.min.js" type="text/javascript"></script>
+    <script src="js/jquery-ui-1.12.1/jquery-ui.min.js" type="text/javascript"></script>
+    <script src="js/jtable.2.4.0/jquery.jtable.js" type="text/javascript"></script>
+    <!-- Include app script files. -->
+    <script src="js/app.js" type="text/javascript"></script>
+    <script src="js/events.js" type="text/javascript"></script>
+
+</head>
+<body>
+
+    <div th:insert="~{template :: hdr}"></div>
+
+    <div id="EventsTableContainer" class="log4j-table-container"></div>
+
+    <div th:insert="~{template :: ftr}"></div>
+
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/products.html
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/products.html b/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/products.html
new file mode 100644
index 0000000..e20cf2d
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/products.html
@@ -0,0 +1,43 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache license, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the license for the specific language governing permissions and
+  ~ limitations under the license.
+  -->
+<html>
+<head>
+    <title>Audit Catalog - Products</title>
+
+    <!-- Include one of jTable styles. -->
+    <link href="js/jtable.2.4.0/themes/metro/blue/jtable.min.css" rel="stylesheet" type="text/css" />
+    <!-- Include app styles. -->
+    <link href="css/app.css" rel="stylesheet" type="text/css" />
+
+    <!-- Include jTable script file. -->
+    <script src="js/jquery-3.2.1.min.js" type="text/javascript"></script>
+    <script src="js/jquery-ui-1.12.1/jquery-ui.min.js" type="text/javascript"></script>
+    <script src="js/jtable.2.4.0/jquery.jtable.js" type="text/javascript"></script>
+    <!-- Include app script files. -->
+    <script src="js/app.js" type="text/javascript"></script>
+    <script src="js/products.js" type="text/javascript"></script>
+
+</head>
+<body>
+
+    <div th:insert="~{template :: hdr}"></div>
+
+    <div id="ProductsTableContainer" class="log4j-table-container"></div>
+
+    <div th:insert="~{template :: ftr}"></div>
+</body>
+</html>

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/template.html
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/template.html b/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/template.html
new file mode 100644
index 0000000..66e61df
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/webapp/WEB-INF/templates/template.html
@@ -0,0 +1,42 @@
+<!--
+  ~ Licensed to the Apache Software Foundation (ASF) under one or more
+  ~ contributor license agreements. See the NOTICE file distributed with
+  ~ this work for additional information regarding copyright ownership.
+  ~ The ASF licenses this file to You under the Apache license, Version 2.0
+  ~ (the "License"); you may not use this file except in compliance with
+  ~ the License. You may obtain a copy of the License at
+  ~
+  ~      http://www.apache.org/licenses/LICENSE-2.0
+  ~
+  ~ Unless required by applicable law or agreed to in writing, software
+  ~ distributed under the License is distributed on an "AS IS" BASIS,
+  ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+  ~ See the license for the specific language governing permissions and
+  ~ limitations under the license.
+  -->
+<!DOCTYPE html>
+
+<html xmlns:th="http://www.thymeleaf.org">
+
+<body>
+
+    <div th:fragment="hdr">
+          <div id="nav">
+            <a href="products">Products</a>
+            <a href="categories">Categories</a>
+            <a href="events">Events</a>
+            <a href="attributes">Attributes</a>
+            <a id="save-all" href="#">Save All Changes</a>
+          </div>
+    </div>
+
+    <div th:fragment="ftr">
+        <div id="ftr">
+            Copyright © 2017 <a href="https://www.apache.org">The Apache Software Foundation</a>. All Rights Reserved.
+        </div>
+        <div id="log4jtooltip"></div>
+    </div>
+
+</body>
+
+</html>

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/css/app.css
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/css/app.css b/log4j-catalog/log4j-catalog-editor/src/main/webapp/css/app.css
new file mode 100644
index 0000000..d1ffda7
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/webapp/css/app.css
@@ -0,0 +1,205 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+@import url('https://fonts.googleapis.com/css?family=Lato');
+
+body {
+  font-family: 'Lato', sans-serif;
+  margin: 0;
+  padding: 0;
+}
+
+#nav {
+  background-color: #f3f3f3;
+  border-bottom: 1px solid #e7e7e7;
+  display: flex;
+  height: 48px;
+  justify-content: space-between;
+  margin-bottom: 24px;
+  width: 100%;
+}
+
+#nav a {
+  border-right: 1px solid #e7e7e7;
+  color: #666;
+  font-size: 16px;
+  line-height: 48px;
+  padding: 0 14px;
+  text-decoration: none;
+}
+
+#nav #save-all {
+  border-right: none;
+  border-left: 1px solid #e7e7e7;
+  margin-left: auto;
+}
+
+#nav a:hover,
+#nav #save-all:hover {
+  background-color: #0b67cd;
+  border-right: 1px solid #0b67cd;
+  color: #fff;
+}
+
+#ftr {
+  margin: 24px;
+  text-align: center;
+}
+
+.log4j-table-container {
+  padding: 0 24px;
+}
+
+.form-processing {
+  position: absolute;
+  width: 100%;
+  height: 100%;
+  background-color: #fff;
+  z-index: 100;
+  opacity: .75;
+}
+
+.form-processing .gif {
+  position: absolute;
+  top: calc(50% - 16px);
+  left: calc(50% - 16px);
+  width: 32px;
+  height: 32px;
+  background-image: url(../images/ajax-loader.gif);
+  background-repeat: no-repeat;
+}
+
+.log4J-action-icon {
+  opacity: .4;
+}
+
+.log4j-catalog-form {
+  position: relative;
+}
+
+.log4j-catalog-form p {
+  position: relative;
+}
+
+.log4j-catalog-form input[type="text"],
+.log4j-catalog-form select,
+.log4j-catalog-form textarea {
+  width: 80%;
+  border: 1px solid #ddd;
+  padding: 2px 7px;
+  background-color: #fff;
+}
+
+.log4j-catalog-form select[multiple] {
+  height: 200px;
+}
+
+.log4j-catalog-form label {
+  display: block;
+  margin-bottom: 7px;
+  color: #aaa;
+}
+
+.log4j-catalog-form .form-error {
+  position: absolute;
+  top: 52px;
+  left: 354px;
+  color: #ff0000;
+  font-size: 12px;
+}
+
+.log4j-catalog-modal {
+  background-color: #fff;
+  border: 1px solid #ddd;
+  box-shadow: 0 0 20px #ccc;
+  left: 0;
+  margin: auto;
+  padding: 24px;
+  position: absolute;
+  right: 0;
+  top: 25%;
+  width: 500px;
+}
+
+.log4j-catalog-title {
+  border-bottom: 1px solid #ddd;
+  margin-bottom: 24px;
+  padding-bottom: 14px;
+}
+
+.log4j-catalog-button + .log4j-catalog-button {
+  margin-left: 24px;
+}
+
+/* Events */
+.event-attributes,
+.event-attribute-row {
+  font-size: 12px;
+}
+
+.event-attribute-row,
+.attribute-constraint-row,
+.product-event-row {
+  display: block;
+  clear: both;
+  line-height: 12px;
+  margin: 2px 0;
+  vertical-align: middle;
+}
+
+.event-attribute-row input[type="text"] {
+  width: 270px;
+  margin-right: 24px;
+}
+
+.event-attribute-item-required {
+  font-size: 10px;
+  color: #aaa;
+}
+
+.event-attribute-row button {
+  margin-left: 24px;
+}
+
+.attribute-constraint-row .form-error {
+  position: static;
+  padding: 0 4px;
+}
+
+.attribute-constraint-name {
+  font-size: 12px;
+  display: inline-block;
+  text-transform: uppercase;
+  width: 145px;
+}
+
+.attribute-constraint-data,
+#addAttributeConstraintValue {
+  width: 251px !important;
+}
+
+#addAttributeConstraintName {
+  width: 145px;
+}
+
+#addEventAttribute {
+  width: 375px !important;
+}
+
+#addEventAttributeButton,
+.remove-event-attribute-button {
+  cursor: pointer;
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/ajax-loader.gif
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/ajax-loader.gif b/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/ajax-loader.gif
new file mode 100644
index 0000000..bd6dd97
Binary files /dev/null and b/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/ajax-loader.gif differ

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/attributes.png
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/attributes.png b/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/attributes.png
new file mode 100644
index 0000000..7957193
Binary files /dev/null and b/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/attributes.png differ

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/constraint.png
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/constraint.png b/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/constraint.png
new file mode 100644
index 0000000..7604751
Binary files /dev/null and b/log4j-catalog/log4j-catalog-editor/src/main/webapp/images/constraint.png differ

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/js/app.js
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/js/app.js b/log4j-catalog/log4j-catalog-editor/src/main/webapp/js/app.js
new file mode 100644
index 0000000..1e906d3
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/webapp/js/app.js
@@ -0,0 +1,81 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+$(document).ready(function () {
+    // Clear localStorage
+    localStorage.clear();
+
+    $('#save-all').click(function(e) {
+      e.preventDefault();
+      saveAllChanges();
+    });
+});
+
+// Modal action handlers
+function closeLog4jModal() {
+    $('.log4j-catalog-modal').remove();
+}
+
+function log4jSubmitHandler(submitHandler) {
+    submitHandler();
+    closeLog4jModal();
+}
+
+function showLog4JModal(title, content) {
+    closeLog4jModal();
+    var modalContent = ' \
+        <div class="log4j-catalog-modal"> \
+            <div class="log4j-catalog-title">' + title + '</div> \
+            <div class="log4j-catalog-content">' + content + '</div> \
+        </div>';
+
+    $('body').append(modalContent);
+    window.scrollTo(0, 0);
+}
+
+function showLoadingAnimation() {
+  $('.log4j-catalog-form').prepend('<div class="form-processing"><div class="gif"></div></div>');
+}
+
+function validateFormContent() {
+    var errors = 0;
+    $('.form-error').remove();
+    $('.required').each(function() {
+        if (!$(this).val()) {
+          errors++;
+          $('<span class="form-error">Required.</span>').insertAfter($(this));
+        }
+    });
+    if (errors) return false;
+    return true;
+}
+
+function saveAllChanges() {
+  $.ajax({
+      type: 'POST',
+      contentType: 'application/json',
+      url: 'catalog',
+      data: null,
+      success:function(response) {
+          if (response.Result === 'OK') {
+              $('.log4j-table-container"').jtable('load');
+          }
+      },
+      error:function(jqXhr, textStatus, errorThrown) {
+          console.error(textStatus + ' - ' + errorThrown);
+      }
+  });
+}

http://git-wip-us.apache.org/repos/asf/logging-log4j-audit/blob/b75cbcd1/log4j-catalog/log4j-catalog-editor/src/main/webapp/js/attributes.js
----------------------------------------------------------------------
diff --git a/log4j-catalog/log4j-catalog-editor/src/main/webapp/js/attributes.js b/log4j-catalog/log4j-catalog-editor/src/main/webapp/js/attributes.js
new file mode 100644
index 0000000..bb71036
--- /dev/null
+++ b/log4j-catalog/log4j-catalog-editor/src/main/webapp/js/attributes.js
@@ -0,0 +1,364 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache license, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the license for the specific language governing permissions and
+ * limitations under the license.
+ */
+$(document).ready(function () {
+    $('#AttributesTableContainer').jtable({
+        title: 'Table of Attributes',
+        paging: true, //Enable paging
+        pageSize: 25, //Set page size (default: 25)
+        sorting: true, //Enable sorting
+        defaultSorting: 'Name ASC', //Set default sorting
+        actions: {
+            listAction: 'api/attributes/list',
+        },
+        toolbar: {
+            items: [{
+                icon: 'js/jtable.2.4.0/themes/metro/add.png',
+                text: 'Add new record',
+                click: () => { addEditAttributeItem() }
+            }]
+        },
+        fields: {
+            id: {
+                key: true,
+                list: false
+            },
+            name: {
+                title: 'Name',
+                width: '15%'
+            },
+            displayName: {
+                title: 'Display Name',
+                width: '15%'
+            },
+            description: {
+                title: 'Description',
+                width: '25%'
+            },
+            dataType: {
+                title: 'Data Type',
+                width: '5%'
+            },
+            indexed: {
+                title: 'Indexed',
+                width: '5%',
+                display: function (attributeData) {
+                    return attributeData.record.indexed ? 'true' : 'false';
+                }
+            },
+            sortable: {
+                title: 'Sortable',
+                width: '5%',
+                display: function (attributeData) {
+                    return attributeData.record.sortable ? 'true' : 'false';
+                }
+            },
+            required: {
+                title: 'Required',
+                width: '5%',
+                display: function (attributeData) {
+                    return attributeData.record.required ? 'true' : 'false';
+                }
+            },
+            requestContext: {
+                title: 'RequestContext',
+                width: '3%',
+                display: function (attributeData) {
+                    return attributeData.record.requestContext ? 'true' : 'false';
+                }
+            },
+            constraints: {
+                title: 'Constraints',
+                width: '16%',
+                sorting: false,
+                edit: false,
+                create: false,
+                display: function (attributeData) {
+                    var constraintList = '';
+                    if (typeof(attributeData.record.constraints) != 'undefined' && attributeData.record.constraints != null) {
+                        constraintList = attributeData.record.constraints.map(function (elem) {
+                            return elem.constraintType.name + '("' + elem.value + '")'
+                        }).join(' | ');
+                    }
+                    //Create a div that will be used to view associated attributes
+                    var $divConstraints = $('<div class="constraints">' + constraintList + '</div>');
+                    return $divConstraints;
+                }
+            },
+            edit: {
+                title: '',
+                width: '2%',
+                display: function (attributeData) {
+                    // Store attribute item data in localStorage
+                    var attributeDataItem = JSON.stringify(attributeData.record);
+                    localStorage.setItem('attributeItem' + attributeData.record.id,attributeDataItem);
+                    return '<img class="log4J-action-icon" src="js/jtable.2.4.0/themes/metro/edit.png" onClick="addEditAttributeItem(' + attributeData.record.id + ')" />';
+                }
+            },
+            delete: {
+                title: '',
+                width: '2%',
+                display: function (attributeData) {
+                    return '<img class="log4J-action-icon" src="js/jtable.2.4.0/themes/metro/delete.png" onClick="deleteAttributeItem(' + attributeData.record.id + ')" />';
+                }
+            }
+        }
+    });
+    $.ajax({
+        type: 'GET',
+        url: 'api/constraints/types',
+        success:function(response){
+            localStorage.setItem('allConstraints', response);
+        },
+        error:function(jqXhr, textStatus, errorThrown){
+            console.error(textStatus + ' - ' + errorThrown);
+        }
+    });
+    //Load attributes list from server
+    $('#AttributesTableContainer').jtable('load');
+});
+
+function deleteAttributeItem(attributeId) {
+    var response = confirm('Are you sure you want to delete this attribute?');
+    if (response) {
+        var postData = {};
+        postData['id'] = attributeId
+        $.ajax({
+            type: 'POST',
+            contentType: 'application/json',
+            url: 'api/attributes/delete',
+            data: JSON.stringify(postData),
+            success:function(response) {
+                if (response.Result === 'OK') {
+                    $('#AttributesTableContainer').jtable('load');
+                }
+            },
+            error:function(jqXhr, textStatus, errorThrown) {
+                console.error(textStatus + ' - ' + errorThrown);
+            }
+        });
+    }
+}
+
+function addEditattributeItemHandler() {
+  var validForm = validateFormContent();
+  if (validForm) {
+      showLoadingAnimation();
+      var postUrl = 'api/attributes/create';
+      var postData = {};
+      var attributeConstraints = [];
+      postData['name'] = $('#attributeName').val();
+      postData['displayName'] = $('#attributeDisplayName').val();
+      postData['description'] = $('#attributeDescription').val();
+      postData['dataType'] = $('#attributeDataType').val();
+      postData['indexed'] = $('#attributeIndexed').val();
+      postData['sortable'] = $('#attributeSortable').val();
+      postData['required'] = $('#attributeRequired').val();
+      postData['requestContext'] = $('#attributeRequestContext').val();
+      $('#attributeConstraints .attribute-constraint-row').each(function() {
+          var attributeConstraintItem = {
+              constraintType: { name: $(this).find('.attribute-constraint-name').text().toLowerCase() },
+              value: $(this).find('input')[0].value,
+          };
+          attributeConstraints.push(attributeConstraintItem);
+      });
+      postData['constraints'] = attributeConstraints;
+      if ($('#attributeId').val()) {
+          postUrl = 'api/attributes/update';
+          postData['id'] = $('#attributeId').val();
+      }
+      $.ajax({
+          type: 'POST',
+          contentType: 'application/json',
+          url: postUrl,
+          data: JSON.stringify(postData),
+          success:function(response) {
+              if (response.Result === 'OK') {
+                  $('#AttributesTableContainer').jtable('load');
+                  closeLog4jModal();
+              }
+          },
+          error:function(jqXhr, textStatus, errorThrown) {
+              console.error(textStatus + ' - ' + errorThrown);
+          }
+      });
+  }
+}
+
+function addEditAttributeItem(attributeId) {
+  var hiddenIdField = '';
+  var attributeData = {};
+  if (attributeId) {
+      hiddenIdField = '<input type="hidden" id="attributeId" name="id" value="' + attributeId + '" />';
+  } else {
+      attributeId = 'tempEventData';
+      var tempEventData = {
+          id: attributeId,
+          constraints: [],
+      }
+      localStorage.setItem('attributeItem' + attributeId, JSON.stringify(tempEventData));
+  }
+  attributeFormContent = ' \
+      <form id="add-edit-attribute-form" class="log4j-catalog-form" method="post"> \
+        ' + hiddenIdField + ' \
+        <p> \
+            <label>Name</label> \
+            <input type="text" id="attributeName" name="name" class="required" /> \
+        </p> \
+        <p> \
+            <label>Display Name</label> \
+            <input type="text" id="attributeDisplayName" name="displayName" class="required" /> \
+        </p> \
+        <p> \
+            <label>Description</label> \
+            <input type="text" id="attributeDescription" name="description" class="required" /> \
+        </p> \
+        <p> \
+            <label>Data Type</label> \
+            <select id="attributeDataType" name="indexed" class="required"> \
+            </select> \
+        </p> \
+        <p> \
+            <label>Indexed</label> \
+            <select id="attributeIndexed" name="indexed" class="required"> \
+                <option value="false">false</option> \
+                <option value="true">true</option> \
+            </select> \
+        </p> \
+        <p> \
+            <label>Sortable</label> \
+            <select id="attributeSortable" name="sortable" class="required"> \
+                <option value="false">false</option> \
+                <option value="true">true</option> \
+            </select> \
+        </p> \
+        <p> \
+            <label>Required</label> \
+            <select id="attributeRequired" name="required" class="required"> \
+                <option value="false">false</option> \
+                <option value="true">true</option> \
+            </select> \
+        </p> \
+        <p> \
+            <label>Request Context</label> \
+            <select id="attributeRequestContext" name="requestContext" class="required"> \
+                <option value="false">false</option> \
+                <option value="true">true</option> \
+            </select> \
+        </p> \
+        <p> \
+            <label>Assigned Constraints</label> \
+            <span id="attributeConstraints"></span> \
+        </p> \
+        <p> \
+            <label>Add Constraint</label> \
+            <span> \
+                <select name="addAttributeConstraintName" id="addAttributeConstraintName"> \
+                    <option value="">loading...</option> \
+                </select> \
+                <input type="text" name="addAttributeConstraintValue" id="addAttributeConstraintValue" /> \
+                <button id="addAttributeConstraintButton">+</button> \
+            </span> \
+        </p> \
+      </form> \
+      <div class="log4j-catalog-button-row"> \
+          <button class="log4j-catalog-button" onclick="closeLog4jModal()">Cancel</button>\
+          <button class="log4j-catalog-button" onclick="addEditattributeItemHandler()">Save</button> \
+      </div> \
+  ';
+  showLog4JModal('Add / Edit Attribute Item', attributeFormContent);
+  var dataTypes = ['STRING', 'BIG_DECIMAL', 'DOUBLE', 'FLOAT', 'INT', 'LONG', 'BOOLEAN', 'LIST', 'MAP'];
+  $.each(dataTypes.sort(), function(index, value) {
+      $('#attributeDataType').append('<option value="' + value + '">' + value + '</option>');
+  });
+  if (localStorage.getItem('attributeItem' + attributeId)) {
+      attributeData = JSON.parse(localStorage.getItem('attributeItem' + attributeId));
+      $('#attributeName').val(attributeData.name);
+      $('#attributeDisplayName').val(attributeData.displayName);
+      $('#attributeDescription').val(attributeData.description);
+      $('#attributeDataType option[value="' + attributeData.dataType + '"]').attr('selected', 'selected');
+      $('#attributeIndexed option[value="' + attributeData.indexed + '"]').attr('selected', 'selected');
+      $('#attributeSortable option[value="' + attributeData.sortable + '"]').attr('selected', 'selected');
+      $('#attributeRequired option[value="' + attributeData.required + '"]').attr('selected', 'selected');
+      $('#attributeRequestContext option[value="' + attributeData.requestContext + '"]').attr('selected', 'selected');
+  }
+  populateAttributeConstraints(attributeData.constraints, attributeId);
+}
+
+function populateAttributeConstraints(assignedConstraints, attributeId) {
+    var selectedConstraints = [];
+    $('#attributeConstraints').children().remove();
+    if (attributeId && assignedConstraints) {
+        assignedConstraints.map((item) => {
+            selectedConstraints.push(item.constraintType.name);
+            $('#attributeConstraints').append(' \
+                <span class="attribute-constraint-row"> \
+                    <span class="attribute-constraint-name">' + item.constraintType.name + '</span> \
+                    <input type="text" name="constraints[]" class="attribute-constraint-data required" value="' + item.value + '" /> \
+                    <button class="remove-attribute-constraint-button" alt="' + attributeId + '" rel="' + item.constraintType.name + '">-</button> \
+                </span> \
+            ');
+        });
+    }
+    function checkPendingRequest() {
+        if ($.active > 0) {
+            window.setTimeout(checkPendingRequest, 1000);
+        } else {
+            var allConstraints = localStorage.getItem('allConstraints').split(',');
+            allConstraints.sort();
+            $('#addAttributeConstraintName option').remove();
+            allConstraints.map((item) => {
+                if (!selectedConstraints.includes(item)) {
+                    $('#addAttributeConstraintName').append(' \
+                        <option value="' + item + '">' + item.toUpperCase() + '</option> \
+                    ');
+                }
+            });
+        }
+    }
+    checkPendingRequest();
+    assignAttributeConstraintListeners(attributeId);
+}
+
+function assignAttributeConstraintListeners(attributeId) {
+    $('#addAttributeConstraintButton, .remove-attribute-constraint-button').unbind();
+    $('#addAttributeConstraintButton').click(function(e) {
+        e.preventDefault();
+        var allConstraints = localStorage.getItem('allConstraints').split(',');
+        attributeData = JSON.parse(localStorage.getItem('attributeItem' + attributeId));
+        if (typeof(attributeData.constraints) == 'undefined' || attributeData.constraints == null) {
+            attributeData.constraints = [];
+        }
+        attributeData.constraints.push({
+            constraintType: { name: $('#addAttributeConstraintName').val() },
+            value: $('#addAttributeConstraintValue').val()
+        });
+        localStorage.setItem('attributeItem' + attributeId, JSON.stringify(attributeData));
+        $('#addAttributeConstraintValue').val('');
+        populateAttributeConstraints(attributeData.constraints, attributeId);
+    });
+    $('.remove-attribute-constraint-button').click(function(e) {
+        e.preventDefault();
+        var allConstraints = localStorage.getItem('allConstraints').split(',');
+        attributeData = JSON.parse(localStorage.getItem('attributeItem' + attributeId));
+        var newConstraints = attributeData.constraints.filter((obj) => {
+            return obj.constraintType.name !== $(this).attr('rel');
+        });
+        attributeData['constraints'] = newConstraints;
+        localStorage.setItem('attributeItem' + attributeId, JSON.stringify(attributeData));
+        populateAttributeConstraints(attributeData.constraints, attributeId);
+    });
+}