| # Copyright 2024 The Bazel Authors. All rights reserved. | |
| # | |
| # 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. | |
| """This is a helper script to generate the log4j2 test files. | |
| Run it like this from the root of the repository: | |
| python3 src/tools/singlejar/data/log4j2_plugins_gen.py | |
| """ | |
| import argparse | |
| import io | |
| import os | |
| import struct | |
| import sys | |
| import zipfile | |
| class PluginEntry: | |
| def __init__(self, key, class_name, name, printable, defer, category): | |
| self.key = key | |
| self.class_name = class_name | |
| self.name = name | |
| self.printable = printable | |
| self.defer = defer | |
| self.category = category | |
| def __repr__(self): | |
| return ( | |
| f"PluginEntry(key={self.key}, class_name={self.class_name}," | |
| f" name={self.name}, printable={self.printable}, defer={self.defer}," | |
| f" category={self.category})" | |
| ) | |
| def read_utf_string(buffer): | |
| length = struct.unpack(">H", buffer.read(2))[0] | |
| return buffer.read(length).decode("utf-8") | |
| def write_utf_string(buffer, string): | |
| encoded = string.encode("utf-8") | |
| buffer.write(struct.pack(">H", len(encoded))) | |
| buffer.write(encoded) | |
| def write_cache_file(categories, output_path): | |
| """Writes categories to output.""" | |
| buffer = io.BytesIO() | |
| buffer.write(struct.pack(">I", len(categories))) | |
| for category, entries in categories.items(): | |
| write_utf_string(buffer, category) | |
| buffer.write(struct.pack(">I", len(entries))) | |
| for entry in entries.values(): | |
| write_utf_string(buffer, entry.key) | |
| write_utf_string(buffer, entry.class_name) | |
| write_utf_string(buffer, entry.name) | |
| buffer.write(struct.pack(">?", entry.printable)) | |
| buffer.write(struct.pack(">?", entry.defer)) | |
| with open(output_path, "wb") as cache_file: | |
| cache_file.write(buffer.getvalue()) | |
| def load_cache_files_from_bytes(byte_data, consolidated_plugins): | |
| """Loads byte_data into consolidated_plugins.""" | |
| buffer = io.BytesIO(byte_data) | |
| count = struct.unpack(">I", buffer.read(4))[0] | |
| for _ in range(count): | |
| category = read_utf_string(buffer) | |
| category_map = consolidated_plugins.setdefault(category, {}) | |
| entries = struct.unpack(">I", buffer.read(4))[0] | |
| for _ in range(entries): | |
| key = read_utf_string(buffer) | |
| class_name = read_utf_string(buffer) | |
| name = read_utf_string(buffer) | |
| printable = struct.unpack(">?", buffer.read(1))[0] | |
| defer = struct.unpack(">?", buffer.read(1))[0] | |
| if key in category_map: | |
| print( | |
| f"Warning: Collision detected for key '{key}' in category" | |
| f" '{category}'. Existing entry will be overwritten." | |
| ) | |
| entry = PluginEntry(key, class_name, name, printable, defer, category) | |
| category_map[key] = entry | |
| def create_jar_with_data(dat_file_path, jar_file_path): | |
| jar_internal_path = ( | |
| "META-INF/org/apache/logging/log4j/core/config/plugins/Log4j2Plugins.dat" | |
| ) | |
| with zipfile.ZipFile(jar_file_path, "w", zipfile.ZIP_DEFLATED) as jar: | |
| jar.write(dat_file_path, arcname=jar_internal_path) | |
| if __name__ == "__main__": | |
| parser = argparse.ArgumentParser() | |
| parser.add_argument("--data_dir", default="src/tools/singlejar/data") | |
| parser.add_argument("--dump") | |
| args = parser.parse_args() | |
| if args.dump: | |
| with open(args.dump, "rb") as f: | |
| data_bytes = f.read() | |
| consolidated_plugins_dict = {} | |
| load_cache_files_from_bytes(data_bytes, consolidated_plugins_dict) | |
| for plugin_category, plugin_entries in consolidated_plugins_dict.items(): | |
| print(f"Category: {plugin_category}") | |
| for k, e in plugin_entries.items(): | |
| print(f" {k}: {e}") | |
| sys.exit(0) | |
| values = [ | |
| ( | |
| "log4j2_plugins_set_1.jar", | |
| { | |
| "cat1": { | |
| "key1": PluginEntry( | |
| "key1", "class1", "name1", True, False, "cat1" | |
| ), | |
| "key2": PluginEntry( | |
| "key2", "class2", "name2", False, True, "cat1" | |
| ), | |
| }, | |
| "cat2": { | |
| "key3": PluginEntry( | |
| "key3", "class3", "name3", True, True, "cat2" | |
| ) | |
| }, | |
| }, | |
| ), | |
| ( | |
| "log4j2_plugins_set_2.jar", | |
| { | |
| "cat1": { | |
| "key11": PluginEntry( | |
| "key11", "class1", "name1", True, False, "cat1" | |
| ), | |
| "key12": PluginEntry( | |
| "key12", "class2", "name2", False, True, "cat1" | |
| ), | |
| }, | |
| "cat3": { | |
| "key13": PluginEntry( | |
| "key13", "class3", "name3", True, True, "cat3" | |
| ), | |
| }, | |
| }, | |
| ), | |
| ] | |
| for v in values: | |
| dat = os.path.join(args.data_dir, "temp.dat") | |
| write_cache_file(v[1], dat) | |
| create_jar_with_data(dat, os.path.join(args.data_dir, v[0])) | |
| os.remove(dat) | |
| write_cache_file( | |
| { | |
| "cat1": { | |
| "key1": PluginEntry( | |
| "key1", "class1", "name1", True, False, "cat1" | |
| ), | |
| "key11": PluginEntry( | |
| "key11", "class1", "name1", True, False, "cat1" | |
| ), | |
| "key12": PluginEntry( | |
| "key12", "class2", "name2", False, True, "cat1" | |
| ), | |
| "key2": PluginEntry( | |
| "key2", "class2", "name2", False, True, "cat1" | |
| ), | |
| }, | |
| "cat2": { | |
| "key3": PluginEntry( | |
| "key3", "class3", "name3", True, True, "cat2" | |
| ), | |
| }, | |
| "cat3": { | |
| "key13": PluginEntry( | |
| "key13", "class3", "name3", True, True, "cat3" | |
| ), | |
| }, | |
| }, | |
| os.path.join(args.data_dir, "log4j2_plugins_set_result.dat"), | |
| ) |