diff --git a/cpp/nativecall/resource.h b/cpp/nativecall/resource.h
deleted file mode 100644
index a6396e3..0000000
--- a/cpp/nativecall/resource.h
+++ /dev/null
@@ -1,15 +0,0 @@
-/{{NO_DEPENDENCIES}}
-// Microsoft Developer Studio generated include file.
-// Used by version.rc
-//
-
-// Next default values for new objects
-//
-#ifdef APSTUDIO_INVOKED
-#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 103
-#define _APS_NEXT_COMMAND_VALUE 40001
-#define _APS_NEXT_CONTROL_VALUE 1000
-#define _APS_NEXT_SYMED_VALUE 101
-#endif
-#endif
diff --git a/cpp/wiiuse/include/classfile_constants.h b/cpp/wiiuse/include/classfile_constants.h
new file mode 100644
index 0000000..30e839e
--- /dev/null
+++ b/cpp/wiiuse/include/classfile_constants.h
@@ -0,0 +1,523 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ *
+ */
+
+#ifndef CLASSFILE_CONSTANTS_H
+#define CLASSFILE_CONSTANTS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Flags */
+
+enum {
+ JVM_ACC_PUBLIC = 0x0001,
+ JVM_ACC_PRIVATE = 0x0002,
+ JVM_ACC_PROTECTED = 0x0004,
+ JVM_ACC_STATIC = 0x0008,
+ JVM_ACC_FINAL = 0x0010,
+ JVM_ACC_SYNCHRONIZED = 0x0020,
+ JVM_ACC_SUPER = 0x0020,
+ JVM_ACC_VOLATILE = 0x0040,
+ JVM_ACC_BRIDGE = 0x0040,
+ JVM_ACC_TRANSIENT = 0x0080,
+ JVM_ACC_VARARGS = 0x0080,
+ JVM_ACC_NATIVE = 0x0100,
+ JVM_ACC_INTERFACE = 0x0200,
+ JVM_ACC_ABSTRACT = 0x0400,
+ JVM_ACC_STRICT = 0x0800,
+ JVM_ACC_SYNTHETIC = 0x1000,
+ JVM_ACC_ANNOTATION = 0x2000,
+ JVM_ACC_ENUM = 0x4000
+};
+
+/* Used in newarray instruction. */
+
+enum {
+ JVM_T_BOOLEAN = 4,
+ JVM_T_CHAR = 5,
+ JVM_T_FLOAT = 6,
+ JVM_T_DOUBLE = 7,
+ JVM_T_BYTE = 8,
+ JVM_T_SHORT = 9,
+ JVM_T_INT = 10,
+ JVM_T_LONG = 11
+};
+
+/* Constant Pool Entries */
+
+enum {
+ JVM_CONSTANT_Utf8 = 1,
+ JVM_CONSTANT_Unicode = 2, /* unused */
+ JVM_CONSTANT_Integer = 3,
+ JVM_CONSTANT_Float = 4,
+ JVM_CONSTANT_Long = 5,
+ JVM_CONSTANT_Double = 6,
+ JVM_CONSTANT_Class = 7,
+ JVM_CONSTANT_String = 8,
+ JVM_CONSTANT_Fieldref = 9,
+ JVM_CONSTANT_Methodref = 10,
+ JVM_CONSTANT_InterfaceMethodref = 11,
+ JVM_CONSTANT_NameAndType = 12
+};
+
+/* StackMapTable type item numbers */
+
+enum {
+ JVM_ITEM_Top = 0,
+ JVM_ITEM_Integer = 1,
+ JVM_ITEM_Float = 2,
+ JVM_ITEM_Double = 3,
+ JVM_ITEM_Long = 4,
+ JVM_ITEM_Null = 5,
+ JVM_ITEM_UninitializedThis = 6,
+ JVM_ITEM_Object = 7,
+ JVM_ITEM_Uninitialized = 8
+};
+
+/* Type signatures */
+
+enum {
+ JVM_SIGNATURE_ARRAY = '[',
+ JVM_SIGNATURE_BYTE = 'B',
+ JVM_SIGNATURE_CHAR = 'C',
+ JVM_SIGNATURE_CLASS = 'L',
+ JVM_SIGNATURE_ENDCLASS = ';',
+ JVM_SIGNATURE_ENUM = 'E',
+ JVM_SIGNATURE_FLOAT = 'F',
+ JVM_SIGNATURE_DOUBLE = 'D',
+ JVM_SIGNATURE_FUNC = '(',
+ JVM_SIGNATURE_ENDFUNC = ')',
+ JVM_SIGNATURE_INT = 'I',
+ JVM_SIGNATURE_LONG = 'J',
+ JVM_SIGNATURE_SHORT = 'S',
+ JVM_SIGNATURE_VOID = 'V',
+ JVM_SIGNATURE_BOOLEAN = 'Z'
+};
+
+/* Opcodes */
+
+enum {
+ JVM_OPC_nop = 0,
+ JVM_OPC_aconst_null = 1,
+ JVM_OPC_iconst_m1 = 2,
+ JVM_OPC_iconst_0 = 3,
+ JVM_OPC_iconst_1 = 4,
+ JVM_OPC_iconst_2 = 5,
+ JVM_OPC_iconst_3 = 6,
+ JVM_OPC_iconst_4 = 7,
+ JVM_OPC_iconst_5 = 8,
+ JVM_OPC_lconst_0 = 9,
+ JVM_OPC_lconst_1 = 10,
+ JVM_OPC_fconst_0 = 11,
+ JVM_OPC_fconst_1 = 12,
+ JVM_OPC_fconst_2 = 13,
+ JVM_OPC_dconst_0 = 14,
+ JVM_OPC_dconst_1 = 15,
+ JVM_OPC_bipush = 16,
+ JVM_OPC_sipush = 17,
+ JVM_OPC_ldc = 18,
+ JVM_OPC_ldc_w = 19,
+ JVM_OPC_ldc2_w = 20,
+ JVM_OPC_iload = 21,
+ JVM_OPC_lload = 22,
+ JVM_OPC_fload = 23,
+ JVM_OPC_dload = 24,
+ JVM_OPC_aload = 25,
+ JVM_OPC_iload_0 = 26,
+ JVM_OPC_iload_1 = 27,
+ JVM_OPC_iload_2 = 28,
+ JVM_OPC_iload_3 = 29,
+ JVM_OPC_lload_0 = 30,
+ JVM_OPC_lload_1 = 31,
+ JVM_OPC_lload_2 = 32,
+ JVM_OPC_lload_3 = 33,
+ JVM_OPC_fload_0 = 34,
+ JVM_OPC_fload_1 = 35,
+ JVM_OPC_fload_2 = 36,
+ JVM_OPC_fload_3 = 37,
+ JVM_OPC_dload_0 = 38,
+ JVM_OPC_dload_1 = 39,
+ JVM_OPC_dload_2 = 40,
+ JVM_OPC_dload_3 = 41,
+ JVM_OPC_aload_0 = 42,
+ JVM_OPC_aload_1 = 43,
+ JVM_OPC_aload_2 = 44,
+ JVM_OPC_aload_3 = 45,
+ JVM_OPC_iaload = 46,
+ JVM_OPC_laload = 47,
+ JVM_OPC_faload = 48,
+ JVM_OPC_daload = 49,
+ JVM_OPC_aaload = 50,
+ JVM_OPC_baload = 51,
+ JVM_OPC_caload = 52,
+ JVM_OPC_saload = 53,
+ JVM_OPC_istore = 54,
+ JVM_OPC_lstore = 55,
+ JVM_OPC_fstore = 56,
+ JVM_OPC_dstore = 57,
+ JVM_OPC_astore = 58,
+ JVM_OPC_istore_0 = 59,
+ JVM_OPC_istore_1 = 60,
+ JVM_OPC_istore_2 = 61,
+ JVM_OPC_istore_3 = 62,
+ JVM_OPC_lstore_0 = 63,
+ JVM_OPC_lstore_1 = 64,
+ JVM_OPC_lstore_2 = 65,
+ JVM_OPC_lstore_3 = 66,
+ JVM_OPC_fstore_0 = 67,
+ JVM_OPC_fstore_1 = 68,
+ JVM_OPC_fstore_2 = 69,
+ JVM_OPC_fstore_3 = 70,
+ JVM_OPC_dstore_0 = 71,
+ JVM_OPC_dstore_1 = 72,
+ JVM_OPC_dstore_2 = 73,
+ JVM_OPC_dstore_3 = 74,
+ JVM_OPC_astore_0 = 75,
+ JVM_OPC_astore_1 = 76,
+ JVM_OPC_astore_2 = 77,
+ JVM_OPC_astore_3 = 78,
+ JVM_OPC_iastore = 79,
+ JVM_OPC_lastore = 80,
+ JVM_OPC_fastore = 81,
+ JVM_OPC_dastore = 82,
+ JVM_OPC_aastore = 83,
+ JVM_OPC_bastore = 84,
+ JVM_OPC_castore = 85,
+ JVM_OPC_sastore = 86,
+ JVM_OPC_pop = 87,
+ JVM_OPC_pop2 = 88,
+ JVM_OPC_dup = 89,
+ JVM_OPC_dup_x1 = 90,
+ JVM_OPC_dup_x2 = 91,
+ JVM_OPC_dup2 = 92,
+ JVM_OPC_dup2_x1 = 93,
+ JVM_OPC_dup2_x2 = 94,
+ JVM_OPC_swap = 95,
+ JVM_OPC_iadd = 96,
+ JVM_OPC_ladd = 97,
+ JVM_OPC_fadd = 98,
+ JVM_OPC_dadd = 99,
+ JVM_OPC_isub = 100,
+ JVM_OPC_lsub = 101,
+ JVM_OPC_fsub = 102,
+ JVM_OPC_dsub = 103,
+ JVM_OPC_imul = 104,
+ JVM_OPC_lmul = 105,
+ JVM_OPC_fmul = 106,
+ JVM_OPC_dmul = 107,
+ JVM_OPC_idiv = 108,
+ JVM_OPC_ldiv = 109,
+ JVM_OPC_fdiv = 110,
+ JVM_OPC_ddiv = 111,
+ JVM_OPC_irem = 112,
+ JVM_OPC_lrem = 113,
+ JVM_OPC_frem = 114,
+ JVM_OPC_drem = 115,
+ JVM_OPC_ineg = 116,
+ JVM_OPC_lneg = 117,
+ JVM_OPC_fneg = 118,
+ JVM_OPC_dneg = 119,
+ JVM_OPC_ishl = 120,
+ JVM_OPC_lshl = 121,
+ JVM_OPC_ishr = 122,
+ JVM_OPC_lshr = 123,
+ JVM_OPC_iushr = 124,
+ JVM_OPC_lushr = 125,
+ JVM_OPC_iand = 126,
+ JVM_OPC_land = 127,
+ JVM_OPC_ior = 128,
+ JVM_OPC_lor = 129,
+ JVM_OPC_ixor = 130,
+ JVM_OPC_lxor = 131,
+ JVM_OPC_iinc = 132,
+ JVM_OPC_i2l = 133,
+ JVM_OPC_i2f = 134,
+ JVM_OPC_i2d = 135,
+ JVM_OPC_l2i = 136,
+ JVM_OPC_l2f = 137,
+ JVM_OPC_l2d = 138,
+ JVM_OPC_f2i = 139,
+ JVM_OPC_f2l = 140,
+ JVM_OPC_f2d = 141,
+ JVM_OPC_d2i = 142,
+ JVM_OPC_d2l = 143,
+ JVM_OPC_d2f = 144,
+ JVM_OPC_i2b = 145,
+ JVM_OPC_i2c = 146,
+ JVM_OPC_i2s = 147,
+ JVM_OPC_lcmp = 148,
+ JVM_OPC_fcmpl = 149,
+ JVM_OPC_fcmpg = 150,
+ JVM_OPC_dcmpl = 151,
+ JVM_OPC_dcmpg = 152,
+ JVM_OPC_ifeq = 153,
+ JVM_OPC_ifne = 154,
+ JVM_OPC_iflt = 155,
+ JVM_OPC_ifge = 156,
+ JVM_OPC_ifgt = 157,
+ JVM_OPC_ifle = 158,
+ JVM_OPC_if_icmpeq = 159,
+ JVM_OPC_if_icmpne = 160,
+ JVM_OPC_if_icmplt = 161,
+ JVM_OPC_if_icmpge = 162,
+ JVM_OPC_if_icmpgt = 163,
+ JVM_OPC_if_icmple = 164,
+ JVM_OPC_if_acmpeq = 165,
+ JVM_OPC_if_acmpne = 166,
+ JVM_OPC_goto = 167,
+ JVM_OPC_jsr = 168,
+ JVM_OPC_ret = 169,
+ JVM_OPC_tableswitch = 170,
+ JVM_OPC_lookupswitch = 171,
+ JVM_OPC_ireturn = 172,
+ JVM_OPC_lreturn = 173,
+ JVM_OPC_freturn = 174,
+ JVM_OPC_dreturn = 175,
+ JVM_OPC_areturn = 176,
+ JVM_OPC_return = 177,
+ JVM_OPC_getstatic = 178,
+ JVM_OPC_putstatic = 179,
+ JVM_OPC_getfield = 180,
+ JVM_OPC_putfield = 181,
+ JVM_OPC_invokevirtual = 182,
+ JVM_OPC_invokespecial = 183,
+ JVM_OPC_invokestatic = 184,
+ JVM_OPC_invokeinterface = 185,
+ JVM_OPC_xxxunusedxxx = 186,
+ JVM_OPC_new = 187,
+ JVM_OPC_newarray = 188,
+ JVM_OPC_anewarray = 189,
+ JVM_OPC_arraylength = 190,
+ JVM_OPC_athrow = 191,
+ JVM_OPC_checkcast = 192,
+ JVM_OPC_instanceof = 193,
+ JVM_OPC_monitorenter = 194,
+ JVM_OPC_monitorexit = 195,
+ JVM_OPC_wide = 196,
+ JVM_OPC_multianewarray = 197,
+ JVM_OPC_ifnull = 198,
+ JVM_OPC_ifnonnull = 199,
+ JVM_OPC_goto_w = 200,
+ JVM_OPC_jsr_w = 201,
+ JVM_OPC_MAX = 201
+};
+
+/* Opcode length initializer, use with something like:
+ * unsigned char opcode_length[JVM_OPC_MAX+1] = JVM_OPCODE_LENGTH_INITIALIZER;
+ */
+#define JVM_OPCODE_LENGTH_INITIALIZER { \
+ 1, /* nop */ \
+ 1, /* aconst_null */ \
+ 1, /* iconst_m1 */ \
+ 1, /* iconst_0 */ \
+ 1, /* iconst_1 */ \
+ 1, /* iconst_2 */ \
+ 1, /* iconst_3 */ \
+ 1, /* iconst_4 */ \
+ 1, /* iconst_5 */ \
+ 1, /* lconst_0 */ \
+ 1, /* lconst_1 */ \
+ 1, /* fconst_0 */ \
+ 1, /* fconst_1 */ \
+ 1, /* fconst_2 */ \
+ 1, /* dconst_0 */ \
+ 1, /* dconst_1 */ \
+ 2, /* bipush */ \
+ 3, /* sipush */ \
+ 2, /* ldc */ \
+ 3, /* ldc_w */ \
+ 3, /* ldc2_w */ \
+ 2, /* iload */ \
+ 2, /* lload */ \
+ 2, /* fload */ \
+ 2, /* dload */ \
+ 2, /* aload */ \
+ 1, /* iload_0 */ \
+ 1, /* iload_1 */ \
+ 1, /* iload_2 */ \
+ 1, /* iload_3 */ \
+ 1, /* lload_0 */ \
+ 1, /* lload_1 */ \
+ 1, /* lload_2 */ \
+ 1, /* lload_3 */ \
+ 1, /* fload_0 */ \
+ 1, /* fload_1 */ \
+ 1, /* fload_2 */ \
+ 1, /* fload_3 */ \
+ 1, /* dload_0 */ \
+ 1, /* dload_1 */ \
+ 1, /* dload_2 */ \
+ 1, /* dload_3 */ \
+ 1, /* aload_0 */ \
+ 1, /* aload_1 */ \
+ 1, /* aload_2 */ \
+ 1, /* aload_3 */ \
+ 1, /* iaload */ \
+ 1, /* laload */ \
+ 1, /* faload */ \
+ 1, /* daload */ \
+ 1, /* aaload */ \
+ 1, /* baload */ \
+ 1, /* caload */ \
+ 1, /* saload */ \
+ 2, /* istore */ \
+ 2, /* lstore */ \
+ 2, /* fstore */ \
+ 2, /* dstore */ \
+ 2, /* astore */ \
+ 1, /* istore_0 */ \
+ 1, /* istore_1 */ \
+ 1, /* istore_2 */ \
+ 1, /* istore_3 */ \
+ 1, /* lstore_0 */ \
+ 1, /* lstore_1 */ \
+ 1, /* lstore_2 */ \
+ 1, /* lstore_3 */ \
+ 1, /* fstore_0 */ \
+ 1, /* fstore_1 */ \
+ 1, /* fstore_2 */ \
+ 1, /* fstore_3 */ \
+ 1, /* dstore_0 */ \
+ 1, /* dstore_1 */ \
+ 1, /* dstore_2 */ \
+ 1, /* dstore_3 */ \
+ 1, /* astore_0 */ \
+ 1, /* astore_1 */ \
+ 1, /* astore_2 */ \
+ 1, /* astore_3 */ \
+ 1, /* iastore */ \
+ 1, /* lastore */ \
+ 1, /* fastore */ \
+ 1, /* dastore */ \
+ 1, /* aastore */ \
+ 1, /* bastore */ \
+ 1, /* castore */ \
+ 1, /* sastore */ \
+ 1, /* pop */ \
+ 1, /* pop2 */ \
+ 1, /* dup */ \
+ 1, /* dup_x1 */ \
+ 1, /* dup_x2 */ \
+ 1, /* dup2 */ \
+ 1, /* dup2_x1 */ \
+ 1, /* dup2_x2 */ \
+ 1, /* swap */ \
+ 1, /* iadd */ \
+ 1, /* ladd */ \
+ 1, /* fadd */ \
+ 1, /* dadd */ \
+ 1, /* isub */ \
+ 1, /* lsub */ \
+ 1, /* fsub */ \
+ 1, /* dsub */ \
+ 1, /* imul */ \
+ 1, /* lmul */ \
+ 1, /* fmul */ \
+ 1, /* dmul */ \
+ 1, /* idiv */ \
+ 1, /* ldiv */ \
+ 1, /* fdiv */ \
+ 1, /* ddiv */ \
+ 1, /* irem */ \
+ 1, /* lrem */ \
+ 1, /* frem */ \
+ 1, /* drem */ \
+ 1, /* ineg */ \
+ 1, /* lneg */ \
+ 1, /* fneg */ \
+ 1, /* dneg */ \
+ 1, /* ishl */ \
+ 1, /* lshl */ \
+ 1, /* ishr */ \
+ 1, /* lshr */ \
+ 1, /* iushr */ \
+ 1, /* lushr */ \
+ 1, /* iand */ \
+ 1, /* land */ \
+ 1, /* ior */ \
+ 1, /* lor */ \
+ 1, /* ixor */ \
+ 1, /* lxor */ \
+ 3, /* iinc */ \
+ 1, /* i2l */ \
+ 1, /* i2f */ \
+ 1, /* i2d */ \
+ 1, /* l2i */ \
+ 1, /* l2f */ \
+ 1, /* l2d */ \
+ 1, /* f2i */ \
+ 1, /* f2l */ \
+ 1, /* f2d */ \
+ 1, /* d2i */ \
+ 1, /* d2l */ \
+ 1, /* d2f */ \
+ 1, /* i2b */ \
+ 1, /* i2c */ \
+ 1, /* i2s */ \
+ 1, /* lcmp */ \
+ 1, /* fcmpl */ \
+ 1, /* fcmpg */ \
+ 1, /* dcmpl */ \
+ 1, /* dcmpg */ \
+ 3, /* ifeq */ \
+ 3, /* ifne */ \
+ 3, /* iflt */ \
+ 3, /* ifge */ \
+ 3, /* ifgt */ \
+ 3, /* ifle */ \
+ 3, /* if_icmpeq */ \
+ 3, /* if_icmpne */ \
+ 3, /* if_icmplt */ \
+ 3, /* if_icmpge */ \
+ 3, /* if_icmpgt */ \
+ 3, /* if_icmple */ \
+ 3, /* if_acmpeq */ \
+ 3, /* if_acmpne */ \
+ 3, /* goto */ \
+ 3, /* jsr */ \
+ 2, /* ret */ \
+ 99, /* tableswitch */ \
+ 99, /* lookupswitch */ \
+ 1, /* ireturn */ \
+ 1, /* lreturn */ \
+ 1, /* freturn */ \
+ 1, /* dreturn */ \
+ 1, /* areturn */ \
+ 1, /* return */ \
+ 3, /* getstatic */ \
+ 3, /* putstatic */ \
+ 3, /* getfield */ \
+ 3, /* putfield */ \
+ 3, /* invokevirtual */ \
+ 3, /* invokespecial */ \
+ 3, /* invokestatic */ \
+ 5, /* invokeinterface */ \
+ 0, /* xxxunusedxxx */ \
+ 3, /* new */ \
+ 2, /* newarray */ \
+ 3, /* anewarray */ \
+ 1, /* arraylength */ \
+ 1, /* athrow */ \
+ 3, /* checkcast */ \
+ 3, /* instanceof */ \
+ 1, /* monitorenter */ \
+ 1, /* monitorexit */ \
+ 0, /* wide */ \
+ 4, /* multianewarray */ \
+ 3, /* ifnull */ \
+ 3, /* ifnonnull */ \
+ 5, /* goto_w */ \
+ 5 /* jsr_w */ \
+}
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* CLASSFILE_CONSTANTS */
diff --git a/cpp/wiiuse/include/jawt.h b/cpp/wiiuse/include/jawt.h
new file mode 100644
index 0000000..87e0b18
--- /dev/null
+++ b/cpp/wiiuse/include/jawt.h
@@ -0,0 +1,278 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+#ifndef _JAVASOFT_JAWT_H_
+#define _JAVASOFT_JAWT_H_
+
+#include "jni.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * AWT native interface (new in JDK 1.3)
+ *
+ * The AWT native interface allows a native C or C++ application a means
+ * by which to access native structures in AWT. This is to facilitate moving
+ * legacy C and C++ applications to Java and to target the needs of the
+ * community who, at present, wish to do their own native rendering to canvases
+ * for performance reasons. Standard extensions such as Java3D also require a
+ * means to access the underlying native data structures of AWT.
+ *
+ * There may be future extensions to this API depending on demand.
+ *
+ * A VM does not have to implement this API in order to pass the JCK.
+ * It is recommended, however, that this API is implemented on VMs that support
+ * standard extensions, such as Java3D.
+ *
+ * Since this is a native API, any program which uses it cannot be considered
+ * 100% pure java.
+ */
+
+/*
+ * AWT Native Drawing Surface (JAWT_DrawingSurface).
+ *
+ * For each platform, there is a native drawing surface structure. This
+ * platform-specific structure can be found in jawt_md.h. It is recommended
+ * that additional platforms follow the same model. It is also recommended
+ * that VMs on Win32 and Solaris support the existing structures in jawt_md.h.
+ *
+ *******************
+ * EXAMPLE OF USAGE:
+ *******************
+ *
+ * In Win32, a programmer wishes to access the HWND of a canvas to perform
+ * native rendering into it. The programmer has declared the paint() method
+ * for their canvas subclass to be native:
+ *
+ *
+ * MyCanvas.java:
+ *
+ * import java.awt.*;
+ *
+ * public class MyCanvas extends Canvas {
+ *
+ * static {
+ * System.loadLibrary("mylib");
+ * }
+ *
+ * public native void paint(Graphics g);
+ * }
+ *
+ *
+ * myfile.c:
+ *
+ * #include "jawt_md.h"
+ * #include
+ *
+ * JNIEXPORT void JNICALL
+ * Java_MyCanvas_paint(JNIEnv* env, jobject canvas, jobject graphics)
+ * {
+ * JAWT awt;
+ * JAWT_DrawingSurface* ds;
+ * JAWT_DrawingSurfaceInfo* dsi;
+ * JAWT_Win32DrawingSurfaceInfo* dsi_win;
+ * jboolean result;
+ * jint lock;
+ *
+ * // Get the AWT
+ * awt.version = JAWT_VERSION_1_3;
+ * result = JAWT_GetAWT(env, &awt);
+ * assert(result != JNI_FALSE);
+ *
+ * // Get the drawing surface
+ * ds = awt.GetDrawingSurface(env, canvas);
+ * assert(ds != NULL);
+ *
+ * // Lock the drawing surface
+ * lock = ds->Lock(ds);
+ * assert((lock & JAWT_LOCK_ERROR) == 0);
+ *
+ * // Get the drawing surface info
+ * dsi = ds->GetDrawingSurfaceInfo(ds);
+ *
+ * // Get the platform-specific drawing info
+ * dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo;
+ *
+ * //////////////////////////////
+ * // !!! DO PAINTING HERE !!! //
+ * //////////////////////////////
+ *
+ * // Free the drawing surface info
+ * ds->FreeDrawingSurfaceInfo(dsi);
+ *
+ * // Unlock the drawing surface
+ * ds->Unlock(ds);
+ *
+ * // Free the drawing surface
+ * awt.FreeDrawingSurface(ds);
+ * }
+ *
+ */
+
+/*
+ * JAWT_Rectangle
+ * Structure for a native rectangle.
+ */
+typedef struct jawt_Rectangle {
+ jint x;
+ jint y;
+ jint width;
+ jint height;
+} JAWT_Rectangle;
+
+struct jawt_DrawingSurface;
+
+/*
+ * JAWT_DrawingSurfaceInfo
+ * Structure for containing the underlying drawing information of a component.
+ */
+typedef struct jawt_DrawingSurfaceInfo {
+ /*
+ * Pointer to the platform-specific information. This can be safely
+ * cast to a JAWT_Win32DrawingSurfaceInfo on Windows or a
+ * JAWT_X11DrawingSurfaceInfo on Solaris. See jawt_md.h for details.
+ */
+ void* platformInfo;
+ /* Cached pointer to the underlying drawing surface */
+ struct jawt_DrawingSurface* ds;
+ /* Bounding rectangle of the drawing surface */
+ JAWT_Rectangle bounds;
+ /* Number of rectangles in the clip */
+ jint clipSize;
+ /* Clip rectangle array */
+ JAWT_Rectangle* clip;
+} JAWT_DrawingSurfaceInfo;
+
+#define JAWT_LOCK_ERROR 0x00000001
+#define JAWT_LOCK_CLIP_CHANGED 0x00000002
+#define JAWT_LOCK_BOUNDS_CHANGED 0x00000004
+#define JAWT_LOCK_SURFACE_CHANGED 0x00000008
+
+/*
+ * JAWT_DrawingSurface
+ * Structure for containing the underlying drawing information of a component.
+ * All operations on a JAWT_DrawingSurface MUST be performed from the same
+ * thread as the call to GetDrawingSurface.
+ */
+typedef struct jawt_DrawingSurface {
+ /*
+ * Cached reference to the Java environment of the calling thread.
+ * If Lock(), Unlock(), GetDrawingSurfaceInfo() or
+ * FreeDrawingSurfaceInfo() are called from a different thread,
+ * this data member should be set before calling those functions.
+ */
+ JNIEnv* env;
+ /* Cached reference to the target object */
+ jobject target;
+ /*
+ * Lock the surface of the target component for native rendering.
+ * When finished drawing, the surface must be unlocked with
+ * Unlock(). This function returns a bitmask with one or more of the
+ * following values:
+ *
+ * JAWT_LOCK_ERROR - When an error has occurred and the surface could not
+ * be locked.
+ *
+ * JAWT_LOCK_CLIP_CHANGED - When the clip region has changed.
+ *
+ * JAWT_LOCK_BOUNDS_CHANGED - When the bounds of the surface have changed.
+ *
+ * JAWT_LOCK_SURFACE_CHANGED - When the surface itself has changed
+ */
+ jint (JNICALL *Lock)
+ (struct jawt_DrawingSurface* ds);
+ /*
+ * Get the drawing surface info.
+ * The value returned may be cached, but the values may change if
+ * additional calls to Lock() or Unlock() are made.
+ * Lock() must be called before this can return a valid value.
+ * Returns NULL if an error has occurred.
+ * When finished with the returned value, FreeDrawingSurfaceInfo must be
+ * called.
+ */
+ JAWT_DrawingSurfaceInfo* (JNICALL *GetDrawingSurfaceInfo)
+ (struct jawt_DrawingSurface* ds);
+ /*
+ * Free the drawing surface info.
+ */
+ void (JNICALL *FreeDrawingSurfaceInfo)
+ (JAWT_DrawingSurfaceInfo* dsi);
+ /*
+ * Unlock the drawing surface of the target component for native rendering.
+ */
+ void (JNICALL *Unlock)
+ (struct jawt_DrawingSurface* ds);
+} JAWT_DrawingSurface;
+
+/*
+ * JAWT
+ * Structure for containing native AWT functions.
+ */
+typedef struct jawt {
+ /*
+ * Version of this structure. This must always be set before
+ * calling JAWT_GetAWT()
+ */
+ jint version;
+ /*
+ * Return a drawing surface from a target jobject. This value
+ * may be cached.
+ * Returns NULL if an error has occurred.
+ * Target must be a java.awt.Component (should be a Canvas
+ * or Window for native rendering).
+ * FreeDrawingSurface() must be called when finished with the
+ * returned JAWT_DrawingSurface.
+ */
+ JAWT_DrawingSurface* (JNICALL *GetDrawingSurface)
+ (JNIEnv* env, jobject target);
+ /*
+ * Free the drawing surface allocated in GetDrawingSurface.
+ */
+ void (JNICALL *FreeDrawingSurface)
+ (JAWT_DrawingSurface* ds);
+ /*
+ * Since 1.4
+ * Locks the entire AWT for synchronization purposes
+ */
+ void (JNICALL *Lock)(JNIEnv* env);
+ /*
+ * Since 1.4
+ * Unlocks the entire AWT for synchronization purposes
+ */
+ void (JNICALL *Unlock)(JNIEnv* env);
+ /*
+ * Since 1.4
+ * Returns a reference to a java.awt.Component from a native
+ * platform handle. On Windows, this corresponds to an HWND;
+ * on Solaris and Linux, this is a Drawable. For other platforms,
+ * see the appropriate machine-dependent header file for a description.
+ * The reference returned by this function is a local
+ * reference that is only valid in this environment.
+ * This function returns a NULL reference if no component could be
+ * found with matching platform information.
+ */
+ jobject (JNICALL *GetComponent)(JNIEnv* env, void* platformInfo);
+
+} JAWT;
+
+/*
+ * Get the AWT native structure. This function returns JNI_FALSE if
+ * an error occurs.
+ */
+_JNI_IMPORT_OR_EXPORT_
+jboolean JNICALL JAWT_GetAWT(JNIEnv* env, JAWT* awt);
+
+#define JAWT_VERSION_1_3 0x00010003
+#define JAWT_VERSION_1_4 0x00010004
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !_JAVASOFT_JAWT_H_ */
diff --git a/cpp/wiiuse/include/jdwpTransport.h b/cpp/wiiuse/include/jdwpTransport.h
new file mode 100644
index 0000000..eae435a
--- /dev/null
+++ b/cpp/wiiuse/include/jdwpTransport.h
@@ -0,0 +1,237 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+/*
+ * Java Debug Wire Protocol Transport Service Provider Interface.
+ */
+
+#ifndef JDWPTRANSPORT_H
+#define JDWPTRANSPORT_H
+
+#include "jni.h"
+
+enum {
+ JDWPTRANSPORT_VERSION_1_0 = 0x00010000
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct jdwpTransportNativeInterface_;
+
+struct _jdwpTransportEnv;
+
+#ifdef __cplusplus
+typedef _jdwpTransportEnv jdwpTransportEnv;
+#else
+typedef const struct jdwpTransportNativeInterface_ *jdwpTransportEnv;
+#endif /* __cplusplus */
+
+/*
+ * Errors. Universal errors with JVMTI/JVMDI equivalents keep the
+ * values the same.
+ */
+typedef enum {
+ JDWPTRANSPORT_ERROR_NONE = 0,
+ JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT = 103,
+ JDWPTRANSPORT_ERROR_OUT_OF_MEMORY = 110,
+ JDWPTRANSPORT_ERROR_INTERNAL = 113,
+ JDWPTRANSPORT_ERROR_ILLEGAL_STATE = 201,
+ JDWPTRANSPORT_ERROR_IO_ERROR = 202,
+ JDWPTRANSPORT_ERROR_TIMEOUT = 203,
+ JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE = 204
+} jdwpTransportError;
+
+
+/*
+ * Structure to define capabilities
+ */
+typedef struct {
+ unsigned int can_timeout_attach :1;
+ unsigned int can_timeout_accept :1;
+ unsigned int can_timeout_handshake :1;
+ unsigned int reserved3 :1;
+ unsigned int reserved4 :1;
+ unsigned int reserved5 :1;
+ unsigned int reserved6 :1;
+ unsigned int reserved7 :1;
+ unsigned int reserved8 :1;
+ unsigned int reserved9 :1;
+ unsigned int reserved10 :1;
+ unsigned int reserved11 :1;
+ unsigned int reserved12 :1;
+ unsigned int reserved13 :1;
+ unsigned int reserved14 :1;
+ unsigned int reserved15 :1;
+} JDWPTransportCapabilities;
+
+
+/*
+ * Structures to define packet layout.
+ *
+ * See: http://java.sun.com/j2se/1.5/docs/guide/jpda/jdwp-spec.html
+ */
+
+enum {
+ JDWPTRANSPORT_FLAGS_NONE = 0x0,
+ JDWPTRANSPORT_FLAGS_REPLY = 0x80
+};
+
+typedef struct {
+ jint len;
+ jint id;
+ jbyte flags;
+ jbyte cmdSet;
+ jbyte cmd;
+ jbyte *data;
+} jdwpCmdPacket;
+
+typedef struct {
+ jint len;
+ jint id;
+ jbyte flags;
+ jshort errorCode;
+ jbyte *data;
+} jdwpReplyPacket;
+
+typedef struct {
+ union {
+ jdwpCmdPacket cmd;
+ jdwpReplyPacket reply;
+ } type;
+} jdwpPacket;
+
+/*
+ * JDWP functions called by the transport.
+ */
+typedef struct jdwpTransportCallback {
+ void *(*alloc)(jint numBytes); /* Call this for all allocations */
+ void (*free)(void *buffer); /* Call this for all deallocations */
+} jdwpTransportCallback;
+
+typedef jint (JNICALL *jdwpTransport_OnLoad_t)(JavaVM *jvm,
+ jdwpTransportCallback *callback,
+ jint version,
+ jdwpTransportEnv** env);
+
+
+
+/* Function Interface */
+
+struct jdwpTransportNativeInterface_ {
+ /* 1 : RESERVED */
+ void *reserved1;
+
+ /* 2 : Get Capabilities */
+ jdwpTransportError (JNICALL *GetCapabilities)(jdwpTransportEnv* env,
+ JDWPTransportCapabilities *capabilities_ptr);
+
+ /* 3 : Attach */
+ jdwpTransportError (JNICALL *Attach)(jdwpTransportEnv* env,
+ const char* address,
+ jlong attach_timeout,
+ jlong handshake_timeout);
+
+ /* 4: StartListening */
+ jdwpTransportError (JNICALL *StartListening)(jdwpTransportEnv* env,
+ const char* address,
+ char** actual_address);
+
+ /* 5: StopListening */
+ jdwpTransportError (JNICALL *StopListening)(jdwpTransportEnv* env);
+
+ /* 6: Accept */
+ jdwpTransportError (JNICALL *Accept)(jdwpTransportEnv* env,
+ jlong accept_timeout,
+ jlong handshake_timeout);
+
+ /* 7: IsOpen */
+ jboolean (JNICALL *IsOpen)(jdwpTransportEnv* env);
+
+ /* 8: Close */
+ jdwpTransportError (JNICALL *Close)(jdwpTransportEnv* env);
+
+ /* 9: ReadPacket */
+ jdwpTransportError (JNICALL *ReadPacket)(jdwpTransportEnv* env,
+ jdwpPacket *pkt);
+
+ /* 10: Write Packet */
+ jdwpTransportError (JNICALL *WritePacket)(jdwpTransportEnv* env,
+ const jdwpPacket* pkt);
+
+ /* 11: GetLastError */
+ jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env,
+ char** error);
+
+};
+
+
+/*
+ * Use inlined functions so that C++ code can use syntax such as
+ * env->Attach("mymachine:5000", 10*1000, 0);
+ *
+ * rather than using C's :-
+ *
+ * (*env)->Attach(env, "mymachine:5000", 10*1000, 0);
+ */
+struct _jdwpTransportEnv {
+ const struct jdwpTransportNativeInterface_ *functions;
+#ifdef __cplusplus
+
+ jdwpTransportError GetCapabilities(JDWPTransportCapabilities *capabilities_ptr) {
+ return functions->GetCapabilities(this, capabilities_ptr);
+ }
+
+ jdwpTransportError Attach(const char* address, jlong attach_timeout,
+ jlong handshake_timeout) {
+ return functions->Attach(this, address, attach_timeout, handshake_timeout);
+ }
+
+ jdwpTransportError StartListening(const char* address,
+ char** actual_address) {
+ return functions->StartListening(this, address, actual_address);
+ }
+
+ jdwpTransportError StopListening(void) {
+ return functions->StopListening(this);
+ }
+
+ jdwpTransportError Accept(jlong accept_timeout, jlong handshake_timeout) {
+ return functions->Accept(this, accept_timeout, handshake_timeout);
+ }
+
+ jboolean IsOpen(void) {
+ return functions->IsOpen(this);
+ }
+
+ jdwpTransportError Close(void) {
+ return functions->Close(this);
+ }
+
+ jdwpTransportError ReadPacket(jdwpPacket *pkt) {
+ return functions->ReadPacket(this, pkt);
+ }
+
+ jdwpTransportError WritePacket(const jdwpPacket* pkt) {
+ return functions->WritePacket(this, pkt);
+ }
+
+ jdwpTransportError GetLastError(char** error) {
+ return functions->GetLastError(this, error);
+ }
+
+
+#endif /* __cplusplus */
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* JDWPTRANSPORT_H */
+
diff --git a/cpp/wiiuse/include/jni.h b/cpp/wiiuse/include/jni.h
new file mode 100644
index 0000000..8ed7366
--- /dev/null
+++ b/cpp/wiiuse/include/jni.h
@@ -0,0 +1,1944 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+/*
+ * We used part of Netscape's Java Runtime Interface (JRI) as the starting
+ * point of our design and implementation.
+ */
+
+/******************************************************************************
+ * Java Runtime Interface
+ * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved.
+ *****************************************************************************/
+
+#ifndef _JAVASOFT_JNI_H_
+#define _JAVASOFT_JNI_H_
+
+#include
+#include
+
+/* jni_md.h contains the machine-dependent typedefs for jbyte, jint
+ and jlong */
+
+#include "jni_md.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * JNI Types
+ */
+
+#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H
+
+typedef unsigned char jboolean;
+typedef unsigned short jchar;
+typedef short jshort;
+typedef float jfloat;
+typedef double jdouble;
+
+typedef jint jsize;
+
+#ifdef __cplusplus
+
+class _jobject {};
+class _jclass : public _jobject {};
+class _jthrowable : public _jobject {};
+class _jstring : public _jobject {};
+class _jarray : public _jobject {};
+class _jbooleanArray : public _jarray {};
+class _jbyteArray : public _jarray {};
+class _jcharArray : public _jarray {};
+class _jshortArray : public _jarray {};
+class _jintArray : public _jarray {};
+class _jlongArray : public _jarray {};
+class _jfloatArray : public _jarray {};
+class _jdoubleArray : public _jarray {};
+class _jobjectArray : public _jarray {};
+
+typedef _jobject *jobject;
+typedef _jclass *jclass;
+typedef _jthrowable *jthrowable;
+typedef _jstring *jstring;
+typedef _jarray *jarray;
+typedef _jbooleanArray *jbooleanArray;
+typedef _jbyteArray *jbyteArray;
+typedef _jcharArray *jcharArray;
+typedef _jshortArray *jshortArray;
+typedef _jintArray *jintArray;
+typedef _jlongArray *jlongArray;
+typedef _jfloatArray *jfloatArray;
+typedef _jdoubleArray *jdoubleArray;
+typedef _jobjectArray *jobjectArray;
+
+#else
+
+struct _jobject;
+
+typedef struct _jobject *jobject;
+typedef jobject jclass;
+typedef jobject jthrowable;
+typedef jobject jstring;
+typedef jobject jarray;
+typedef jarray jbooleanArray;
+typedef jarray jbyteArray;
+typedef jarray jcharArray;
+typedef jarray jshortArray;
+typedef jarray jintArray;
+typedef jarray jlongArray;
+typedef jarray jfloatArray;
+typedef jarray jdoubleArray;
+typedef jarray jobjectArray;
+
+#endif
+
+typedef jobject jweak;
+
+typedef union jvalue {
+ jboolean z;
+ jbyte b;
+ jchar c;
+ jshort s;
+ jint i;
+ jlong j;
+ jfloat f;
+ jdouble d;
+ jobject l;
+} jvalue;
+
+struct _jfieldID;
+typedef struct _jfieldID *jfieldID;
+
+struct _jmethodID;
+typedef struct _jmethodID *jmethodID;
+
+/* Return values from jobjectRefType */
+typedef enum _jobjectType {
+ JNIInvalidRefType = 0,
+ JNILocalRefType = 1,
+ JNIGlobalRefType = 2,
+ JNIWeakGlobalRefType = 3
+} jobjectRefType;
+
+
+#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */
+
+/*
+ * jboolean constants
+ */
+
+#define JNI_FALSE 0
+#define JNI_TRUE 1
+
+/*
+ * possible return values for JNI functions.
+ */
+
+#define JNI_OK 0 /* success */
+#define JNI_ERR (-1) /* unknown error */
+#define JNI_EDETACHED (-2) /* thread detached from the VM */
+#define JNI_EVERSION (-3) /* JNI version error */
+#define JNI_ENOMEM (-4) /* not enough memory */
+#define JNI_EEXIST (-5) /* VM already created */
+#define JNI_EINVAL (-6) /* invalid arguments */
+
+/*
+ * used in ReleaseScalarArrayElements
+ */
+
+#define JNI_COMMIT 1
+#define JNI_ABORT 2
+
+/*
+ * used in RegisterNatives to describe native method name, signature,
+ * and function pointer.
+ */
+
+typedef struct {
+ char *name;
+ char *signature;
+ void *fnPtr;
+} JNINativeMethod;
+
+/*
+ * JNI Native Method Interface.
+ */
+
+struct JNINativeInterface_;
+
+struct JNIEnv_;
+
+#ifdef __cplusplus
+typedef JNIEnv_ JNIEnv;
+#else
+typedef const struct JNINativeInterface_ *JNIEnv;
+#endif
+
+/*
+ * JNI Invocation Interface.
+ */
+
+struct JNIInvokeInterface_;
+
+struct JavaVM_;
+
+#ifdef __cplusplus
+typedef JavaVM_ JavaVM;
+#else
+typedef const struct JNIInvokeInterface_ *JavaVM;
+#endif
+
+struct JNINativeInterface_ {
+ void *reserved0;
+ void *reserved1;
+ void *reserved2;
+
+ void *reserved3;
+ jint (JNICALL *GetVersion)(JNIEnv *env);
+
+ jclass (JNICALL *DefineClass)
+ (JNIEnv *env, const char *name, jobject loader, const jbyte *buf,
+ jsize len);
+ jclass (JNICALL *FindClass)
+ (JNIEnv *env, const char *name);
+
+ jmethodID (JNICALL *FromReflectedMethod)
+ (JNIEnv *env, jobject method);
+ jfieldID (JNICALL *FromReflectedField)
+ (JNIEnv *env, jobject field);
+
+ jobject (JNICALL *ToReflectedMethod)
+ (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic);
+
+ jclass (JNICALL *GetSuperclass)
+ (JNIEnv *env, jclass sub);
+ jboolean (JNICALL *IsAssignableFrom)
+ (JNIEnv *env, jclass sub, jclass sup);
+
+ jobject (JNICALL *ToReflectedField)
+ (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic);
+
+ jint (JNICALL *Throw)
+ (JNIEnv *env, jthrowable obj);
+ jint (JNICALL *ThrowNew)
+ (JNIEnv *env, jclass clazz, const char *msg);
+ jthrowable (JNICALL *ExceptionOccurred)
+ (JNIEnv *env);
+ void (JNICALL *ExceptionDescribe)
+ (JNIEnv *env);
+ void (JNICALL *ExceptionClear)
+ (JNIEnv *env);
+ void (JNICALL *FatalError)
+ (JNIEnv *env, const char *msg);
+
+ jint (JNICALL *PushLocalFrame)
+ (JNIEnv *env, jint capacity);
+ jobject (JNICALL *PopLocalFrame)
+ (JNIEnv *env, jobject result);
+
+ jobject (JNICALL *NewGlobalRef)
+ (JNIEnv *env, jobject lobj);
+ void (JNICALL *DeleteGlobalRef)
+ (JNIEnv *env, jobject gref);
+ void (JNICALL *DeleteLocalRef)
+ (JNIEnv *env, jobject obj);
+ jboolean (JNICALL *IsSameObject)
+ (JNIEnv *env, jobject obj1, jobject obj2);
+ jobject (JNICALL *NewLocalRef)
+ (JNIEnv *env, jobject ref);
+ jint (JNICALL *EnsureLocalCapacity)
+ (JNIEnv *env, jint capacity);
+
+ jobject (JNICALL *AllocObject)
+ (JNIEnv *env, jclass clazz);
+ jobject (JNICALL *NewObject)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jobject (JNICALL *NewObjectV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jobject (JNICALL *NewObjectA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jclass (JNICALL *GetObjectClass)
+ (JNIEnv *env, jobject obj);
+ jboolean (JNICALL *IsInstanceOf)
+ (JNIEnv *env, jobject obj, jclass clazz);
+
+ jmethodID (JNICALL *GetMethodID)
+ (JNIEnv *env, jclass clazz, const char *name, const char *sig);
+
+ jobject (JNICALL *CallObjectMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jobject (JNICALL *CallObjectMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jobject (JNICALL *CallObjectMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);
+
+ jboolean (JNICALL *CallBooleanMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jboolean (JNICALL *CallBooleanMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jboolean (JNICALL *CallBooleanMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);
+
+ jbyte (JNICALL *CallByteMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jbyte (JNICALL *CallByteMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jbyte (JNICALL *CallByteMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jchar (JNICALL *CallCharMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jchar (JNICALL *CallCharMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jchar (JNICALL *CallCharMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jshort (JNICALL *CallShortMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jshort (JNICALL *CallShortMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jshort (JNICALL *CallShortMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jint (JNICALL *CallIntMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jint (JNICALL *CallIntMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jint (JNICALL *CallIntMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jlong (JNICALL *CallLongMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jlong (JNICALL *CallLongMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jlong (JNICALL *CallLongMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jfloat (JNICALL *CallFloatMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jfloat (JNICALL *CallFloatMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jfloat (JNICALL *CallFloatMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jdouble (JNICALL *CallDoubleMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jdouble (JNICALL *CallDoubleMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jdouble (JNICALL *CallDoubleMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ void (JNICALL *CallVoidMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ void (JNICALL *CallVoidMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ void (JNICALL *CallVoidMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);
+
+ jobject (JNICALL *CallNonvirtualObjectMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jobject (JNICALL *CallNonvirtualObjectMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jobject (JNICALL *CallNonvirtualObjectMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue * args);
+
+ jboolean (JNICALL *CallNonvirtualBooleanMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jboolean (JNICALL *CallNonvirtualBooleanMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jboolean (JNICALL *CallNonvirtualBooleanMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue * args);
+
+ jbyte (JNICALL *CallNonvirtualByteMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jbyte (JNICALL *CallNonvirtualByteMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jbyte (JNICALL *CallNonvirtualByteMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jchar (JNICALL *CallNonvirtualCharMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jchar (JNICALL *CallNonvirtualCharMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jchar (JNICALL *CallNonvirtualCharMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jshort (JNICALL *CallNonvirtualShortMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jshort (JNICALL *CallNonvirtualShortMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jshort (JNICALL *CallNonvirtualShortMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jint (JNICALL *CallNonvirtualIntMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jint (JNICALL *CallNonvirtualIntMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jint (JNICALL *CallNonvirtualIntMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jlong (JNICALL *CallNonvirtualLongMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jlong (JNICALL *CallNonvirtualLongMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jlong (JNICALL *CallNonvirtualLongMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jfloat (JNICALL *CallNonvirtualFloatMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jfloat (JNICALL *CallNonvirtualFloatMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jfloat (JNICALL *CallNonvirtualFloatMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jdouble (JNICALL *CallNonvirtualDoubleMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jdouble (JNICALL *CallNonvirtualDoubleMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jdouble (JNICALL *CallNonvirtualDoubleMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ void (JNICALL *CallNonvirtualVoidMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ void (JNICALL *CallNonvirtualVoidMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ void (JNICALL *CallNonvirtualVoidMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue * args);
+
+ jfieldID (JNICALL *GetFieldID)
+ (JNIEnv *env, jclass clazz, const char *name, const char *sig);
+
+ jobject (JNICALL *GetObjectField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jboolean (JNICALL *GetBooleanField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jbyte (JNICALL *GetByteField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jchar (JNICALL *GetCharField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jshort (JNICALL *GetShortField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jint (JNICALL *GetIntField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jlong (JNICALL *GetLongField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jfloat (JNICALL *GetFloatField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jdouble (JNICALL *GetDoubleField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+
+ void (JNICALL *SetObjectField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val);
+ void (JNICALL *SetBooleanField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val);
+ void (JNICALL *SetByteField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val);
+ void (JNICALL *SetCharField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val);
+ void (JNICALL *SetShortField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val);
+ void (JNICALL *SetIntField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jint val);
+ void (JNICALL *SetLongField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val);
+ void (JNICALL *SetFloatField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val);
+ void (JNICALL *SetDoubleField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val);
+
+ jmethodID (JNICALL *GetStaticMethodID)
+ (JNIEnv *env, jclass clazz, const char *name, const char *sig);
+
+ jobject (JNICALL *CallStaticObjectMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jobject (JNICALL *CallStaticObjectMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jobject (JNICALL *CallStaticObjectMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jboolean (JNICALL *CallStaticBooleanMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jboolean (JNICALL *CallStaticBooleanMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jboolean (JNICALL *CallStaticBooleanMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jbyte (JNICALL *CallStaticByteMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jbyte (JNICALL *CallStaticByteMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jbyte (JNICALL *CallStaticByteMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jchar (JNICALL *CallStaticCharMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jchar (JNICALL *CallStaticCharMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jchar (JNICALL *CallStaticCharMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jshort (JNICALL *CallStaticShortMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jshort (JNICALL *CallStaticShortMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jshort (JNICALL *CallStaticShortMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jint (JNICALL *CallStaticIntMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jint (JNICALL *CallStaticIntMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jint (JNICALL *CallStaticIntMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jlong (JNICALL *CallStaticLongMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jlong (JNICALL *CallStaticLongMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jlong (JNICALL *CallStaticLongMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jfloat (JNICALL *CallStaticFloatMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jfloat (JNICALL *CallStaticFloatMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jfloat (JNICALL *CallStaticFloatMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jdouble (JNICALL *CallStaticDoubleMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jdouble (JNICALL *CallStaticDoubleMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jdouble (JNICALL *CallStaticDoubleMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ void (JNICALL *CallStaticVoidMethod)
+ (JNIEnv *env, jclass cls, jmethodID methodID, ...);
+ void (JNICALL *CallStaticVoidMethodV)
+ (JNIEnv *env, jclass cls, jmethodID methodID, va_list args);
+ void (JNICALL *CallStaticVoidMethodA)
+ (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args);
+
+ jfieldID (JNICALL *GetStaticFieldID)
+ (JNIEnv *env, jclass clazz, const char *name, const char *sig);
+ jobject (JNICALL *GetStaticObjectField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jboolean (JNICALL *GetStaticBooleanField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jbyte (JNICALL *GetStaticByteField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jchar (JNICALL *GetStaticCharField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jshort (JNICALL *GetStaticShortField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jint (JNICALL *GetStaticIntField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jlong (JNICALL *GetStaticLongField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jfloat (JNICALL *GetStaticFloatField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jdouble (JNICALL *GetStaticDoubleField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+
+ void (JNICALL *SetStaticObjectField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value);
+ void (JNICALL *SetStaticBooleanField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value);
+ void (JNICALL *SetStaticByteField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value);
+ void (JNICALL *SetStaticCharField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value);
+ void (JNICALL *SetStaticShortField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value);
+ void (JNICALL *SetStaticIntField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value);
+ void (JNICALL *SetStaticLongField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value);
+ void (JNICALL *SetStaticFloatField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value);
+ void (JNICALL *SetStaticDoubleField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value);
+
+ jstring (JNICALL *NewString)
+ (JNIEnv *env, const jchar *unicode, jsize len);
+ jsize (JNICALL *GetStringLength)
+ (JNIEnv *env, jstring str);
+ const jchar *(JNICALL *GetStringChars)
+ (JNIEnv *env, jstring str, jboolean *isCopy);
+ void (JNICALL *ReleaseStringChars)
+ (JNIEnv *env, jstring str, const jchar *chars);
+
+ jstring (JNICALL *NewStringUTF)
+ (JNIEnv *env, const char *utf);
+ jsize (JNICALL *GetStringUTFLength)
+ (JNIEnv *env, jstring str);
+ const char* (JNICALL *GetStringUTFChars)
+ (JNIEnv *env, jstring str, jboolean *isCopy);
+ void (JNICALL *ReleaseStringUTFChars)
+ (JNIEnv *env, jstring str, const char* chars);
+
+
+ jsize (JNICALL *GetArrayLength)
+ (JNIEnv *env, jarray array);
+
+ jobjectArray (JNICALL *NewObjectArray)
+ (JNIEnv *env, jsize len, jclass clazz, jobject init);
+ jobject (JNICALL *GetObjectArrayElement)
+ (JNIEnv *env, jobjectArray array, jsize index);
+ void (JNICALL *SetObjectArrayElement)
+ (JNIEnv *env, jobjectArray array, jsize index, jobject val);
+
+ jbooleanArray (JNICALL *NewBooleanArray)
+ (JNIEnv *env, jsize len);
+ jbyteArray (JNICALL *NewByteArray)
+ (JNIEnv *env, jsize len);
+ jcharArray (JNICALL *NewCharArray)
+ (JNIEnv *env, jsize len);
+ jshortArray (JNICALL *NewShortArray)
+ (JNIEnv *env, jsize len);
+ jintArray (JNICALL *NewIntArray)
+ (JNIEnv *env, jsize len);
+ jlongArray (JNICALL *NewLongArray)
+ (JNIEnv *env, jsize len);
+ jfloatArray (JNICALL *NewFloatArray)
+ (JNIEnv *env, jsize len);
+ jdoubleArray (JNICALL *NewDoubleArray)
+ (JNIEnv *env, jsize len);
+
+ jboolean * (JNICALL *GetBooleanArrayElements)
+ (JNIEnv *env, jbooleanArray array, jboolean *isCopy);
+ jbyte * (JNICALL *GetByteArrayElements)
+ (JNIEnv *env, jbyteArray array, jboolean *isCopy);
+ jchar * (JNICALL *GetCharArrayElements)
+ (JNIEnv *env, jcharArray array, jboolean *isCopy);
+ jshort * (JNICALL *GetShortArrayElements)
+ (JNIEnv *env, jshortArray array, jboolean *isCopy);
+ jint * (JNICALL *GetIntArrayElements)
+ (JNIEnv *env, jintArray array, jboolean *isCopy);
+ jlong * (JNICALL *GetLongArrayElements)
+ (JNIEnv *env, jlongArray array, jboolean *isCopy);
+ jfloat * (JNICALL *GetFloatArrayElements)
+ (JNIEnv *env, jfloatArray array, jboolean *isCopy);
+ jdouble * (JNICALL *GetDoubleArrayElements)
+ (JNIEnv *env, jdoubleArray array, jboolean *isCopy);
+
+ void (JNICALL *ReleaseBooleanArrayElements)
+ (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode);
+ void (JNICALL *ReleaseByteArrayElements)
+ (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode);
+ void (JNICALL *ReleaseCharArrayElements)
+ (JNIEnv *env, jcharArray array, jchar *elems, jint mode);
+ void (JNICALL *ReleaseShortArrayElements)
+ (JNIEnv *env, jshortArray array, jshort *elems, jint mode);
+ void (JNICALL *ReleaseIntArrayElements)
+ (JNIEnv *env, jintArray array, jint *elems, jint mode);
+ void (JNICALL *ReleaseLongArrayElements)
+ (JNIEnv *env, jlongArray array, jlong *elems, jint mode);
+ void (JNICALL *ReleaseFloatArrayElements)
+ (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode);
+ void (JNICALL *ReleaseDoubleArrayElements)
+ (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode);
+
+ void (JNICALL *GetBooleanArrayRegion)
+ (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf);
+ void (JNICALL *GetByteArrayRegion)
+ (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf);
+ void (JNICALL *GetCharArrayRegion)
+ (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf);
+ void (JNICALL *GetShortArrayRegion)
+ (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf);
+ void (JNICALL *GetIntArrayRegion)
+ (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf);
+ void (JNICALL *GetLongArrayRegion)
+ (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf);
+ void (JNICALL *GetFloatArrayRegion)
+ (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf);
+ void (JNICALL *GetDoubleArrayRegion)
+ (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf);
+
+ void (JNICALL *SetBooleanArrayRegion)
+ (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf);
+ void (JNICALL *SetByteArrayRegion)
+ (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf);
+ void (JNICALL *SetCharArrayRegion)
+ (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf);
+ void (JNICALL *SetShortArrayRegion)
+ (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf);
+ void (JNICALL *SetIntArrayRegion)
+ (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf);
+ void (JNICALL *SetLongArrayRegion)
+ (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf);
+ void (JNICALL *SetFloatArrayRegion)
+ (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf);
+ void (JNICALL *SetDoubleArrayRegion)
+ (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf);
+
+ jint (JNICALL *RegisterNatives)
+ (JNIEnv *env, jclass clazz, const JNINativeMethod *methods,
+ jint nMethods);
+ jint (JNICALL *UnregisterNatives)
+ (JNIEnv *env, jclass clazz);
+
+ jint (JNICALL *MonitorEnter)
+ (JNIEnv *env, jobject obj);
+ jint (JNICALL *MonitorExit)
+ (JNIEnv *env, jobject obj);
+
+ jint (JNICALL *GetJavaVM)
+ (JNIEnv *env, JavaVM **vm);
+
+ void (JNICALL *GetStringRegion)
+ (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);
+ void (JNICALL *GetStringUTFRegion)
+ (JNIEnv *env, jstring str, jsize start, jsize len, char *buf);
+
+ void * (JNICALL *GetPrimitiveArrayCritical)
+ (JNIEnv *env, jarray array, jboolean *isCopy);
+ void (JNICALL *ReleasePrimitiveArrayCritical)
+ (JNIEnv *env, jarray array, void *carray, jint mode);
+
+ const jchar * (JNICALL *GetStringCritical)
+ (JNIEnv *env, jstring string, jboolean *isCopy);
+ void (JNICALL *ReleaseStringCritical)
+ (JNIEnv *env, jstring string, const jchar *cstring);
+
+ jweak (JNICALL *NewWeakGlobalRef)
+ (JNIEnv *env, jobject obj);
+ void (JNICALL *DeleteWeakGlobalRef)
+ (JNIEnv *env, jweak ref);
+
+ jboolean (JNICALL *ExceptionCheck)
+ (JNIEnv *env);
+
+ jobject (JNICALL *NewDirectByteBuffer)
+ (JNIEnv* env, void* address, jlong capacity);
+ void* (JNICALL *GetDirectBufferAddress)
+ (JNIEnv* env, jobject buf);
+ jlong (JNICALL *GetDirectBufferCapacity)
+ (JNIEnv* env, jobject buf);
+
+ /* New JNI 1.6 Features */
+
+ jobjectRefType (JNICALL *GetObjectRefType)
+ (JNIEnv* env, jobject obj);
+};
+
+/*
+ * We use inlined functions for C++ so that programmers can write:
+ *
+ * env->FindClass("java/lang/String")
+ *
+ * in C++ rather than:
+ *
+ * (*env)->FindClass(env, "java/lang/String")
+ *
+ * in C.
+ */
+
+struct JNIEnv_ {
+ const struct JNINativeInterface_ *functions;
+#ifdef __cplusplus
+
+ jint GetVersion() {
+ return functions->GetVersion(this);
+ }
+ jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
+ jsize len) {
+ return functions->DefineClass(this, name, loader, buf, len);
+ }
+ jclass FindClass(const char *name) {
+ return functions->FindClass(this, name);
+ }
+ jmethodID FromReflectedMethod(jobject method) {
+ return functions->FromReflectedMethod(this,method);
+ }
+ jfieldID FromReflectedField(jobject field) {
+ return functions->FromReflectedField(this,field);
+ }
+
+ jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) {
+ return functions->ToReflectedMethod(this, cls, methodID, isStatic);
+ }
+
+ jclass GetSuperclass(jclass sub) {
+ return functions->GetSuperclass(this, sub);
+ }
+ jboolean IsAssignableFrom(jclass sub, jclass sup) {
+ return functions->IsAssignableFrom(this, sub, sup);
+ }
+
+ jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) {
+ return functions->ToReflectedField(this,cls,fieldID,isStatic);
+ }
+
+ jint Throw(jthrowable obj) {
+ return functions->Throw(this, obj);
+ }
+ jint ThrowNew(jclass clazz, const char *msg) {
+ return functions->ThrowNew(this, clazz, msg);
+ }
+ jthrowable ExceptionOccurred() {
+ return functions->ExceptionOccurred(this);
+ }
+ void ExceptionDescribe() {
+ functions->ExceptionDescribe(this);
+ }
+ void ExceptionClear() {
+ functions->ExceptionClear(this);
+ }
+ void FatalError(const char *msg) {
+ functions->FatalError(this, msg);
+ }
+
+ jint PushLocalFrame(jint capacity) {
+ return functions->PushLocalFrame(this,capacity);
+ }
+ jobject PopLocalFrame(jobject result) {
+ return functions->PopLocalFrame(this,result);
+ }
+
+ jobject NewGlobalRef(jobject lobj) {
+ return functions->NewGlobalRef(this,lobj);
+ }
+ void DeleteGlobalRef(jobject gref) {
+ functions->DeleteGlobalRef(this,gref);
+ }
+ void DeleteLocalRef(jobject obj) {
+ functions->DeleteLocalRef(this, obj);
+ }
+
+ jboolean IsSameObject(jobject obj1, jobject obj2) {
+ return functions->IsSameObject(this,obj1,obj2);
+ }
+
+ jobject NewLocalRef(jobject ref) {
+ return functions->NewLocalRef(this,ref);
+ }
+ jint EnsureLocalCapacity(jint capacity) {
+ return functions->EnsureLocalCapacity(this,capacity);
+ }
+
+ jobject AllocObject(jclass clazz) {
+ return functions->AllocObject(this,clazz);
+ }
+ jobject NewObject(jclass clazz, jmethodID methodID, ...) {
+ va_list args;
+ jobject result;
+ va_start(args, methodID);
+ result = functions->NewObjectV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jobject NewObjectV(jclass clazz, jmethodID methodID,
+ va_list args) {
+ return functions->NewObjectV(this,clazz,methodID,args);
+ }
+ jobject NewObjectA(jclass clazz, jmethodID methodID,
+ const jvalue *args) {
+ return functions->NewObjectA(this,clazz,methodID,args);
+ }
+
+ jclass GetObjectClass(jobject obj) {
+ return functions->GetObjectClass(this,obj);
+ }
+ jboolean IsInstanceOf(jobject obj, jclass clazz) {
+ return functions->IsInstanceOf(this,obj,clazz);
+ }
+
+ jmethodID GetMethodID(jclass clazz, const char *name,
+ const char *sig) {
+ return functions->GetMethodID(this,clazz,name,sig);
+ }
+
+ jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jobject result;
+ va_start(args,methodID);
+ result = functions->CallObjectMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jobject CallObjectMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallObjectMethodV(this,obj,methodID,args);
+ }
+ jobject CallObjectMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallObjectMethodA(this,obj,methodID,args);
+ }
+
+ jboolean CallBooleanMethod(jobject obj,
+ jmethodID methodID, ...) {
+ va_list args;
+ jboolean result;
+ va_start(args,methodID);
+ result = functions->CallBooleanMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jboolean CallBooleanMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallBooleanMethodV(this,obj,methodID,args);
+ }
+ jboolean CallBooleanMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallBooleanMethodA(this,obj,methodID, args);
+ }
+
+ jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jbyte result;
+ va_start(args,methodID);
+ result = functions->CallByteMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jbyte CallByteMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallByteMethodV(this,obj,methodID,args);
+ }
+ jbyte CallByteMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallByteMethodA(this,obj,methodID,args);
+ }
+
+ jchar CallCharMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jchar result;
+ va_start(args,methodID);
+ result = functions->CallCharMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jchar CallCharMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallCharMethodV(this,obj,methodID,args);
+ }
+ jchar CallCharMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallCharMethodA(this,obj,methodID,args);
+ }
+
+ jshort CallShortMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jshort result;
+ va_start(args,methodID);
+ result = functions->CallShortMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jshort CallShortMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallShortMethodV(this,obj,methodID,args);
+ }
+ jshort CallShortMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallShortMethodA(this,obj,methodID,args);
+ }
+
+ jint CallIntMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jint result;
+ va_start(args,methodID);
+ result = functions->CallIntMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jint CallIntMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallIntMethodV(this,obj,methodID,args);
+ }
+ jint CallIntMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallIntMethodA(this,obj,methodID,args);
+ }
+
+ jlong CallLongMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jlong result;
+ va_start(args,methodID);
+ result = functions->CallLongMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jlong CallLongMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallLongMethodV(this,obj,methodID,args);
+ }
+ jlong CallLongMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallLongMethodA(this,obj,methodID,args);
+ }
+
+ jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jfloat result;
+ va_start(args,methodID);
+ result = functions->CallFloatMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jfloat CallFloatMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallFloatMethodV(this,obj,methodID,args);
+ }
+ jfloat CallFloatMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallFloatMethodA(this,obj,methodID,args);
+ }
+
+ jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jdouble result;
+ va_start(args,methodID);
+ result = functions->CallDoubleMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jdouble CallDoubleMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallDoubleMethodV(this,obj,methodID,args);
+ }
+ jdouble CallDoubleMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallDoubleMethodA(this,obj,methodID,args);
+ }
+
+ void CallVoidMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ va_start(args,methodID);
+ functions->CallVoidMethodV(this,obj,methodID,args);
+ va_end(args);
+ }
+ void CallVoidMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ functions->CallVoidMethodV(this,obj,methodID,args);
+ }
+ void CallVoidMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ functions->CallVoidMethodA(this,obj,methodID,args);
+ }
+
+ jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jobject result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualObjectMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualObjectMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualObjectMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jboolean result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualBooleanMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualBooleanMethodA(this,obj,clazz,
+ methodID, args);
+ }
+
+ jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jbyte result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualByteMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualByteMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualByteMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jchar CallNonvirtualCharMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jchar result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualCharMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualCharMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualCharMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jshort CallNonvirtualShortMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jshort result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualShortMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualShortMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualShortMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jint CallNonvirtualIntMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jint result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualIntMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jint CallNonvirtualIntMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualIntMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jint CallNonvirtualIntMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualIntMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jlong CallNonvirtualLongMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jlong result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualLongMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualLongMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualLongMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jfloat result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualFloatMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz,
+ jmethodID methodID,
+ va_list args) {
+ return functions->CallNonvirtualFloatMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz,
+ jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallNonvirtualFloatMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jdouble result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz,
+ jmethodID methodID,
+ va_list args) {
+ return functions->CallNonvirtualDoubleMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz,
+ jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallNonvirtualDoubleMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ void CallNonvirtualVoidMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ va_start(args,methodID);
+ functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args);
+ va_end(args);
+ }
+ void CallNonvirtualVoidMethodV(jobject obj, jclass clazz,
+ jmethodID methodID,
+ va_list args) {
+ functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args);
+ }
+ void CallNonvirtualVoidMethodA(jobject obj, jclass clazz,
+ jmethodID methodID,
+ const jvalue * args) {
+ functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args);
+ }
+
+ jfieldID GetFieldID(jclass clazz, const char *name,
+ const char *sig) {
+ return functions->GetFieldID(this,clazz,name,sig);
+ }
+
+ jobject GetObjectField(jobject obj, jfieldID fieldID) {
+ return functions->GetObjectField(this,obj,fieldID);
+ }
+ jboolean GetBooleanField(jobject obj, jfieldID fieldID) {
+ return functions->GetBooleanField(this,obj,fieldID);
+ }
+ jbyte GetByteField(jobject obj, jfieldID fieldID) {
+ return functions->GetByteField(this,obj,fieldID);
+ }
+ jchar GetCharField(jobject obj, jfieldID fieldID) {
+ return functions->GetCharField(this,obj,fieldID);
+ }
+ jshort GetShortField(jobject obj, jfieldID fieldID) {
+ return functions->GetShortField(this,obj,fieldID);
+ }
+ jint GetIntField(jobject obj, jfieldID fieldID) {
+ return functions->GetIntField(this,obj,fieldID);
+ }
+ jlong GetLongField(jobject obj, jfieldID fieldID) {
+ return functions->GetLongField(this,obj,fieldID);
+ }
+ jfloat GetFloatField(jobject obj, jfieldID fieldID) {
+ return functions->GetFloatField(this,obj,fieldID);
+ }
+ jdouble GetDoubleField(jobject obj, jfieldID fieldID) {
+ return functions->GetDoubleField(this,obj,fieldID);
+ }
+
+ void SetObjectField(jobject obj, jfieldID fieldID, jobject val) {
+ functions->SetObjectField(this,obj,fieldID,val);
+ }
+ void SetBooleanField(jobject obj, jfieldID fieldID,
+ jboolean val) {
+ functions->SetBooleanField(this,obj,fieldID,val);
+ }
+ void SetByteField(jobject obj, jfieldID fieldID,
+ jbyte val) {
+ functions->SetByteField(this,obj,fieldID,val);
+ }
+ void SetCharField(jobject obj, jfieldID fieldID,
+ jchar val) {
+ functions->SetCharField(this,obj,fieldID,val);
+ }
+ void SetShortField(jobject obj, jfieldID fieldID,
+ jshort val) {
+ functions->SetShortField(this,obj,fieldID,val);
+ }
+ void SetIntField(jobject obj, jfieldID fieldID,
+ jint val) {
+ functions->SetIntField(this,obj,fieldID,val);
+ }
+ void SetLongField(jobject obj, jfieldID fieldID,
+ jlong val) {
+ functions->SetLongField(this,obj,fieldID,val);
+ }
+ void SetFloatField(jobject obj, jfieldID fieldID,
+ jfloat val) {
+ functions->SetFloatField(this,obj,fieldID,val);
+ }
+ void SetDoubleField(jobject obj, jfieldID fieldID,
+ jdouble val) {
+ functions->SetDoubleField(this,obj,fieldID,val);
+ }
+
+ jmethodID GetStaticMethodID(jclass clazz, const char *name,
+ const char *sig) {
+ return functions->GetStaticMethodID(this,clazz,name,sig);
+ }
+
+ jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID,
+ ...) {
+ va_list args;
+ jobject result;
+ va_start(args,methodID);
+ result = functions->CallStaticObjectMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID,
+ va_list args) {
+ return functions->CallStaticObjectMethodV(this,clazz,methodID,args);
+ }
+ jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID,
+ const jvalue *args) {
+ return functions->CallStaticObjectMethodA(this,clazz,methodID,args);
+ }
+
+ jboolean CallStaticBooleanMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jboolean result;
+ va_start(args,methodID);
+ result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jboolean CallStaticBooleanMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticBooleanMethodV(this,clazz,methodID,args);
+ }
+ jboolean CallStaticBooleanMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticBooleanMethodA(this,clazz,methodID,args);
+ }
+
+ jbyte CallStaticByteMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jbyte result;
+ va_start(args,methodID);
+ result = functions->CallStaticByteMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jbyte CallStaticByteMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticByteMethodV(this,clazz,methodID,args);
+ }
+ jbyte CallStaticByteMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticByteMethodA(this,clazz,methodID,args);
+ }
+
+ jchar CallStaticCharMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jchar result;
+ va_start(args,methodID);
+ result = functions->CallStaticCharMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jchar CallStaticCharMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticCharMethodV(this,clazz,methodID,args);
+ }
+ jchar CallStaticCharMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticCharMethodA(this,clazz,methodID,args);
+ }
+
+ jshort CallStaticShortMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jshort result;
+ va_start(args,methodID);
+ result = functions->CallStaticShortMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jshort CallStaticShortMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticShortMethodV(this,clazz,methodID,args);
+ }
+ jshort CallStaticShortMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticShortMethodA(this,clazz,methodID,args);
+ }
+
+ jint CallStaticIntMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jint result;
+ va_start(args,methodID);
+ result = functions->CallStaticIntMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jint CallStaticIntMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticIntMethodV(this,clazz,methodID,args);
+ }
+ jint CallStaticIntMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticIntMethodA(this,clazz,methodID,args);
+ }
+
+ jlong CallStaticLongMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jlong result;
+ va_start(args,methodID);
+ result = functions->CallStaticLongMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jlong CallStaticLongMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticLongMethodV(this,clazz,methodID,args);
+ }
+ jlong CallStaticLongMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticLongMethodA(this,clazz,methodID,args);
+ }
+
+ jfloat CallStaticFloatMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jfloat result;
+ va_start(args,methodID);
+ result = functions->CallStaticFloatMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jfloat CallStaticFloatMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticFloatMethodV(this,clazz,methodID,args);
+ }
+ jfloat CallStaticFloatMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticFloatMethodA(this,clazz,methodID,args);
+ }
+
+ jdouble CallStaticDoubleMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jdouble result;
+ va_start(args,methodID);
+ result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jdouble CallStaticDoubleMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticDoubleMethodV(this,clazz,methodID,args);
+ }
+ jdouble CallStaticDoubleMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticDoubleMethodA(this,clazz,methodID,args);
+ }
+
+ void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) {
+ va_list args;
+ va_start(args,methodID);
+ functions->CallStaticVoidMethodV(this,cls,methodID,args);
+ va_end(args);
+ }
+ void CallStaticVoidMethodV(jclass cls, jmethodID methodID,
+ va_list args) {
+ functions->CallStaticVoidMethodV(this,cls,methodID,args);
+ }
+ void CallStaticVoidMethodA(jclass cls, jmethodID methodID,
+ const jvalue * args) {
+ functions->CallStaticVoidMethodA(this,cls,methodID,args);
+ }
+
+ jfieldID GetStaticFieldID(jclass clazz, const char *name,
+ const char *sig) {
+ return functions->GetStaticFieldID(this,clazz,name,sig);
+ }
+ jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticObjectField(this,clazz,fieldID);
+ }
+ jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticBooleanField(this,clazz,fieldID);
+ }
+ jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticByteField(this,clazz,fieldID);
+ }
+ jchar GetStaticCharField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticCharField(this,clazz,fieldID);
+ }
+ jshort GetStaticShortField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticShortField(this,clazz,fieldID);
+ }
+ jint GetStaticIntField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticIntField(this,clazz,fieldID);
+ }
+ jlong GetStaticLongField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticLongField(this,clazz,fieldID);
+ }
+ jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticFloatField(this,clazz,fieldID);
+ }
+ jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticDoubleField(this,clazz,fieldID);
+ }
+
+ void SetStaticObjectField(jclass clazz, jfieldID fieldID,
+ jobject value) {
+ functions->SetStaticObjectField(this,clazz,fieldID,value);
+ }
+ void SetStaticBooleanField(jclass clazz, jfieldID fieldID,
+ jboolean value) {
+ functions->SetStaticBooleanField(this,clazz,fieldID,value);
+ }
+ void SetStaticByteField(jclass clazz, jfieldID fieldID,
+ jbyte value) {
+ functions->SetStaticByteField(this,clazz,fieldID,value);
+ }
+ void SetStaticCharField(jclass clazz, jfieldID fieldID,
+ jchar value) {
+ functions->SetStaticCharField(this,clazz,fieldID,value);
+ }
+ void SetStaticShortField(jclass clazz, jfieldID fieldID,
+ jshort value) {
+ functions->SetStaticShortField(this,clazz,fieldID,value);
+ }
+ void SetStaticIntField(jclass clazz, jfieldID fieldID,
+ jint value) {
+ functions->SetStaticIntField(this,clazz,fieldID,value);
+ }
+ void SetStaticLongField(jclass clazz, jfieldID fieldID,
+ jlong value) {
+ functions->SetStaticLongField(this,clazz,fieldID,value);
+ }
+ void SetStaticFloatField(jclass clazz, jfieldID fieldID,
+ jfloat value) {
+ functions->SetStaticFloatField(this,clazz,fieldID,value);
+ }
+ void SetStaticDoubleField(jclass clazz, jfieldID fieldID,
+ jdouble value) {
+ functions->SetStaticDoubleField(this,clazz,fieldID,value);
+ }
+
+ jstring NewString(const jchar *unicode, jsize len) {
+ return functions->NewString(this,unicode,len);
+ }
+ jsize GetStringLength(jstring str) {
+ return functions->GetStringLength(this,str);
+ }
+ const jchar *GetStringChars(jstring str, jboolean *isCopy) {
+ return functions->GetStringChars(this,str,isCopy);
+ }
+ void ReleaseStringChars(jstring str, const jchar *chars) {
+ functions->ReleaseStringChars(this,str,chars);
+ }
+
+ jstring NewStringUTF(const char *utf) {
+ return functions->NewStringUTF(this,utf);
+ }
+ jsize GetStringUTFLength(jstring str) {
+ return functions->GetStringUTFLength(this,str);
+ }
+ const char* GetStringUTFChars(jstring str, jboolean *isCopy) {
+ return functions->GetStringUTFChars(this,str,isCopy);
+ }
+ void ReleaseStringUTFChars(jstring str, const char* chars) {
+ functions->ReleaseStringUTFChars(this,str,chars);
+ }
+
+ jsize GetArrayLength(jarray array) {
+ return functions->GetArrayLength(this,array);
+ }
+
+ jobjectArray NewObjectArray(jsize len, jclass clazz,
+ jobject init) {
+ return functions->NewObjectArray(this,len,clazz,init);
+ }
+ jobject GetObjectArrayElement(jobjectArray array, jsize index) {
+ return functions->GetObjectArrayElement(this,array,index);
+ }
+ void SetObjectArrayElement(jobjectArray array, jsize index,
+ jobject val) {
+ functions->SetObjectArrayElement(this,array,index,val);
+ }
+
+ jbooleanArray NewBooleanArray(jsize len) {
+ return functions->NewBooleanArray(this,len);
+ }
+ jbyteArray NewByteArray(jsize len) {
+ return functions->NewByteArray(this,len);
+ }
+ jcharArray NewCharArray(jsize len) {
+ return functions->NewCharArray(this,len);
+ }
+ jshortArray NewShortArray(jsize len) {
+ return functions->NewShortArray(this,len);
+ }
+ jintArray NewIntArray(jsize len) {
+ return functions->NewIntArray(this,len);
+ }
+ jlongArray NewLongArray(jsize len) {
+ return functions->NewLongArray(this,len);
+ }
+ jfloatArray NewFloatArray(jsize len) {
+ return functions->NewFloatArray(this,len);
+ }
+ jdoubleArray NewDoubleArray(jsize len) {
+ return functions->NewDoubleArray(this,len);
+ }
+
+ jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) {
+ return functions->GetBooleanArrayElements(this,array,isCopy);
+ }
+ jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) {
+ return functions->GetByteArrayElements(this,array,isCopy);
+ }
+ jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) {
+ return functions->GetCharArrayElements(this,array,isCopy);
+ }
+ jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) {
+ return functions->GetShortArrayElements(this,array,isCopy);
+ }
+ jint * GetIntArrayElements(jintArray array, jboolean *isCopy) {
+ return functions->GetIntArrayElements(this,array,isCopy);
+ }
+ jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) {
+ return functions->GetLongArrayElements(this,array,isCopy);
+ }
+ jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) {
+ return functions->GetFloatArrayElements(this,array,isCopy);
+ }
+ jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) {
+ return functions->GetDoubleArrayElements(this,array,isCopy);
+ }
+
+ void ReleaseBooleanArrayElements(jbooleanArray array,
+ jboolean *elems,
+ jint mode) {
+ functions->ReleaseBooleanArrayElements(this,array,elems,mode);
+ }
+ void ReleaseByteArrayElements(jbyteArray array,
+ jbyte *elems,
+ jint mode) {
+ functions->ReleaseByteArrayElements(this,array,elems,mode);
+ }
+ void ReleaseCharArrayElements(jcharArray array,
+ jchar *elems,
+ jint mode) {
+ functions->ReleaseCharArrayElements(this,array,elems,mode);
+ }
+ void ReleaseShortArrayElements(jshortArray array,
+ jshort *elems,
+ jint mode) {
+ functions->ReleaseShortArrayElements(this,array,elems,mode);
+ }
+ void ReleaseIntArrayElements(jintArray array,
+ jint *elems,
+ jint mode) {
+ functions->ReleaseIntArrayElements(this,array,elems,mode);
+ }
+ void ReleaseLongArrayElements(jlongArray array,
+ jlong *elems,
+ jint mode) {
+ functions->ReleaseLongArrayElements(this,array,elems,mode);
+ }
+ void ReleaseFloatArrayElements(jfloatArray array,
+ jfloat *elems,
+ jint mode) {
+ functions->ReleaseFloatArrayElements(this,array,elems,mode);
+ }
+ void ReleaseDoubleArrayElements(jdoubleArray array,
+ jdouble *elems,
+ jint mode) {
+ functions->ReleaseDoubleArrayElements(this,array,elems,mode);
+ }
+
+ void GetBooleanArrayRegion(jbooleanArray array,
+ jsize start, jsize len, jboolean *buf) {
+ functions->GetBooleanArrayRegion(this,array,start,len,buf);
+ }
+ void GetByteArrayRegion(jbyteArray array,
+ jsize start, jsize len, jbyte *buf) {
+ functions->GetByteArrayRegion(this,array,start,len,buf);
+ }
+ void GetCharArrayRegion(jcharArray array,
+ jsize start, jsize len, jchar *buf) {
+ functions->GetCharArrayRegion(this,array,start,len,buf);
+ }
+ void GetShortArrayRegion(jshortArray array,
+ jsize start, jsize len, jshort *buf) {
+ functions->GetShortArrayRegion(this,array,start,len,buf);
+ }
+ void GetIntArrayRegion(jintArray array,
+ jsize start, jsize len, jint *buf) {
+ functions->GetIntArrayRegion(this,array,start,len,buf);
+ }
+ void GetLongArrayRegion(jlongArray array,
+ jsize start, jsize len, jlong *buf) {
+ functions->GetLongArrayRegion(this,array,start,len,buf);
+ }
+ void GetFloatArrayRegion(jfloatArray array,
+ jsize start, jsize len, jfloat *buf) {
+ functions->GetFloatArrayRegion(this,array,start,len,buf);
+ }
+ void GetDoubleArrayRegion(jdoubleArray array,
+ jsize start, jsize len, jdouble *buf) {
+ functions->GetDoubleArrayRegion(this,array,start,len,buf);
+ }
+
+ void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
+ const jboolean *buf) {
+ functions->SetBooleanArrayRegion(this,array,start,len,buf);
+ }
+ void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,
+ const jbyte *buf) {
+ functions->SetByteArrayRegion(this,array,start,len,buf);
+ }
+ void SetCharArrayRegion(jcharArray array, jsize start, jsize len,
+ const jchar *buf) {
+ functions->SetCharArrayRegion(this,array,start,len,buf);
+ }
+ void SetShortArrayRegion(jshortArray array, jsize start, jsize len,
+ const jshort *buf) {
+ functions->SetShortArrayRegion(this,array,start,len,buf);
+ }
+ void SetIntArrayRegion(jintArray array, jsize start, jsize len,
+ const jint *buf) {
+ functions->SetIntArrayRegion(this,array,start,len,buf);
+ }
+ void SetLongArrayRegion(jlongArray array, jsize start, jsize len,
+ const jlong *buf) {
+ functions->SetLongArrayRegion(this,array,start,len,buf);
+ }
+ void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
+ const jfloat *buf) {
+ functions->SetFloatArrayRegion(this,array,start,len,buf);
+ }
+ void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
+ const jdouble *buf) {
+ functions->SetDoubleArrayRegion(this,array,start,len,buf);
+ }
+
+ jint RegisterNatives(jclass clazz, const JNINativeMethod *methods,
+ jint nMethods) {
+ return functions->RegisterNatives(this,clazz,methods,nMethods);
+ }
+ jint UnregisterNatives(jclass clazz) {
+ return functions->UnregisterNatives(this,clazz);
+ }
+
+ jint MonitorEnter(jobject obj) {
+ return functions->MonitorEnter(this,obj);
+ }
+ jint MonitorExit(jobject obj) {
+ return functions->MonitorExit(this,obj);
+ }
+
+ jint GetJavaVM(JavaVM **vm) {
+ return functions->GetJavaVM(this,vm);
+ }
+
+ void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) {
+ functions->GetStringRegion(this,str,start,len,buf);
+ }
+ void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) {
+ functions->GetStringUTFRegion(this,str,start,len,buf);
+ }
+
+ void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) {
+ return functions->GetPrimitiveArrayCritical(this,array,isCopy);
+ }
+ void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) {
+ functions->ReleasePrimitiveArrayCritical(this,array,carray,mode);
+ }
+
+ const jchar * GetStringCritical(jstring string, jboolean *isCopy) {
+ return functions->GetStringCritical(this,string,isCopy);
+ }
+ void ReleaseStringCritical(jstring string, const jchar *cstring) {
+ functions->ReleaseStringCritical(this,string,cstring);
+ }
+
+ jweak NewWeakGlobalRef(jobject obj) {
+ return functions->NewWeakGlobalRef(this,obj);
+ }
+ void DeleteWeakGlobalRef(jweak ref) {
+ functions->DeleteWeakGlobalRef(this,ref);
+ }
+
+ jboolean ExceptionCheck() {
+ return functions->ExceptionCheck(this);
+ }
+
+ jobject NewDirectByteBuffer(void* address, jlong capacity) {
+ return functions->NewDirectByteBuffer(this, address, capacity);
+ }
+ void* GetDirectBufferAddress(jobject buf) {
+ return functions->GetDirectBufferAddress(this, buf);
+ }
+ jlong GetDirectBufferCapacity(jobject buf) {
+ return functions->GetDirectBufferCapacity(this, buf);
+ }
+ jobjectRefType GetObjectRefType(jobject obj) {
+ return functions->GetObjectRefType(this, obj);
+ }
+
+#endif /* __cplusplus */
+};
+
+typedef struct JavaVMOption {
+ char *optionString;
+ void *extraInfo;
+} JavaVMOption;
+
+typedef struct JavaVMInitArgs {
+ jint version;
+
+ jint nOptions;
+ JavaVMOption *options;
+ jboolean ignoreUnrecognized;
+} JavaVMInitArgs;
+
+typedef struct JavaVMAttachArgs {
+ jint version;
+
+ char *name;
+ jobject group;
+} JavaVMAttachArgs;
+
+/* These will be VM-specific. */
+
+#define JDK1_2
+#define JDK1_4
+
+/* End VM-specific. */
+
+struct JNIInvokeInterface_ {
+ void *reserved0;
+ void *reserved1;
+ void *reserved2;
+
+ jint (JNICALL *DestroyJavaVM)(JavaVM *vm);
+
+ jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args);
+
+ jint (JNICALL *DetachCurrentThread)(JavaVM *vm);
+
+ jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version);
+
+ jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args);
+};
+
+struct JavaVM_ {
+ const struct JNIInvokeInterface_ *functions;
+#ifdef __cplusplus
+
+ jint DestroyJavaVM() {
+ return functions->DestroyJavaVM(this);
+ }
+ jint AttachCurrentThread(void **penv, void *args) {
+ return functions->AttachCurrentThread(this, penv, args);
+ }
+ jint DetachCurrentThread() {
+ return functions->DetachCurrentThread(this);
+ }
+
+ jint GetEnv(void **penv, jint version) {
+ return functions->GetEnv(this, penv, version);
+ }
+ jint AttachCurrentThreadAsDaemon(void **penv, void *args) {
+ return functions->AttachCurrentThreadAsDaemon(this, penv, args);
+ }
+#endif
+};
+
+#ifdef _JNI_IMPLEMENTATION_
+#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT
+#else
+#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT
+#endif
+_JNI_IMPORT_OR_EXPORT_ jint JNICALL
+JNI_GetDefaultJavaVMInitArgs(void *args);
+
+_JNI_IMPORT_OR_EXPORT_ jint JNICALL
+JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args);
+
+_JNI_IMPORT_OR_EXPORT_ jint JNICALL
+JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *);
+
+/* Defined by native libraries. */
+JNIEXPORT jint JNICALL
+JNI_OnLoad(JavaVM *vm, void *reserved);
+
+JNIEXPORT void JNICALL
+JNI_OnUnload(JavaVM *vm, void *reserved);
+
+#define JNI_VERSION_1_1 0x00010001
+#define JNI_VERSION_1_2 0x00010002
+#define JNI_VERSION_1_4 0x00010004
+#define JNI_VERSION_1_6 0x00010006
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* !_JAVASOFT_JNI_H_ */
+
+
+
diff --git a/cpp/wiiuse/include/jvmti.h b/cpp/wiiuse/include/jvmti.h
new file mode 100644
index 0000000..865f21e
--- /dev/null
+++ b/cpp/wiiuse/include/jvmti.h
@@ -0,0 +1,2504 @@
+#ifdef USE_PRAGMA_IDENT_HDR
+#pragma ident "@(#)jvmtiLib.xsl 1.38 06/08/02 23:22:31 JVM"
+#endif
+/*
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+ /* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */
+
+
+ /* Include file for the Java(tm) Virtual Machine Tool Interface */
+
+#ifndef _JAVA_JVMTI_H_
+#define _JAVA_JVMTI_H_
+
+#include "jni.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ JVMTI_VERSION_1 = 0x30010000,
+ JVMTI_VERSION_1_0 = 0x30010000,
+ JVMTI_VERSION_1_1 = 0x30010100,
+
+ JVMTI_VERSION = 0x30000000 + (1 * 0x10000) + (1 * 0x100) + 102 /* version: 1.1.102 */
+};
+
+JNIEXPORT jint JNICALL
+Agent_OnLoad(JavaVM *vm, char *options, void *reserved);
+
+JNIEXPORT jint JNICALL
+Agent_OnAttach(JavaVM* vm, char* options, void* reserved);
+
+JNIEXPORT void JNICALL
+Agent_OnUnload(JavaVM *vm);
+
+ /* Forward declaration of the environment */
+
+struct _jvmtiEnv;
+
+struct jvmtiInterface_1_;
+
+#ifdef __cplusplus
+typedef _jvmtiEnv jvmtiEnv;
+#else
+typedef const struct jvmtiInterface_1_ *jvmtiEnv;
+#endif /* __cplusplus */
+
+/* Derived Base Types */
+
+typedef jobject jthread;
+typedef jobject jthreadGroup;
+typedef jlong jlocation;
+struct _jrawMonitorID;
+typedef struct _jrawMonitorID *jrawMonitorID;
+typedef struct JNINativeInterface_ jniNativeInterface;
+
+ /* Constants */
+
+
+ /* Thread State Flags */
+
+enum {
+ JVMTI_THREAD_STATE_ALIVE = 0x0001,
+ JVMTI_THREAD_STATE_TERMINATED = 0x0002,
+ JVMTI_THREAD_STATE_RUNNABLE = 0x0004,
+ JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400,
+ JVMTI_THREAD_STATE_WAITING = 0x0080,
+ JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010,
+ JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020,
+ JVMTI_THREAD_STATE_SLEEPING = 0x0040,
+ JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100,
+ JVMTI_THREAD_STATE_PARKED = 0x0200,
+ JVMTI_THREAD_STATE_SUSPENDED = 0x100000,
+ JVMTI_THREAD_STATE_INTERRUPTED = 0x200000,
+ JVMTI_THREAD_STATE_IN_NATIVE = 0x400000,
+ JVMTI_THREAD_STATE_VENDOR_1 = 0x10000000,
+ JVMTI_THREAD_STATE_VENDOR_2 = 0x20000000,
+ JVMTI_THREAD_STATE_VENDOR_3 = 0x40000000
+};
+
+ /* java.lang.Thread.State Conversion Masks */
+
+enum {
+ JVMTI_JAVA_LANG_THREAD_STATE_MASK = JVMTI_THREAD_STATE_TERMINATED | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT,
+ JVMTI_JAVA_LANG_THREAD_STATE_NEW = 0,
+ JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED = JVMTI_THREAD_STATE_TERMINATED,
+ JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE,
+ JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER,
+ JVMTI_JAVA_LANG_THREAD_STATE_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY,
+ JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT
+};
+
+ /* Thread Priority Constants */
+
+enum {
+ JVMTI_THREAD_MIN_PRIORITY = 1,
+ JVMTI_THREAD_NORM_PRIORITY = 5,
+ JVMTI_THREAD_MAX_PRIORITY = 10
+};
+
+ /* Heap Filter Flags */
+
+enum {
+ JVMTI_HEAP_FILTER_TAGGED = 0x4,
+ JVMTI_HEAP_FILTER_UNTAGGED = 0x8,
+ JVMTI_HEAP_FILTER_CLASS_TAGGED = 0x10,
+ JVMTI_HEAP_FILTER_CLASS_UNTAGGED = 0x20
+};
+
+ /* Heap Visit Control Flags */
+
+enum {
+ JVMTI_VISIT_OBJECTS = 0x100,
+ JVMTI_VISIT_ABORT = 0x8000
+};
+
+ /* Heap Reference Enumeration */
+
+typedef enum {
+ JVMTI_HEAP_REFERENCE_CLASS = 1,
+ JVMTI_HEAP_REFERENCE_FIELD = 2,
+ JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT = 3,
+ JVMTI_HEAP_REFERENCE_CLASS_LOADER = 4,
+ JVMTI_HEAP_REFERENCE_SIGNERS = 5,
+ JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN = 6,
+ JVMTI_HEAP_REFERENCE_INTERFACE = 7,
+ JVMTI_HEAP_REFERENCE_STATIC_FIELD = 8,
+ JVMTI_HEAP_REFERENCE_CONSTANT_POOL = 9,
+ JVMTI_HEAP_REFERENCE_SUPERCLASS = 10,
+ JVMTI_HEAP_REFERENCE_JNI_GLOBAL = 21,
+ JVMTI_HEAP_REFERENCE_SYSTEM_CLASS = 22,
+ JVMTI_HEAP_REFERENCE_MONITOR = 23,
+ JVMTI_HEAP_REFERENCE_STACK_LOCAL = 24,
+ JVMTI_HEAP_REFERENCE_JNI_LOCAL = 25,
+ JVMTI_HEAP_REFERENCE_THREAD = 26,
+ JVMTI_HEAP_REFERENCE_OTHER = 27
+} jvmtiHeapReferenceKind;
+
+ /* Primitive Type Enumeration */
+
+typedef enum {
+ JVMTI_PRIMITIVE_TYPE_BOOLEAN = 90,
+ JVMTI_PRIMITIVE_TYPE_BYTE = 66,
+ JVMTI_PRIMITIVE_TYPE_CHAR = 67,
+ JVMTI_PRIMITIVE_TYPE_SHORT = 83,
+ JVMTI_PRIMITIVE_TYPE_INT = 73,
+ JVMTI_PRIMITIVE_TYPE_LONG = 74,
+ JVMTI_PRIMITIVE_TYPE_FLOAT = 70,
+ JVMTI_PRIMITIVE_TYPE_DOUBLE = 68
+} jvmtiPrimitiveType;
+
+ /* Heap Object Filter Enumeration */
+
+typedef enum {
+ JVMTI_HEAP_OBJECT_TAGGED = 1,
+ JVMTI_HEAP_OBJECT_UNTAGGED = 2,
+ JVMTI_HEAP_OBJECT_EITHER = 3
+} jvmtiHeapObjectFilter;
+
+ /* Heap Root Kind Enumeration */
+
+typedef enum {
+ JVMTI_HEAP_ROOT_JNI_GLOBAL = 1,
+ JVMTI_HEAP_ROOT_SYSTEM_CLASS = 2,
+ JVMTI_HEAP_ROOT_MONITOR = 3,
+ JVMTI_HEAP_ROOT_STACK_LOCAL = 4,
+ JVMTI_HEAP_ROOT_JNI_LOCAL = 5,
+ JVMTI_HEAP_ROOT_THREAD = 6,
+ JVMTI_HEAP_ROOT_OTHER = 7
+} jvmtiHeapRootKind;
+
+ /* Object Reference Enumeration */
+
+typedef enum {
+ JVMTI_REFERENCE_CLASS = 1,
+ JVMTI_REFERENCE_FIELD = 2,
+ JVMTI_REFERENCE_ARRAY_ELEMENT = 3,
+ JVMTI_REFERENCE_CLASS_LOADER = 4,
+ JVMTI_REFERENCE_SIGNERS = 5,
+ JVMTI_REFERENCE_PROTECTION_DOMAIN = 6,
+ JVMTI_REFERENCE_INTERFACE = 7,
+ JVMTI_REFERENCE_STATIC_FIELD = 8,
+ JVMTI_REFERENCE_CONSTANT_POOL = 9
+} jvmtiObjectReferenceKind;
+
+ /* Iteration Control Enumeration */
+
+typedef enum {
+ JVMTI_ITERATION_CONTINUE = 1,
+ JVMTI_ITERATION_IGNORE = 2,
+ JVMTI_ITERATION_ABORT = 0
+} jvmtiIterationControl;
+
+ /* Class Status Flags */
+
+enum {
+ JVMTI_CLASS_STATUS_VERIFIED = 1,
+ JVMTI_CLASS_STATUS_PREPARED = 2,
+ JVMTI_CLASS_STATUS_INITIALIZED = 4,
+ JVMTI_CLASS_STATUS_ERROR = 8,
+ JVMTI_CLASS_STATUS_ARRAY = 16,
+ JVMTI_CLASS_STATUS_PRIMITIVE = 32
+};
+
+ /* Event Enable/Disable */
+
+typedef enum {
+ JVMTI_ENABLE = 1,
+ JVMTI_DISABLE = 0
+} jvmtiEventMode;
+
+ /* Extension Function/Event Parameter Types */
+
+typedef enum {
+ JVMTI_TYPE_JBYTE = 101,
+ JVMTI_TYPE_JCHAR = 102,
+ JVMTI_TYPE_JSHORT = 103,
+ JVMTI_TYPE_JINT = 104,
+ JVMTI_TYPE_JLONG = 105,
+ JVMTI_TYPE_JFLOAT = 106,
+ JVMTI_TYPE_JDOUBLE = 107,
+ JVMTI_TYPE_JBOOLEAN = 108,
+ JVMTI_TYPE_JOBJECT = 109,
+ JVMTI_TYPE_JTHREAD = 110,
+ JVMTI_TYPE_JCLASS = 111,
+ JVMTI_TYPE_JVALUE = 112,
+ JVMTI_TYPE_JFIELDID = 113,
+ JVMTI_TYPE_JMETHODID = 114,
+ JVMTI_TYPE_CCHAR = 115,
+ JVMTI_TYPE_CVOID = 116,
+ JVMTI_TYPE_JNIENV = 117
+} jvmtiParamTypes;
+
+ /* Extension Function/Event Parameter Kinds */
+
+typedef enum {
+ JVMTI_KIND_IN = 91,
+ JVMTI_KIND_IN_PTR = 92,
+ JVMTI_KIND_IN_BUF = 93,
+ JVMTI_KIND_ALLOC_BUF = 94,
+ JVMTI_KIND_ALLOC_ALLOC_BUF = 95,
+ JVMTI_KIND_OUT = 96,
+ JVMTI_KIND_OUT_BUF = 97
+} jvmtiParamKind;
+
+ /* Timer Kinds */
+
+typedef enum {
+ JVMTI_TIMER_USER_CPU = 30,
+ JVMTI_TIMER_TOTAL_CPU = 31,
+ JVMTI_TIMER_ELAPSED = 32
+} jvmtiTimerKind;
+
+ /* Phases of execution */
+
+typedef enum {
+ JVMTI_PHASE_ONLOAD = 1,
+ JVMTI_PHASE_PRIMORDIAL = 2,
+ JVMTI_PHASE_START = 6,
+ JVMTI_PHASE_LIVE = 4,
+ JVMTI_PHASE_DEAD = 8
+} jvmtiPhase;
+
+ /* Version Interface Types */
+
+enum {
+ JVMTI_VERSION_INTERFACE_JNI = 0x00000000,
+ JVMTI_VERSION_INTERFACE_JVMTI = 0x30000000
+};
+
+ /* Version Masks */
+
+enum {
+ JVMTI_VERSION_MASK_INTERFACE_TYPE = 0x70000000,
+ JVMTI_VERSION_MASK_MAJOR = 0x0FFF0000,
+ JVMTI_VERSION_MASK_MINOR = 0x0000FF00,
+ JVMTI_VERSION_MASK_MICRO = 0x000000FF
+};
+
+ /* Version Shifts */
+
+enum {
+ JVMTI_VERSION_SHIFT_MAJOR = 16,
+ JVMTI_VERSION_SHIFT_MINOR = 8,
+ JVMTI_VERSION_SHIFT_MICRO = 0
+};
+
+ /* Verbose Flag Enumeration */
+
+typedef enum {
+ JVMTI_VERBOSE_OTHER = 0,
+ JVMTI_VERBOSE_GC = 1,
+ JVMTI_VERBOSE_CLASS = 2,
+ JVMTI_VERBOSE_JNI = 4
+} jvmtiVerboseFlag;
+
+ /* JLocation Format Enumeration */
+
+typedef enum {
+ JVMTI_JLOCATION_JVMBCI = 1,
+ JVMTI_JLOCATION_MACHINEPC = 2,
+ JVMTI_JLOCATION_OTHER = 0
+} jvmtiJlocationFormat;
+
+ /* Resource Exhaustion Flags */
+
+enum {
+ JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR = 0x0001,
+ JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP = 0x0002,
+ JVMTI_RESOURCE_EXHAUSTED_THREADS = 0x0004
+};
+
+ /* Errors */
+
+typedef enum {
+ JVMTI_ERROR_NONE = 0,
+ JVMTI_ERROR_INVALID_THREAD = 10,
+ JVMTI_ERROR_INVALID_THREAD_GROUP = 11,
+ JVMTI_ERROR_INVALID_PRIORITY = 12,
+ JVMTI_ERROR_THREAD_NOT_SUSPENDED = 13,
+ JVMTI_ERROR_THREAD_SUSPENDED = 14,
+ JVMTI_ERROR_THREAD_NOT_ALIVE = 15,
+ JVMTI_ERROR_INVALID_OBJECT = 20,
+ JVMTI_ERROR_INVALID_CLASS = 21,
+ JVMTI_ERROR_CLASS_NOT_PREPARED = 22,
+ JVMTI_ERROR_INVALID_METHODID = 23,
+ JVMTI_ERROR_INVALID_LOCATION = 24,
+ JVMTI_ERROR_INVALID_FIELDID = 25,
+ JVMTI_ERROR_NO_MORE_FRAMES = 31,
+ JVMTI_ERROR_OPAQUE_FRAME = 32,
+ JVMTI_ERROR_TYPE_MISMATCH = 34,
+ JVMTI_ERROR_INVALID_SLOT = 35,
+ JVMTI_ERROR_DUPLICATE = 40,
+ JVMTI_ERROR_NOT_FOUND = 41,
+ JVMTI_ERROR_INVALID_MONITOR = 50,
+ JVMTI_ERROR_NOT_MONITOR_OWNER = 51,
+ JVMTI_ERROR_INTERRUPT = 52,
+ JVMTI_ERROR_INVALID_CLASS_FORMAT = 60,
+ JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION = 61,
+ JVMTI_ERROR_FAILS_VERIFICATION = 62,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED = 63,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED = 64,
+ JVMTI_ERROR_INVALID_TYPESTATE = 65,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED = 66,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED = 67,
+ JVMTI_ERROR_UNSUPPORTED_VERSION = 68,
+ JVMTI_ERROR_NAMES_DONT_MATCH = 69,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED = 70,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED = 71,
+ JVMTI_ERROR_UNMODIFIABLE_CLASS = 79,
+ JVMTI_ERROR_NOT_AVAILABLE = 98,
+ JVMTI_ERROR_MUST_POSSESS_CAPABILITY = 99,
+ JVMTI_ERROR_NULL_POINTER = 100,
+ JVMTI_ERROR_ABSENT_INFORMATION = 101,
+ JVMTI_ERROR_INVALID_EVENT_TYPE = 102,
+ JVMTI_ERROR_ILLEGAL_ARGUMENT = 103,
+ JVMTI_ERROR_NATIVE_METHOD = 104,
+ JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED = 106,
+ JVMTI_ERROR_OUT_OF_MEMORY = 110,
+ JVMTI_ERROR_ACCESS_DENIED = 111,
+ JVMTI_ERROR_WRONG_PHASE = 112,
+ JVMTI_ERROR_INTERNAL = 113,
+ JVMTI_ERROR_UNATTACHED_THREAD = 115,
+ JVMTI_ERROR_INVALID_ENVIRONMENT = 116,
+ JVMTI_ERROR_MAX = 116
+} jvmtiError;
+
+ /* Event IDs */
+
+typedef enum {
+ JVMTI_MIN_EVENT_TYPE_VAL = 50,
+ JVMTI_EVENT_VM_INIT = 50,
+ JVMTI_EVENT_VM_DEATH = 51,
+ JVMTI_EVENT_THREAD_START = 52,
+ JVMTI_EVENT_THREAD_END = 53,
+ JVMTI_EVENT_CLASS_FILE_LOAD_HOOK = 54,
+ JVMTI_EVENT_CLASS_LOAD = 55,
+ JVMTI_EVENT_CLASS_PREPARE = 56,
+ JVMTI_EVENT_VM_START = 57,
+ JVMTI_EVENT_EXCEPTION = 58,
+ JVMTI_EVENT_EXCEPTION_CATCH = 59,
+ JVMTI_EVENT_SINGLE_STEP = 60,
+ JVMTI_EVENT_FRAME_POP = 61,
+ JVMTI_EVENT_BREAKPOINT = 62,
+ JVMTI_EVENT_FIELD_ACCESS = 63,
+ JVMTI_EVENT_FIELD_MODIFICATION = 64,
+ JVMTI_EVENT_METHOD_ENTRY = 65,
+ JVMTI_EVENT_METHOD_EXIT = 66,
+ JVMTI_EVENT_NATIVE_METHOD_BIND = 67,
+ JVMTI_EVENT_COMPILED_METHOD_LOAD = 68,
+ JVMTI_EVENT_COMPILED_METHOD_UNLOAD = 69,
+ JVMTI_EVENT_DYNAMIC_CODE_GENERATED = 70,
+ JVMTI_EVENT_DATA_DUMP_REQUEST = 71,
+ JVMTI_EVENT_MONITOR_WAIT = 73,
+ JVMTI_EVENT_MONITOR_WAITED = 74,
+ JVMTI_EVENT_MONITOR_CONTENDED_ENTER = 75,
+ JVMTI_EVENT_MONITOR_CONTENDED_ENTERED = 76,
+ JVMTI_EVENT_RESOURCE_EXHAUSTED = 80,
+ JVMTI_EVENT_GARBAGE_COLLECTION_START = 81,
+ JVMTI_EVENT_GARBAGE_COLLECTION_FINISH = 82,
+ JVMTI_EVENT_OBJECT_FREE = 83,
+ JVMTI_EVENT_VM_OBJECT_ALLOC = 84,
+ JVMTI_MAX_EVENT_TYPE_VAL = 84
+} jvmtiEvent;
+
+
+ /* Pre-Declarations */
+struct _jvmtiThreadInfo;
+typedef struct _jvmtiThreadInfo jvmtiThreadInfo;
+struct _jvmtiMonitorStackDepthInfo;
+typedef struct _jvmtiMonitorStackDepthInfo jvmtiMonitorStackDepthInfo;
+struct _jvmtiThreadGroupInfo;
+typedef struct _jvmtiThreadGroupInfo jvmtiThreadGroupInfo;
+struct _jvmtiFrameInfo;
+typedef struct _jvmtiFrameInfo jvmtiFrameInfo;
+struct _jvmtiStackInfo;
+typedef struct _jvmtiStackInfo jvmtiStackInfo;
+struct _jvmtiHeapReferenceInfoField;
+typedef struct _jvmtiHeapReferenceInfoField jvmtiHeapReferenceInfoField;
+struct _jvmtiHeapReferenceInfoArray;
+typedef struct _jvmtiHeapReferenceInfoArray jvmtiHeapReferenceInfoArray;
+struct _jvmtiHeapReferenceInfoConstantPool;
+typedef struct _jvmtiHeapReferenceInfoConstantPool jvmtiHeapReferenceInfoConstantPool;
+struct _jvmtiHeapReferenceInfoStackLocal;
+typedef struct _jvmtiHeapReferenceInfoStackLocal jvmtiHeapReferenceInfoStackLocal;
+struct _jvmtiHeapReferenceInfoJniLocal;
+typedef struct _jvmtiHeapReferenceInfoJniLocal jvmtiHeapReferenceInfoJniLocal;
+struct _jvmtiHeapReferenceInfoReserved;
+typedef struct _jvmtiHeapReferenceInfoReserved jvmtiHeapReferenceInfoReserved;
+union _jvmtiHeapReferenceInfo;
+typedef union _jvmtiHeapReferenceInfo jvmtiHeapReferenceInfo;
+struct _jvmtiHeapCallbacks;
+typedef struct _jvmtiHeapCallbacks jvmtiHeapCallbacks;
+struct _jvmtiClassDefinition;
+typedef struct _jvmtiClassDefinition jvmtiClassDefinition;
+struct _jvmtiMonitorUsage;
+typedef struct _jvmtiMonitorUsage jvmtiMonitorUsage;
+struct _jvmtiLineNumberEntry;
+typedef struct _jvmtiLineNumberEntry jvmtiLineNumberEntry;
+struct _jvmtiLocalVariableEntry;
+typedef struct _jvmtiLocalVariableEntry jvmtiLocalVariableEntry;
+struct _jvmtiParamInfo;
+typedef struct _jvmtiParamInfo jvmtiParamInfo;
+struct _jvmtiExtensionFunctionInfo;
+typedef struct _jvmtiExtensionFunctionInfo jvmtiExtensionFunctionInfo;
+struct _jvmtiExtensionEventInfo;
+typedef struct _jvmtiExtensionEventInfo jvmtiExtensionEventInfo;
+struct _jvmtiTimerInfo;
+typedef struct _jvmtiTimerInfo jvmtiTimerInfo;
+struct _jvmtiAddrLocationMap;
+typedef struct _jvmtiAddrLocationMap jvmtiAddrLocationMap;
+
+ /* Function Types */
+
+typedef void (JNICALL *jvmtiStartFunction)
+ (jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg);
+
+typedef jint (JNICALL *jvmtiHeapIterationCallback)
+ (jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data);
+
+typedef jint (JNICALL *jvmtiHeapReferenceCallback)
+ (jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info, jlong class_tag, jlong referrer_class_tag, jlong size, jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data);
+
+typedef jint (JNICALL *jvmtiPrimitiveFieldCallback)
+ (jvmtiHeapReferenceKind kind, const jvmtiHeapReferenceInfo* info, jlong object_class_tag, jlong* object_tag_ptr, jvalue value, jvmtiPrimitiveType value_type, void* user_data);
+
+typedef jint (JNICALL *jvmtiArrayPrimitiveValueCallback)
+ (jlong class_tag, jlong size, jlong* tag_ptr, jint element_count, jvmtiPrimitiveType element_type, const void* elements, void* user_data);
+
+typedef jint (JNICALL *jvmtiStringPrimitiveValueCallback)
+ (jlong class_tag, jlong size, jlong* tag_ptr, const jchar* value, jint value_length, void* user_data);
+
+typedef jint (JNICALL *jvmtiReservedCallback)
+ ();
+
+typedef jvmtiIterationControl (JNICALL *jvmtiHeapObjectCallback)
+ (jlong class_tag, jlong size, jlong* tag_ptr, void* user_data);
+
+typedef jvmtiIterationControl (JNICALL *jvmtiHeapRootCallback)
+ (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, void* user_data);
+
+typedef jvmtiIterationControl (JNICALL *jvmtiStackReferenceCallback)
+ (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong thread_tag, jint depth, jmethodID method, jint slot, void* user_data);
+
+typedef jvmtiIterationControl (JNICALL *jvmtiObjectReferenceCallback)
+ (jvmtiObjectReferenceKind reference_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong referrer_tag, jint referrer_index, void* user_data);
+
+typedef jvmtiError (JNICALL *jvmtiExtensionFunction)
+ (jvmtiEnv* jvmti_env, ...);
+
+typedef void (JNICALL *jvmtiExtensionEvent)
+ (jvmtiEnv* jvmti_env, ...);
+
+
+ /* Structure Types */
+struct _jvmtiThreadInfo {
+ char* name;
+ jint priority;
+ jboolean is_daemon;
+ jthreadGroup thread_group;
+ jobject context_class_loader;
+};
+struct _jvmtiMonitorStackDepthInfo {
+ jobject monitor;
+ jint stack_depth;
+};
+struct _jvmtiThreadGroupInfo {
+ jthreadGroup parent;
+ char* name;
+ jint max_priority;
+ jboolean is_daemon;
+};
+struct _jvmtiFrameInfo {
+ jmethodID method;
+ jlocation location;
+};
+struct _jvmtiStackInfo {
+ jthread thread;
+ jint state;
+ jvmtiFrameInfo* frame_buffer;
+ jint frame_count;
+};
+struct _jvmtiHeapReferenceInfoField {
+ jint index;
+};
+struct _jvmtiHeapReferenceInfoArray {
+ jint index;
+};
+struct _jvmtiHeapReferenceInfoConstantPool {
+ jint index;
+};
+struct _jvmtiHeapReferenceInfoStackLocal {
+ jlong thread_tag;
+ jlong thread_id;
+ jint depth;
+ jmethodID method;
+ jlocation location;
+ jint slot;
+};
+struct _jvmtiHeapReferenceInfoJniLocal {
+ jlong thread_tag;
+ jlong thread_id;
+ jint depth;
+ jmethodID method;
+};
+struct _jvmtiHeapReferenceInfoReserved {
+ jlong reserved1;
+ jlong reserved2;
+ jlong reserved3;
+ jlong reserved4;
+ jlong reserved5;
+ jlong reserved6;
+ jlong reserved7;
+ jlong reserved8;
+};
+union _jvmtiHeapReferenceInfo {
+ jvmtiHeapReferenceInfoField field;
+ jvmtiHeapReferenceInfoArray array;
+ jvmtiHeapReferenceInfoConstantPool constant_pool;
+ jvmtiHeapReferenceInfoStackLocal stack_local;
+ jvmtiHeapReferenceInfoJniLocal jni_local;
+ jvmtiHeapReferenceInfoReserved other;
+};
+struct _jvmtiHeapCallbacks {
+ jvmtiHeapIterationCallback heap_iteration_callback;
+ jvmtiHeapReferenceCallback heap_reference_callback;
+ jvmtiPrimitiveFieldCallback primitive_field_callback;
+ jvmtiArrayPrimitiveValueCallback array_primitive_value_callback;
+ jvmtiStringPrimitiveValueCallback string_primitive_value_callback;
+ jvmtiReservedCallback reserved5;
+ jvmtiReservedCallback reserved6;
+ jvmtiReservedCallback reserved7;
+ jvmtiReservedCallback reserved8;
+ jvmtiReservedCallback reserved9;
+ jvmtiReservedCallback reserved10;
+ jvmtiReservedCallback reserved11;
+ jvmtiReservedCallback reserved12;
+ jvmtiReservedCallback reserved13;
+ jvmtiReservedCallback reserved14;
+ jvmtiReservedCallback reserved15;
+};
+struct _jvmtiClassDefinition {
+ jclass klass;
+ jint class_byte_count;
+ const unsigned char* class_bytes;
+};
+struct _jvmtiMonitorUsage {
+ jthread owner;
+ jint entry_count;
+ jint waiter_count;
+ jthread* waiters;
+ jint notify_waiter_count;
+ jthread* notify_waiters;
+};
+struct _jvmtiLineNumberEntry {
+ jlocation start_location;
+ jint line_number;
+};
+struct _jvmtiLocalVariableEntry {
+ jlocation start_location;
+ jint length;
+ char* name;
+ char* signature;
+ char* generic_signature;
+ jint slot;
+};
+struct _jvmtiParamInfo {
+ char* name;
+ jvmtiParamKind kind;
+ jvmtiParamTypes base_type;
+ jboolean null_ok;
+};
+struct _jvmtiExtensionFunctionInfo {
+ jvmtiExtensionFunction func;
+ char* id;
+ char* short_description;
+ jint param_count;
+ jvmtiParamInfo* params;
+ jint error_count;
+ jvmtiError* errors;
+};
+struct _jvmtiExtensionEventInfo {
+ jint extension_event_index;
+ char* id;
+ char* short_description;
+ jint param_count;
+ jvmtiParamInfo* params;
+};
+struct _jvmtiTimerInfo {
+ jlong max_value;
+ jboolean may_skip_forward;
+ jboolean may_skip_backward;
+ jvmtiTimerKind kind;
+ jlong reserved1;
+ jlong reserved2;
+};
+struct _jvmtiAddrLocationMap {
+ const void* start_address;
+ jlocation location;
+};
+
+typedef struct {
+ unsigned int can_tag_objects : 1;
+ unsigned int can_generate_field_modification_events : 1;
+ unsigned int can_generate_field_access_events : 1;
+ unsigned int can_get_bytecodes : 1;
+ unsigned int can_get_synthetic_attribute : 1;
+ unsigned int can_get_owned_monitor_info : 1;
+ unsigned int can_get_current_contended_monitor : 1;
+ unsigned int can_get_monitor_info : 1;
+ unsigned int can_pop_frame : 1;
+ unsigned int can_redefine_classes : 1;
+ unsigned int can_signal_thread : 1;
+ unsigned int can_get_source_file_name : 1;
+ unsigned int can_get_line_numbers : 1;
+ unsigned int can_get_source_debug_extension : 1;
+ unsigned int can_access_local_variables : 1;
+ unsigned int can_maintain_original_method_order : 1;
+ unsigned int can_generate_single_step_events : 1;
+ unsigned int can_generate_exception_events : 1;
+ unsigned int can_generate_frame_pop_events : 1;
+ unsigned int can_generate_breakpoint_events : 1;
+ unsigned int can_suspend : 1;
+ unsigned int can_redefine_any_class : 1;
+ unsigned int can_get_current_thread_cpu_time : 1;
+ unsigned int can_get_thread_cpu_time : 1;
+ unsigned int can_generate_method_entry_events : 1;
+ unsigned int can_generate_method_exit_events : 1;
+ unsigned int can_generate_all_class_hook_events : 1;
+ unsigned int can_generate_compiled_method_load_events : 1;
+ unsigned int can_generate_monitor_events : 1;
+ unsigned int can_generate_vm_object_alloc_events : 1;
+ unsigned int can_generate_native_method_bind_events : 1;
+ unsigned int can_generate_garbage_collection_events : 1;
+ unsigned int can_generate_object_free_events : 1;
+ unsigned int can_force_early_return : 1;
+ unsigned int can_get_owned_monitor_stack_depth_info : 1;
+ unsigned int can_get_constant_pool : 1;
+ unsigned int can_set_native_method_prefix : 1;
+ unsigned int can_retransform_classes : 1;
+ unsigned int can_retransform_any_class : 1;
+ unsigned int can_generate_resource_exhaustion_heap_events : 1;
+ unsigned int can_generate_resource_exhaustion_threads_events : 1;
+ unsigned int : 7;
+ unsigned int : 16;
+ unsigned int : 16;
+ unsigned int : 16;
+ unsigned int : 16;
+ unsigned int : 16;
+} jvmtiCapabilities;
+
+
+ /* Event Definitions */
+
+typedef void (JNICALL *jvmtiEventReserved)(void);
+
+
+typedef void (JNICALL *jvmtiEventBreakpoint)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location);
+
+typedef void (JNICALL *jvmtiEventClassFileLoadHook)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data);
+
+typedef void (JNICALL *jvmtiEventClassLoad)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jclass klass);
+
+typedef void (JNICALL *jvmtiEventClassPrepare)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jclass klass);
+
+typedef void (JNICALL *jvmtiEventCompiledMethodLoad)
+ (jvmtiEnv *jvmti_env,
+ jmethodID method,
+ jint code_size,
+ const void* code_addr,
+ jint map_length,
+ const jvmtiAddrLocationMap* map,
+ const void* compile_info);
+
+typedef void (JNICALL *jvmtiEventCompiledMethodUnload)
+ (jvmtiEnv *jvmti_env,
+ jmethodID method,
+ const void* code_addr);
+
+typedef void (JNICALL *jvmtiEventDataDumpRequest)
+ (jvmtiEnv *jvmti_env);
+
+typedef void (JNICALL *jvmtiEventDynamicCodeGenerated)
+ (jvmtiEnv *jvmti_env,
+ const char* name,
+ const void* address,
+ jint length);
+
+typedef void (JNICALL *jvmtiEventException)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location,
+ jobject exception,
+ jmethodID catch_method,
+ jlocation catch_location);
+
+typedef void (JNICALL *jvmtiEventExceptionCatch)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location,
+ jobject exception);
+
+typedef void (JNICALL *jvmtiEventFieldAccess)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location,
+ jclass field_klass,
+ jobject object,
+ jfieldID field);
+
+typedef void (JNICALL *jvmtiEventFieldModification)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location,
+ jclass field_klass,
+ jobject object,
+ jfieldID field,
+ char signature_type,
+ jvalue new_value);
+
+typedef void (JNICALL *jvmtiEventFramePop)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jboolean was_popped_by_exception);
+
+typedef void (JNICALL *jvmtiEventGarbageCollectionFinish)
+ (jvmtiEnv *jvmti_env);
+
+typedef void (JNICALL *jvmtiEventGarbageCollectionStart)
+ (jvmtiEnv *jvmti_env);
+
+typedef void (JNICALL *jvmtiEventMethodEntry)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method);
+
+typedef void (JNICALL *jvmtiEventMethodExit)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jboolean was_popped_by_exception,
+ jvalue return_value);
+
+typedef void (JNICALL *jvmtiEventMonitorContendedEnter)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jobject object);
+
+typedef void (JNICALL *jvmtiEventMonitorContendedEntered)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jobject object);
+
+typedef void (JNICALL *jvmtiEventMonitorWait)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jobject object,
+ jlong timeout);
+
+typedef void (JNICALL *jvmtiEventMonitorWaited)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jobject object,
+ jboolean timed_out);
+
+typedef void (JNICALL *jvmtiEventNativeMethodBind)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ void* address,
+ void** new_address_ptr);
+
+typedef void (JNICALL *jvmtiEventObjectFree)
+ (jvmtiEnv *jvmti_env,
+ jlong tag);
+
+typedef void (JNICALL *jvmtiEventResourceExhausted)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jint flags,
+ const void* reserved,
+ const char* description);
+
+typedef void (JNICALL *jvmtiEventSingleStep)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location);
+
+typedef void (JNICALL *jvmtiEventThreadEnd)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread);
+
+typedef void (JNICALL *jvmtiEventThreadStart)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread);
+
+typedef void (JNICALL *jvmtiEventVMDeath)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env);
+
+typedef void (JNICALL *jvmtiEventVMInit)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread);
+
+typedef void (JNICALL *jvmtiEventVMObjectAlloc)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jobject object,
+ jclass object_klass,
+ jlong size);
+
+typedef void (JNICALL *jvmtiEventVMStart)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env);
+
+ /* Event Callback Structure */
+
+typedef struct {
+ /* 50 : VM Initialization Event */
+ jvmtiEventVMInit VMInit;
+ /* 51 : VM Death Event */
+ jvmtiEventVMDeath VMDeath;
+ /* 52 : Thread Start */
+ jvmtiEventThreadStart ThreadStart;
+ /* 53 : Thread End */
+ jvmtiEventThreadEnd ThreadEnd;
+ /* 54 : Class File Load Hook */
+ jvmtiEventClassFileLoadHook ClassFileLoadHook;
+ /* 55 : Class Load */
+ jvmtiEventClassLoad ClassLoad;
+ /* 56 : Class Prepare */
+ jvmtiEventClassPrepare ClassPrepare;
+ /* 57 : VM Start Event */
+ jvmtiEventVMStart VMStart;
+ /* 58 : Exception */
+ jvmtiEventException Exception;
+ /* 59 : Exception Catch */
+ jvmtiEventExceptionCatch ExceptionCatch;
+ /* 60 : Single Step */
+ jvmtiEventSingleStep SingleStep;
+ /* 61 : Frame Pop */
+ jvmtiEventFramePop FramePop;
+ /* 62 : Breakpoint */
+ jvmtiEventBreakpoint Breakpoint;
+ /* 63 : Field Access */
+ jvmtiEventFieldAccess FieldAccess;
+ /* 64 : Field Modification */
+ jvmtiEventFieldModification FieldModification;
+ /* 65 : Method Entry */
+ jvmtiEventMethodEntry MethodEntry;
+ /* 66 : Method Exit */
+ jvmtiEventMethodExit MethodExit;
+ /* 67 : Native Method Bind */
+ jvmtiEventNativeMethodBind NativeMethodBind;
+ /* 68 : Compiled Method Load */
+ jvmtiEventCompiledMethodLoad CompiledMethodLoad;
+ /* 69 : Compiled Method Unload */
+ jvmtiEventCompiledMethodUnload CompiledMethodUnload;
+ /* 70 : Dynamic Code Generated */
+ jvmtiEventDynamicCodeGenerated DynamicCodeGenerated;
+ /* 71 : Data Dump Request */
+ jvmtiEventDataDumpRequest DataDumpRequest;
+ /* 72 */
+ jvmtiEventReserved reserved72;
+ /* 73 : Monitor Wait */
+ jvmtiEventMonitorWait MonitorWait;
+ /* 74 : Monitor Waited */
+ jvmtiEventMonitorWaited MonitorWaited;
+ /* 75 : Monitor Contended Enter */
+ jvmtiEventMonitorContendedEnter MonitorContendedEnter;
+ /* 76 : Monitor Contended Entered */
+ jvmtiEventMonitorContendedEntered MonitorContendedEntered;
+ /* 77 */
+ jvmtiEventReserved reserved77;
+ /* 78 */
+ jvmtiEventReserved reserved78;
+ /* 79 */
+ jvmtiEventReserved reserved79;
+ /* 80 : Resource Exhausted */
+ jvmtiEventResourceExhausted ResourceExhausted;
+ /* 81 : Garbage Collection Start */
+ jvmtiEventGarbageCollectionStart GarbageCollectionStart;
+ /* 82 : Garbage Collection Finish */
+ jvmtiEventGarbageCollectionFinish GarbageCollectionFinish;
+ /* 83 : Object Free */
+ jvmtiEventObjectFree ObjectFree;
+ /* 84 : VM Object Allocation */
+ jvmtiEventVMObjectAlloc VMObjectAlloc;
+} jvmtiEventCallbacks;
+
+
+ /* Function Interface */
+
+typedef struct jvmtiInterface_1_ {
+
+ /* 1 : RESERVED */
+ void *reserved1;
+
+ /* 2 : Set Event Notification Mode */
+ jvmtiError (JNICALL *SetEventNotificationMode) (jvmtiEnv* env,
+ jvmtiEventMode mode,
+ jvmtiEvent event_type,
+ jthread event_thread,
+ ...);
+
+ /* 3 : RESERVED */
+ void *reserved3;
+
+ /* 4 : Get All Threads */
+ jvmtiError (JNICALL *GetAllThreads) (jvmtiEnv* env,
+ jint* threads_count_ptr,
+ jthread** threads_ptr);
+
+ /* 5 : Suspend Thread */
+ jvmtiError (JNICALL *SuspendThread) (jvmtiEnv* env,
+ jthread thread);
+
+ /* 6 : Resume Thread */
+ jvmtiError (JNICALL *ResumeThread) (jvmtiEnv* env,
+ jthread thread);
+
+ /* 7 : Stop Thread */
+ jvmtiError (JNICALL *StopThread) (jvmtiEnv* env,
+ jthread thread,
+ jobject exception);
+
+ /* 8 : Interrupt Thread */
+ jvmtiError (JNICALL *InterruptThread) (jvmtiEnv* env,
+ jthread thread);
+
+ /* 9 : Get Thread Info */
+ jvmtiError (JNICALL *GetThreadInfo) (jvmtiEnv* env,
+ jthread thread,
+ jvmtiThreadInfo* info_ptr);
+
+ /* 10 : Get Owned Monitor Info */
+ jvmtiError (JNICALL *GetOwnedMonitorInfo) (jvmtiEnv* env,
+ jthread thread,
+ jint* owned_monitor_count_ptr,
+ jobject** owned_monitors_ptr);
+
+ /* 11 : Get Current Contended Monitor */
+ jvmtiError (JNICALL *GetCurrentContendedMonitor) (jvmtiEnv* env,
+ jthread thread,
+ jobject* monitor_ptr);
+
+ /* 12 : Run Agent Thread */
+ jvmtiError (JNICALL *RunAgentThread) (jvmtiEnv* env,
+ jthread thread,
+ jvmtiStartFunction proc,
+ const void* arg,
+ jint priority);
+
+ /* 13 : Get Top Thread Groups */
+ jvmtiError (JNICALL *GetTopThreadGroups) (jvmtiEnv* env,
+ jint* group_count_ptr,
+ jthreadGroup** groups_ptr);
+
+ /* 14 : Get Thread Group Info */
+ jvmtiError (JNICALL *GetThreadGroupInfo) (jvmtiEnv* env,
+ jthreadGroup group,
+ jvmtiThreadGroupInfo* info_ptr);
+
+ /* 15 : Get Thread Group Children */
+ jvmtiError (JNICALL *GetThreadGroupChildren) (jvmtiEnv* env,
+ jthreadGroup group,
+ jint* thread_count_ptr,
+ jthread** threads_ptr,
+ jint* group_count_ptr,
+ jthreadGroup** groups_ptr);
+
+ /* 16 : Get Frame Count */
+ jvmtiError (JNICALL *GetFrameCount) (jvmtiEnv* env,
+ jthread thread,
+ jint* count_ptr);
+
+ /* 17 : Get Thread State */
+ jvmtiError (JNICALL *GetThreadState) (jvmtiEnv* env,
+ jthread thread,
+ jint* thread_state_ptr);
+
+ /* 18 : Get Current Thread */
+ jvmtiError (JNICALL *GetCurrentThread) (jvmtiEnv* env,
+ jthread* thread_ptr);
+
+ /* 19 : Get Frame Location */
+ jvmtiError (JNICALL *GetFrameLocation) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jmethodID* method_ptr,
+ jlocation* location_ptr);
+
+ /* 20 : Notify Frame Pop */
+ jvmtiError (JNICALL *NotifyFramePop) (jvmtiEnv* env,
+ jthread thread,
+ jint depth);
+
+ /* 21 : Get Local Variable - Object */
+ jvmtiError (JNICALL *GetLocalObject) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jobject* value_ptr);
+
+ /* 22 : Get Local Variable - Int */
+ jvmtiError (JNICALL *GetLocalInt) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jint* value_ptr);
+
+ /* 23 : Get Local Variable - Long */
+ jvmtiError (JNICALL *GetLocalLong) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jlong* value_ptr);
+
+ /* 24 : Get Local Variable - Float */
+ jvmtiError (JNICALL *GetLocalFloat) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jfloat* value_ptr);
+
+ /* 25 : Get Local Variable - Double */
+ jvmtiError (JNICALL *GetLocalDouble) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jdouble* value_ptr);
+
+ /* 26 : Set Local Variable - Object */
+ jvmtiError (JNICALL *SetLocalObject) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jobject value);
+
+ /* 27 : Set Local Variable - Int */
+ jvmtiError (JNICALL *SetLocalInt) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jint value);
+
+ /* 28 : Set Local Variable - Long */
+ jvmtiError (JNICALL *SetLocalLong) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jlong value);
+
+ /* 29 : Set Local Variable - Float */
+ jvmtiError (JNICALL *SetLocalFloat) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jfloat value);
+
+ /* 30 : Set Local Variable - Double */
+ jvmtiError (JNICALL *SetLocalDouble) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jdouble value);
+
+ /* 31 : Create Raw Monitor */
+ jvmtiError (JNICALL *CreateRawMonitor) (jvmtiEnv* env,
+ const char* name,
+ jrawMonitorID* monitor_ptr);
+
+ /* 32 : Destroy Raw Monitor */
+ jvmtiError (JNICALL *DestroyRawMonitor) (jvmtiEnv* env,
+ jrawMonitorID monitor);
+
+ /* 33 : Raw Monitor Enter */
+ jvmtiError (JNICALL *RawMonitorEnter) (jvmtiEnv* env,
+ jrawMonitorID monitor);
+
+ /* 34 : Raw Monitor Exit */
+ jvmtiError (JNICALL *RawMonitorExit) (jvmtiEnv* env,
+ jrawMonitorID monitor);
+
+ /* 35 : Raw Monitor Wait */
+ jvmtiError (JNICALL *RawMonitorWait) (jvmtiEnv* env,
+ jrawMonitorID monitor,
+ jlong millis);
+
+ /* 36 : Raw Monitor Notify */
+ jvmtiError (JNICALL *RawMonitorNotify) (jvmtiEnv* env,
+ jrawMonitorID monitor);
+
+ /* 37 : Raw Monitor Notify All */
+ jvmtiError (JNICALL *RawMonitorNotifyAll) (jvmtiEnv* env,
+ jrawMonitorID monitor);
+
+ /* 38 : Set Breakpoint */
+ jvmtiError (JNICALL *SetBreakpoint) (jvmtiEnv* env,
+ jmethodID method,
+ jlocation location);
+
+ /* 39 : Clear Breakpoint */
+ jvmtiError (JNICALL *ClearBreakpoint) (jvmtiEnv* env,
+ jmethodID method,
+ jlocation location);
+
+ /* 40 : RESERVED */
+ void *reserved40;
+
+ /* 41 : Set Field Access Watch */
+ jvmtiError (JNICALL *SetFieldAccessWatch) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field);
+
+ /* 42 : Clear Field Access Watch */
+ jvmtiError (JNICALL *ClearFieldAccessWatch) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field);
+
+ /* 43 : Set Field Modification Watch */
+ jvmtiError (JNICALL *SetFieldModificationWatch) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field);
+
+ /* 44 : Clear Field Modification Watch */
+ jvmtiError (JNICALL *ClearFieldModificationWatch) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field);
+
+ /* 45 : Is Modifiable Class */
+ jvmtiError (JNICALL *IsModifiableClass) (jvmtiEnv* env,
+ jclass klass,
+ jboolean* is_modifiable_class_ptr);
+
+ /* 46 : Allocate */
+ jvmtiError (JNICALL *Allocate) (jvmtiEnv* env,
+ jlong size,
+ unsigned char** mem_ptr);
+
+ /* 47 : Deallocate */
+ jvmtiError (JNICALL *Deallocate) (jvmtiEnv* env,
+ unsigned char* mem);
+
+ /* 48 : Get Class Signature */
+ jvmtiError (JNICALL *GetClassSignature) (jvmtiEnv* env,
+ jclass klass,
+ char** signature_ptr,
+ char** generic_ptr);
+
+ /* 49 : Get Class Status */
+ jvmtiError (JNICALL *GetClassStatus) (jvmtiEnv* env,
+ jclass klass,
+ jint* status_ptr);
+
+ /* 50 : Get Source File Name */
+ jvmtiError (JNICALL *GetSourceFileName) (jvmtiEnv* env,
+ jclass klass,
+ char** source_name_ptr);
+
+ /* 51 : Get Class Modifiers */
+ jvmtiError (JNICALL *GetClassModifiers) (jvmtiEnv* env,
+ jclass klass,
+ jint* modifiers_ptr);
+
+ /* 52 : Get Class Methods */
+ jvmtiError (JNICALL *GetClassMethods) (jvmtiEnv* env,
+ jclass klass,
+ jint* method_count_ptr,
+ jmethodID** methods_ptr);
+
+ /* 53 : Get Class Fields */
+ jvmtiError (JNICALL *GetClassFields) (jvmtiEnv* env,
+ jclass klass,
+ jint* field_count_ptr,
+ jfieldID** fields_ptr);
+
+ /* 54 : Get Implemented Interfaces */
+ jvmtiError (JNICALL *GetImplementedInterfaces) (jvmtiEnv* env,
+ jclass klass,
+ jint* interface_count_ptr,
+ jclass** interfaces_ptr);
+
+ /* 55 : Is Interface */
+ jvmtiError (JNICALL *IsInterface) (jvmtiEnv* env,
+ jclass klass,
+ jboolean* is_interface_ptr);
+
+ /* 56 : Is Array Class */
+ jvmtiError (JNICALL *IsArrayClass) (jvmtiEnv* env,
+ jclass klass,
+ jboolean* is_array_class_ptr);
+
+ /* 57 : Get Class Loader */
+ jvmtiError (JNICALL *GetClassLoader) (jvmtiEnv* env,
+ jclass klass,
+ jobject* classloader_ptr);
+
+ /* 58 : Get Object Hash Code */
+ jvmtiError (JNICALL *GetObjectHashCode) (jvmtiEnv* env,
+ jobject object,
+ jint* hash_code_ptr);
+
+ /* 59 : Get Object Monitor Usage */
+ jvmtiError (JNICALL *GetObjectMonitorUsage) (jvmtiEnv* env,
+ jobject object,
+ jvmtiMonitorUsage* info_ptr);
+
+ /* 60 : Get Field Name (and Signature) */
+ jvmtiError (JNICALL *GetFieldName) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr);
+
+ /* 61 : Get Field Declaring Class */
+ jvmtiError (JNICALL *GetFieldDeclaringClass) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jclass* declaring_class_ptr);
+
+ /* 62 : Get Field Modifiers */
+ jvmtiError (JNICALL *GetFieldModifiers) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jint* modifiers_ptr);
+
+ /* 63 : Is Field Synthetic */
+ jvmtiError (JNICALL *IsFieldSynthetic) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jboolean* is_synthetic_ptr);
+
+ /* 64 : Get Method Name (and Signature) */
+ jvmtiError (JNICALL *GetMethodName) (jvmtiEnv* env,
+ jmethodID method,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr);
+
+ /* 65 : Get Method Declaring Class */
+ jvmtiError (JNICALL *GetMethodDeclaringClass) (jvmtiEnv* env,
+ jmethodID method,
+ jclass* declaring_class_ptr);
+
+ /* 66 : Get Method Modifiers */
+ jvmtiError (JNICALL *GetMethodModifiers) (jvmtiEnv* env,
+ jmethodID method,
+ jint* modifiers_ptr);
+
+ /* 67 : RESERVED */
+ void *reserved67;
+
+ /* 68 : Get Max Locals */
+ jvmtiError (JNICALL *GetMaxLocals) (jvmtiEnv* env,
+ jmethodID method,
+ jint* max_ptr);
+
+ /* 69 : Get Arguments Size */
+ jvmtiError (JNICALL *GetArgumentsSize) (jvmtiEnv* env,
+ jmethodID method,
+ jint* size_ptr);
+
+ /* 70 : Get Line Number Table */
+ jvmtiError (JNICALL *GetLineNumberTable) (jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLineNumberEntry** table_ptr);
+
+ /* 71 : Get Method Location */
+ jvmtiError (JNICALL *GetMethodLocation) (jvmtiEnv* env,
+ jmethodID method,
+ jlocation* start_location_ptr,
+ jlocation* end_location_ptr);
+
+ /* 72 : Get Local Variable Table */
+ jvmtiError (JNICALL *GetLocalVariableTable) (jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLocalVariableEntry** table_ptr);
+
+ /* 73 : Set Native Method Prefix */
+ jvmtiError (JNICALL *SetNativeMethodPrefix) (jvmtiEnv* env,
+ const char* prefix);
+
+ /* 74 : Set Native Method Prefixes */
+ jvmtiError (JNICALL *SetNativeMethodPrefixes) (jvmtiEnv* env,
+ jint prefix_count,
+ char** prefixes);
+
+ /* 75 : Get Bytecodes */
+ jvmtiError (JNICALL *GetBytecodes) (jvmtiEnv* env,
+ jmethodID method,
+ jint* bytecode_count_ptr,
+ unsigned char** bytecodes_ptr);
+
+ /* 76 : Is Method Native */
+ jvmtiError (JNICALL *IsMethodNative) (jvmtiEnv* env,
+ jmethodID method,
+ jboolean* is_native_ptr);
+
+ /* 77 : Is Method Synthetic */
+ jvmtiError (JNICALL *IsMethodSynthetic) (jvmtiEnv* env,
+ jmethodID method,
+ jboolean* is_synthetic_ptr);
+
+ /* 78 : Get Loaded Classes */
+ jvmtiError (JNICALL *GetLoadedClasses) (jvmtiEnv* env,
+ jint* class_count_ptr,
+ jclass** classes_ptr);
+
+ /* 79 : Get Classloader Classes */
+ jvmtiError (JNICALL *GetClassLoaderClasses) (jvmtiEnv* env,
+ jobject initiating_loader,
+ jint* class_count_ptr,
+ jclass** classes_ptr);
+
+ /* 80 : Pop Frame */
+ jvmtiError (JNICALL *PopFrame) (jvmtiEnv* env,
+ jthread thread);
+
+ /* 81 : Force Early Return - Object */
+ jvmtiError (JNICALL *ForceEarlyReturnObject) (jvmtiEnv* env,
+ jthread thread,
+ jobject value);
+
+ /* 82 : Force Early Return - Int */
+ jvmtiError (JNICALL *ForceEarlyReturnInt) (jvmtiEnv* env,
+ jthread thread,
+ jint value);
+
+ /* 83 : Force Early Return - Long */
+ jvmtiError (JNICALL *ForceEarlyReturnLong) (jvmtiEnv* env,
+ jthread thread,
+ jlong value);
+
+ /* 84 : Force Early Return - Float */
+ jvmtiError (JNICALL *ForceEarlyReturnFloat) (jvmtiEnv* env,
+ jthread thread,
+ jfloat value);
+
+ /* 85 : Force Early Return - Double */
+ jvmtiError (JNICALL *ForceEarlyReturnDouble) (jvmtiEnv* env,
+ jthread thread,
+ jdouble value);
+
+ /* 86 : Force Early Return - Void */
+ jvmtiError (JNICALL *ForceEarlyReturnVoid) (jvmtiEnv* env,
+ jthread thread);
+
+ /* 87 : Redefine Classes */
+ jvmtiError (JNICALL *RedefineClasses) (jvmtiEnv* env,
+ jint class_count,
+ const jvmtiClassDefinition* class_definitions);
+
+ /* 88 : Get Version Number */
+ jvmtiError (JNICALL *GetVersionNumber) (jvmtiEnv* env,
+ jint* version_ptr);
+
+ /* 89 : Get Capabilities */
+ jvmtiError (JNICALL *GetCapabilities) (jvmtiEnv* env,
+ jvmtiCapabilities* capabilities_ptr);
+
+ /* 90 : Get Source Debug Extension */
+ jvmtiError (JNICALL *GetSourceDebugExtension) (jvmtiEnv* env,
+ jclass klass,
+ char** source_debug_extension_ptr);
+
+ /* 91 : Is Method Obsolete */
+ jvmtiError (JNICALL *IsMethodObsolete) (jvmtiEnv* env,
+ jmethodID method,
+ jboolean* is_obsolete_ptr);
+
+ /* 92 : Suspend Thread List */
+ jvmtiError (JNICALL *SuspendThreadList) (jvmtiEnv* env,
+ jint request_count,
+ const jthread* request_list,
+ jvmtiError* results);
+
+ /* 93 : Resume Thread List */
+ jvmtiError (JNICALL *ResumeThreadList) (jvmtiEnv* env,
+ jint request_count,
+ const jthread* request_list,
+ jvmtiError* results);
+
+ /* 94 : RESERVED */
+ void *reserved94;
+
+ /* 95 : RESERVED */
+ void *reserved95;
+
+ /* 96 : RESERVED */
+ void *reserved96;
+
+ /* 97 : RESERVED */
+ void *reserved97;
+
+ /* 98 : RESERVED */
+ void *reserved98;
+
+ /* 99 : RESERVED */
+ void *reserved99;
+
+ /* 100 : Get All Stack Traces */
+ jvmtiError (JNICALL *GetAllStackTraces) (jvmtiEnv* env,
+ jint max_frame_count,
+ jvmtiStackInfo** stack_info_ptr,
+ jint* thread_count_ptr);
+
+ /* 101 : Get Thread List Stack Traces */
+ jvmtiError (JNICALL *GetThreadListStackTraces) (jvmtiEnv* env,
+ jint thread_count,
+ const jthread* thread_list,
+ jint max_frame_count,
+ jvmtiStackInfo** stack_info_ptr);
+
+ /* 102 : Get Thread Local Storage */
+ jvmtiError (JNICALL *GetThreadLocalStorage) (jvmtiEnv* env,
+ jthread thread,
+ void** data_ptr);
+
+ /* 103 : Set Thread Local Storage */
+ jvmtiError (JNICALL *SetThreadLocalStorage) (jvmtiEnv* env,
+ jthread thread,
+ const void* data);
+
+ /* 104 : Get Stack Trace */
+ jvmtiError (JNICALL *GetStackTrace) (jvmtiEnv* env,
+ jthread thread,
+ jint start_depth,
+ jint max_frame_count,
+ jvmtiFrameInfo* frame_buffer,
+ jint* count_ptr);
+
+ /* 105 : RESERVED */
+ void *reserved105;
+
+ /* 106 : Get Tag */
+ jvmtiError (JNICALL *GetTag) (jvmtiEnv* env,
+ jobject object,
+ jlong* tag_ptr);
+
+ /* 107 : Set Tag */
+ jvmtiError (JNICALL *SetTag) (jvmtiEnv* env,
+ jobject object,
+ jlong tag);
+
+ /* 108 : Force Garbage Collection */
+ jvmtiError (JNICALL *ForceGarbageCollection) (jvmtiEnv* env);
+
+ /* 109 : Iterate Over Objects Reachable From Object */
+ jvmtiError (JNICALL *IterateOverObjectsReachableFromObject) (jvmtiEnv* env,
+ jobject object,
+ jvmtiObjectReferenceCallback object_reference_callback,
+ const void* user_data);
+
+ /* 110 : Iterate Over Reachable Objects */
+ jvmtiError (JNICALL *IterateOverReachableObjects) (jvmtiEnv* env,
+ jvmtiHeapRootCallback heap_root_callback,
+ jvmtiStackReferenceCallback stack_ref_callback,
+ jvmtiObjectReferenceCallback object_ref_callback,
+ const void* user_data);
+
+ /* 111 : Iterate Over Heap */
+ jvmtiError (JNICALL *IterateOverHeap) (jvmtiEnv* env,
+ jvmtiHeapObjectFilter object_filter,
+ jvmtiHeapObjectCallback heap_object_callback,
+ const void* user_data);
+
+ /* 112 : Iterate Over Instances Of Class */
+ jvmtiError (JNICALL *IterateOverInstancesOfClass) (jvmtiEnv* env,
+ jclass klass,
+ jvmtiHeapObjectFilter object_filter,
+ jvmtiHeapObjectCallback heap_object_callback,
+ const void* user_data);
+
+ /* 113 : RESERVED */
+ void *reserved113;
+
+ /* 114 : Get Objects With Tags */
+ jvmtiError (JNICALL *GetObjectsWithTags) (jvmtiEnv* env,
+ jint tag_count,
+ const jlong* tags,
+ jint* count_ptr,
+ jobject** object_result_ptr,
+ jlong** tag_result_ptr);
+
+ /* 115 : Follow References */
+ jvmtiError (JNICALL *FollowReferences) (jvmtiEnv* env,
+ jint heap_filter,
+ jclass klass,
+ jobject initial_object,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data);
+
+ /* 116 : Iterate Through Heap */
+ jvmtiError (JNICALL *IterateThroughHeap) (jvmtiEnv* env,
+ jint heap_filter,
+ jclass klass,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data);
+
+ /* 117 : RESERVED */
+ void *reserved117;
+
+ /* 118 : RESERVED */
+ void *reserved118;
+
+ /* 119 : RESERVED */
+ void *reserved119;
+
+ /* 120 : Set JNI Function Table */
+ jvmtiError (JNICALL *SetJNIFunctionTable) (jvmtiEnv* env,
+ const jniNativeInterface* function_table);
+
+ /* 121 : Get JNI Function Table */
+ jvmtiError (JNICALL *GetJNIFunctionTable) (jvmtiEnv* env,
+ jniNativeInterface** function_table);
+
+ /* 122 : Set Event Callbacks */
+ jvmtiError (JNICALL *SetEventCallbacks) (jvmtiEnv* env,
+ const jvmtiEventCallbacks* callbacks,
+ jint size_of_callbacks);
+
+ /* 123 : Generate Events */
+ jvmtiError (JNICALL *GenerateEvents) (jvmtiEnv* env,
+ jvmtiEvent event_type);
+
+ /* 124 : Get Extension Functions */
+ jvmtiError (JNICALL *GetExtensionFunctions) (jvmtiEnv* env,
+ jint* extension_count_ptr,
+ jvmtiExtensionFunctionInfo** extensions);
+
+ /* 125 : Get Extension Events */
+ jvmtiError (JNICALL *GetExtensionEvents) (jvmtiEnv* env,
+ jint* extension_count_ptr,
+ jvmtiExtensionEventInfo** extensions);
+
+ /* 126 : Set Extension Event Callback */
+ jvmtiError (JNICALL *SetExtensionEventCallback) (jvmtiEnv* env,
+ jint extension_event_index,
+ jvmtiExtensionEvent callback);
+
+ /* 127 : Dispose Environment */
+ jvmtiError (JNICALL *DisposeEnvironment) (jvmtiEnv* env);
+
+ /* 128 : Get Error Name */
+ jvmtiError (JNICALL *GetErrorName) (jvmtiEnv* env,
+ jvmtiError error,
+ char** name_ptr);
+
+ /* 129 : Get JLocation Format */
+ jvmtiError (JNICALL *GetJLocationFormat) (jvmtiEnv* env,
+ jvmtiJlocationFormat* format_ptr);
+
+ /* 130 : Get System Properties */
+ jvmtiError (JNICALL *GetSystemProperties) (jvmtiEnv* env,
+ jint* count_ptr,
+ char*** property_ptr);
+
+ /* 131 : Get System Property */
+ jvmtiError (JNICALL *GetSystemProperty) (jvmtiEnv* env,
+ const char* property,
+ char** value_ptr);
+
+ /* 132 : Set System Property */
+ jvmtiError (JNICALL *SetSystemProperty) (jvmtiEnv* env,
+ const char* property,
+ const char* value);
+
+ /* 133 : Get Phase */
+ jvmtiError (JNICALL *GetPhase) (jvmtiEnv* env,
+ jvmtiPhase* phase_ptr);
+
+ /* 134 : Get Current Thread CPU Timer Information */
+ jvmtiError (JNICALL *GetCurrentThreadCpuTimerInfo) (jvmtiEnv* env,
+ jvmtiTimerInfo* info_ptr);
+
+ /* 135 : Get Current Thread CPU Time */
+ jvmtiError (JNICALL *GetCurrentThreadCpuTime) (jvmtiEnv* env,
+ jlong* nanos_ptr);
+
+ /* 136 : Get Thread CPU Timer Information */
+ jvmtiError (JNICALL *GetThreadCpuTimerInfo) (jvmtiEnv* env,
+ jvmtiTimerInfo* info_ptr);
+
+ /* 137 : Get Thread CPU Time */
+ jvmtiError (JNICALL *GetThreadCpuTime) (jvmtiEnv* env,
+ jthread thread,
+ jlong* nanos_ptr);
+
+ /* 138 : Get Timer Information */
+ jvmtiError (JNICALL *GetTimerInfo) (jvmtiEnv* env,
+ jvmtiTimerInfo* info_ptr);
+
+ /* 139 : Get Time */
+ jvmtiError (JNICALL *GetTime) (jvmtiEnv* env,
+ jlong* nanos_ptr);
+
+ /* 140 : Get Potential Capabilities */
+ jvmtiError (JNICALL *GetPotentialCapabilities) (jvmtiEnv* env,
+ jvmtiCapabilities* capabilities_ptr);
+
+ /* 141 : RESERVED */
+ void *reserved141;
+
+ /* 142 : Add Capabilities */
+ jvmtiError (JNICALL *AddCapabilities) (jvmtiEnv* env,
+ const jvmtiCapabilities* capabilities_ptr);
+
+ /* 143 : Relinquish Capabilities */
+ jvmtiError (JNICALL *RelinquishCapabilities) (jvmtiEnv* env,
+ const jvmtiCapabilities* capabilities_ptr);
+
+ /* 144 : Get Available Processors */
+ jvmtiError (JNICALL *GetAvailableProcessors) (jvmtiEnv* env,
+ jint* processor_count_ptr);
+
+ /* 145 : Get Class Version Numbers */
+ jvmtiError (JNICALL *GetClassVersionNumbers) (jvmtiEnv* env,
+ jclass klass,
+ jint* minor_version_ptr,
+ jint* major_version_ptr);
+
+ /* 146 : Get Constant Pool */
+ jvmtiError (JNICALL *GetConstantPool) (jvmtiEnv* env,
+ jclass klass,
+ jint* constant_pool_count_ptr,
+ jint* constant_pool_byte_count_ptr,
+ unsigned char** constant_pool_bytes_ptr);
+
+ /* 147 : Get Environment Local Storage */
+ jvmtiError (JNICALL *GetEnvironmentLocalStorage) (jvmtiEnv* env,
+ void** data_ptr);
+
+ /* 148 : Set Environment Local Storage */
+ jvmtiError (JNICALL *SetEnvironmentLocalStorage) (jvmtiEnv* env,
+ const void* data);
+
+ /* 149 : Add To Bootstrap Class Loader Search */
+ jvmtiError (JNICALL *AddToBootstrapClassLoaderSearch) (jvmtiEnv* env,
+ const char* segment);
+
+ /* 150 : Set Verbose Flag */
+ jvmtiError (JNICALL *SetVerboseFlag) (jvmtiEnv* env,
+ jvmtiVerboseFlag flag,
+ jboolean value);
+
+ /* 151 : Add To System Class Loader Search */
+ jvmtiError (JNICALL *AddToSystemClassLoaderSearch) (jvmtiEnv* env,
+ const char* segment);
+
+ /* 152 : Retransform Classes */
+ jvmtiError (JNICALL *RetransformClasses) (jvmtiEnv* env,
+ jint class_count,
+ const jclass* classes);
+
+ /* 153 : Get Owned Monitor Stack Depth Info */
+ jvmtiError (JNICALL *GetOwnedMonitorStackDepthInfo) (jvmtiEnv* env,
+ jthread thread,
+ jint* monitor_info_count_ptr,
+ jvmtiMonitorStackDepthInfo** monitor_info_ptr);
+
+ /* 154 : Get Object Size */
+ jvmtiError (JNICALL *GetObjectSize) (jvmtiEnv* env,
+ jobject object,
+ jlong* size_ptr);
+
+} jvmtiInterface_1;
+
+struct _jvmtiEnv {
+ const struct jvmtiInterface_1_ *functions;
+#ifdef __cplusplus
+
+
+ jvmtiError Allocate(jlong size,
+ unsigned char** mem_ptr) {
+ return functions->Allocate(this, size, mem_ptr);
+ }
+
+ jvmtiError Deallocate(unsigned char* mem) {
+ return functions->Deallocate(this, mem);
+ }
+
+ jvmtiError GetThreadState(jthread thread,
+ jint* thread_state_ptr) {
+ return functions->GetThreadState(this, thread, thread_state_ptr);
+ }
+
+ jvmtiError GetCurrentThread(jthread* thread_ptr) {
+ return functions->GetCurrentThread(this, thread_ptr);
+ }
+
+ jvmtiError GetAllThreads(jint* threads_count_ptr,
+ jthread** threads_ptr) {
+ return functions->GetAllThreads(this, threads_count_ptr, threads_ptr);
+ }
+
+ jvmtiError SuspendThread(jthread thread) {
+ return functions->SuspendThread(this, thread);
+ }
+
+ jvmtiError SuspendThreadList(jint request_count,
+ const jthread* request_list,
+ jvmtiError* results) {
+ return functions->SuspendThreadList(this, request_count, request_list, results);
+ }
+
+ jvmtiError ResumeThread(jthread thread) {
+ return functions->ResumeThread(this, thread);
+ }
+
+ jvmtiError ResumeThreadList(jint request_count,
+ const jthread* request_list,
+ jvmtiError* results) {
+ return functions->ResumeThreadList(this, request_count, request_list, results);
+ }
+
+ jvmtiError StopThread(jthread thread,
+ jobject exception) {
+ return functions->StopThread(this, thread, exception);
+ }
+
+ jvmtiError InterruptThread(jthread thread) {
+ return functions->InterruptThread(this, thread);
+ }
+
+ jvmtiError GetThreadInfo(jthread thread,
+ jvmtiThreadInfo* info_ptr) {
+ return functions->GetThreadInfo(this, thread, info_ptr);
+ }
+
+ jvmtiError GetOwnedMonitorInfo(jthread thread,
+ jint* owned_monitor_count_ptr,
+ jobject** owned_monitors_ptr) {
+ return functions->GetOwnedMonitorInfo(this, thread, owned_monitor_count_ptr, owned_monitors_ptr);
+ }
+
+ jvmtiError GetOwnedMonitorStackDepthInfo(jthread thread,
+ jint* monitor_info_count_ptr,
+ jvmtiMonitorStackDepthInfo** monitor_info_ptr) {
+ return functions->GetOwnedMonitorStackDepthInfo(this, thread, monitor_info_count_ptr, monitor_info_ptr);
+ }
+
+ jvmtiError GetCurrentContendedMonitor(jthread thread,
+ jobject* monitor_ptr) {
+ return functions->GetCurrentContendedMonitor(this, thread, monitor_ptr);
+ }
+
+ jvmtiError RunAgentThread(jthread thread,
+ jvmtiStartFunction proc,
+ const void* arg,
+ jint priority) {
+ return functions->RunAgentThread(this, thread, proc, arg, priority);
+ }
+
+ jvmtiError SetThreadLocalStorage(jthread thread,
+ const void* data) {
+ return functions->SetThreadLocalStorage(this, thread, data);
+ }
+
+ jvmtiError GetThreadLocalStorage(jthread thread,
+ void** data_ptr) {
+ return functions->GetThreadLocalStorage(this, thread, data_ptr);
+ }
+
+ jvmtiError GetTopThreadGroups(jint* group_count_ptr,
+ jthreadGroup** groups_ptr) {
+ return functions->GetTopThreadGroups(this, group_count_ptr, groups_ptr);
+ }
+
+ jvmtiError GetThreadGroupInfo(jthreadGroup group,
+ jvmtiThreadGroupInfo* info_ptr) {
+ return functions->GetThreadGroupInfo(this, group, info_ptr);
+ }
+
+ jvmtiError GetThreadGroupChildren(jthreadGroup group,
+ jint* thread_count_ptr,
+ jthread** threads_ptr,
+ jint* group_count_ptr,
+ jthreadGroup** groups_ptr) {
+ return functions->GetThreadGroupChildren(this, group, thread_count_ptr, threads_ptr, group_count_ptr, groups_ptr);
+ }
+
+ jvmtiError GetStackTrace(jthread thread,
+ jint start_depth,
+ jint max_frame_count,
+ jvmtiFrameInfo* frame_buffer,
+ jint* count_ptr) {
+ return functions->GetStackTrace(this, thread, start_depth, max_frame_count, frame_buffer, count_ptr);
+ }
+
+ jvmtiError GetAllStackTraces(jint max_frame_count,
+ jvmtiStackInfo** stack_info_ptr,
+ jint* thread_count_ptr) {
+ return functions->GetAllStackTraces(this, max_frame_count, stack_info_ptr, thread_count_ptr);
+ }
+
+ jvmtiError GetThreadListStackTraces(jint thread_count,
+ const jthread* thread_list,
+ jint max_frame_count,
+ jvmtiStackInfo** stack_info_ptr) {
+ return functions->GetThreadListStackTraces(this, thread_count, thread_list, max_frame_count, stack_info_ptr);
+ }
+
+ jvmtiError GetFrameCount(jthread thread,
+ jint* count_ptr) {
+ return functions->GetFrameCount(this, thread, count_ptr);
+ }
+
+ jvmtiError PopFrame(jthread thread) {
+ return functions->PopFrame(this, thread);
+ }
+
+ jvmtiError GetFrameLocation(jthread thread,
+ jint depth,
+ jmethodID* method_ptr,
+ jlocation* location_ptr) {
+ return functions->GetFrameLocation(this, thread, depth, method_ptr, location_ptr);
+ }
+
+ jvmtiError NotifyFramePop(jthread thread,
+ jint depth) {
+ return functions->NotifyFramePop(this, thread, depth);
+ }
+
+ jvmtiError ForceEarlyReturnObject(jthread thread,
+ jobject value) {
+ return functions->ForceEarlyReturnObject(this, thread, value);
+ }
+
+ jvmtiError ForceEarlyReturnInt(jthread thread,
+ jint value) {
+ return functions->ForceEarlyReturnInt(this, thread, value);
+ }
+
+ jvmtiError ForceEarlyReturnLong(jthread thread,
+ jlong value) {
+ return functions->ForceEarlyReturnLong(this, thread, value);
+ }
+
+ jvmtiError ForceEarlyReturnFloat(jthread thread,
+ jfloat value) {
+ return functions->ForceEarlyReturnFloat(this, thread, value);
+ }
+
+ jvmtiError ForceEarlyReturnDouble(jthread thread,
+ jdouble value) {
+ return functions->ForceEarlyReturnDouble(this, thread, value);
+ }
+
+ jvmtiError ForceEarlyReturnVoid(jthread thread) {
+ return functions->ForceEarlyReturnVoid(this, thread);
+ }
+
+ jvmtiError FollowReferences(jint heap_filter,
+ jclass klass,
+ jobject initial_object,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data) {
+ return functions->FollowReferences(this, heap_filter, klass, initial_object, callbacks, user_data);
+ }
+
+ jvmtiError IterateThroughHeap(jint heap_filter,
+ jclass klass,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data) {
+ return functions->IterateThroughHeap(this, heap_filter, klass, callbacks, user_data);
+ }
+
+ jvmtiError GetTag(jobject object,
+ jlong* tag_ptr) {
+ return functions->GetTag(this, object, tag_ptr);
+ }
+
+ jvmtiError SetTag(jobject object,
+ jlong tag) {
+ return functions->SetTag(this, object, tag);
+ }
+
+ jvmtiError GetObjectsWithTags(jint tag_count,
+ const jlong* tags,
+ jint* count_ptr,
+ jobject** object_result_ptr,
+ jlong** tag_result_ptr) {
+ return functions->GetObjectsWithTags(this, tag_count, tags, count_ptr, object_result_ptr, tag_result_ptr);
+ }
+
+ jvmtiError ForceGarbageCollection() {
+ return functions->ForceGarbageCollection(this);
+ }
+
+ jvmtiError IterateOverObjectsReachableFromObject(jobject object,
+ jvmtiObjectReferenceCallback object_reference_callback,
+ const void* user_data) {
+ return functions->IterateOverObjectsReachableFromObject(this, object, object_reference_callback, user_data);
+ }
+
+ jvmtiError IterateOverReachableObjects(jvmtiHeapRootCallback heap_root_callback,
+ jvmtiStackReferenceCallback stack_ref_callback,
+ jvmtiObjectReferenceCallback object_ref_callback,
+ const void* user_data) {
+ return functions->IterateOverReachableObjects(this, heap_root_callback, stack_ref_callback, object_ref_callback, user_data);
+ }
+
+ jvmtiError IterateOverHeap(jvmtiHeapObjectFilter object_filter,
+ jvmtiHeapObjectCallback heap_object_callback,
+ const void* user_data) {
+ return functions->IterateOverHeap(this, object_filter, heap_object_callback, user_data);
+ }
+
+ jvmtiError IterateOverInstancesOfClass(jclass klass,
+ jvmtiHeapObjectFilter object_filter,
+ jvmtiHeapObjectCallback heap_object_callback,
+ const void* user_data) {
+ return functions->IterateOverInstancesOfClass(this, klass, object_filter, heap_object_callback, user_data);
+ }
+
+ jvmtiError GetLocalObject(jthread thread,
+ jint depth,
+ jint slot,
+ jobject* value_ptr) {
+ return functions->GetLocalObject(this, thread, depth, slot, value_ptr);
+ }
+
+ jvmtiError GetLocalInt(jthread thread,
+ jint depth,
+ jint slot,
+ jint* value_ptr) {
+ return functions->GetLocalInt(this, thread, depth, slot, value_ptr);
+ }
+
+ jvmtiError GetLocalLong(jthread thread,
+ jint depth,
+ jint slot,
+ jlong* value_ptr) {
+ return functions->GetLocalLong(this, thread, depth, slot, value_ptr);
+ }
+
+ jvmtiError GetLocalFloat(jthread thread,
+ jint depth,
+ jint slot,
+ jfloat* value_ptr) {
+ return functions->GetLocalFloat(this, thread, depth, slot, value_ptr);
+ }
+
+ jvmtiError GetLocalDouble(jthread thread,
+ jint depth,
+ jint slot,
+ jdouble* value_ptr) {
+ return functions->GetLocalDouble(this, thread, depth, slot, value_ptr);
+ }
+
+ jvmtiError SetLocalObject(jthread thread,
+ jint depth,
+ jint slot,
+ jobject value) {
+ return functions->SetLocalObject(this, thread, depth, slot, value);
+ }
+
+ jvmtiError SetLocalInt(jthread thread,
+ jint depth,
+ jint slot,
+ jint value) {
+ return functions->SetLocalInt(this, thread, depth, slot, value);
+ }
+
+ jvmtiError SetLocalLong(jthread thread,
+ jint depth,
+ jint slot,
+ jlong value) {
+ return functions->SetLocalLong(this, thread, depth, slot, value);
+ }
+
+ jvmtiError SetLocalFloat(jthread thread,
+ jint depth,
+ jint slot,
+ jfloat value) {
+ return functions->SetLocalFloat(this, thread, depth, slot, value);
+ }
+
+ jvmtiError SetLocalDouble(jthread thread,
+ jint depth,
+ jint slot,
+ jdouble value) {
+ return functions->SetLocalDouble(this, thread, depth, slot, value);
+ }
+
+ jvmtiError SetBreakpoint(jmethodID method,
+ jlocation location) {
+ return functions->SetBreakpoint(this, method, location);
+ }
+
+ jvmtiError ClearBreakpoint(jmethodID method,
+ jlocation location) {
+ return functions->ClearBreakpoint(this, method, location);
+ }
+
+ jvmtiError SetFieldAccessWatch(jclass klass,
+ jfieldID field) {
+ return functions->SetFieldAccessWatch(this, klass, field);
+ }
+
+ jvmtiError ClearFieldAccessWatch(jclass klass,
+ jfieldID field) {
+ return functions->ClearFieldAccessWatch(this, klass, field);
+ }
+
+ jvmtiError SetFieldModificationWatch(jclass klass,
+ jfieldID field) {
+ return functions->SetFieldModificationWatch(this, klass, field);
+ }
+
+ jvmtiError ClearFieldModificationWatch(jclass klass,
+ jfieldID field) {
+ return functions->ClearFieldModificationWatch(this, klass, field);
+ }
+
+ jvmtiError GetLoadedClasses(jint* class_count_ptr,
+ jclass** classes_ptr) {
+ return functions->GetLoadedClasses(this, class_count_ptr, classes_ptr);
+ }
+
+ jvmtiError GetClassLoaderClasses(jobject initiating_loader,
+ jint* class_count_ptr,
+ jclass** classes_ptr) {
+ return functions->GetClassLoaderClasses(this, initiating_loader, class_count_ptr, classes_ptr);
+ }
+
+ jvmtiError GetClassSignature(jclass klass,
+ char** signature_ptr,
+ char** generic_ptr) {
+ return functions->GetClassSignature(this, klass, signature_ptr, generic_ptr);
+ }
+
+ jvmtiError GetClassStatus(jclass klass,
+ jint* status_ptr) {
+ return functions->GetClassStatus(this, klass, status_ptr);
+ }
+
+ jvmtiError GetSourceFileName(jclass klass,
+ char** source_name_ptr) {
+ return functions->GetSourceFileName(this, klass, source_name_ptr);
+ }
+
+ jvmtiError GetClassModifiers(jclass klass,
+ jint* modifiers_ptr) {
+ return functions->GetClassModifiers(this, klass, modifiers_ptr);
+ }
+
+ jvmtiError GetClassMethods(jclass klass,
+ jint* method_count_ptr,
+ jmethodID** methods_ptr) {
+ return functions->GetClassMethods(this, klass, method_count_ptr, methods_ptr);
+ }
+
+ jvmtiError GetClassFields(jclass klass,
+ jint* field_count_ptr,
+ jfieldID** fields_ptr) {
+ return functions->GetClassFields(this, klass, field_count_ptr, fields_ptr);
+ }
+
+ jvmtiError GetImplementedInterfaces(jclass klass,
+ jint* interface_count_ptr,
+ jclass** interfaces_ptr) {
+ return functions->GetImplementedInterfaces(this, klass, interface_count_ptr, interfaces_ptr);
+ }
+
+ jvmtiError GetClassVersionNumbers(jclass klass,
+ jint* minor_version_ptr,
+ jint* major_version_ptr) {
+ return functions->GetClassVersionNumbers(this, klass, minor_version_ptr, major_version_ptr);
+ }
+
+ jvmtiError GetConstantPool(jclass klass,
+ jint* constant_pool_count_ptr,
+ jint* constant_pool_byte_count_ptr,
+ unsigned char** constant_pool_bytes_ptr) {
+ return functions->GetConstantPool(this, klass, constant_pool_count_ptr, constant_pool_byte_count_ptr, constant_pool_bytes_ptr);
+ }
+
+ jvmtiError IsInterface(jclass klass,
+ jboolean* is_interface_ptr) {
+ return functions->IsInterface(this, klass, is_interface_ptr);
+ }
+
+ jvmtiError IsArrayClass(jclass klass,
+ jboolean* is_array_class_ptr) {
+ return functions->IsArrayClass(this, klass, is_array_class_ptr);
+ }
+
+ jvmtiError IsModifiableClass(jclass klass,
+ jboolean* is_modifiable_class_ptr) {
+ return functions->IsModifiableClass(this, klass, is_modifiable_class_ptr);
+ }
+
+ jvmtiError GetClassLoader(jclass klass,
+ jobject* classloader_ptr) {
+ return functions->GetClassLoader(this, klass, classloader_ptr);
+ }
+
+ jvmtiError GetSourceDebugExtension(jclass klass,
+ char** source_debug_extension_ptr) {
+ return functions->GetSourceDebugExtension(this, klass, source_debug_extension_ptr);
+ }
+
+ jvmtiError RetransformClasses(jint class_count,
+ const jclass* classes) {
+ return functions->RetransformClasses(this, class_count, classes);
+ }
+
+ jvmtiError RedefineClasses(jint class_count,
+ const jvmtiClassDefinition* class_definitions) {
+ return functions->RedefineClasses(this, class_count, class_definitions);
+ }
+
+ jvmtiError GetObjectSize(jobject object,
+ jlong* size_ptr) {
+ return functions->GetObjectSize(this, object, size_ptr);
+ }
+
+ jvmtiError GetObjectHashCode(jobject object,
+ jint* hash_code_ptr) {
+ return functions->GetObjectHashCode(this, object, hash_code_ptr);
+ }
+
+ jvmtiError GetObjectMonitorUsage(jobject object,
+ jvmtiMonitorUsage* info_ptr) {
+ return functions->GetObjectMonitorUsage(this, object, info_ptr);
+ }
+
+ jvmtiError GetFieldName(jclass klass,
+ jfieldID field,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr) {
+ return functions->GetFieldName(this, klass, field, name_ptr, signature_ptr, generic_ptr);
+ }
+
+ jvmtiError GetFieldDeclaringClass(jclass klass,
+ jfieldID field,
+ jclass* declaring_class_ptr) {
+ return functions->GetFieldDeclaringClass(this, klass, field, declaring_class_ptr);
+ }
+
+ jvmtiError GetFieldModifiers(jclass klass,
+ jfieldID field,
+ jint* modifiers_ptr) {
+ return functions->GetFieldModifiers(this, klass, field, modifiers_ptr);
+ }
+
+ jvmtiError IsFieldSynthetic(jclass klass,
+ jfieldID field,
+ jboolean* is_synthetic_ptr) {
+ return functions->IsFieldSynthetic(this, klass, field, is_synthetic_ptr);
+ }
+
+ jvmtiError GetMethodName(jmethodID method,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr) {
+ return functions->GetMethodName(this, method, name_ptr, signature_ptr, generic_ptr);
+ }
+
+ jvmtiError GetMethodDeclaringClass(jmethodID method,
+ jclass* declaring_class_ptr) {
+ return functions->GetMethodDeclaringClass(this, method, declaring_class_ptr);
+ }
+
+ jvmtiError GetMethodModifiers(jmethodID method,
+ jint* modifiers_ptr) {
+ return functions->GetMethodModifiers(this, method, modifiers_ptr);
+ }
+
+ jvmtiError GetMaxLocals(jmethodID method,
+ jint* max_ptr) {
+ return functions->GetMaxLocals(this, method, max_ptr);
+ }
+
+ jvmtiError GetArgumentsSize(jmethodID method,
+ jint* size_ptr) {
+ return functions->GetArgumentsSize(this, method, size_ptr);
+ }
+
+ jvmtiError GetLineNumberTable(jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLineNumberEntry** table_ptr) {
+ return functions->GetLineNumberTable(this, method, entry_count_ptr, table_ptr);
+ }
+
+ jvmtiError GetMethodLocation(jmethodID method,
+ jlocation* start_location_ptr,
+ jlocation* end_location_ptr) {
+ return functions->GetMethodLocation(this, method, start_location_ptr, end_location_ptr);
+ }
+
+ jvmtiError GetLocalVariableTable(jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLocalVariableEntry** table_ptr) {
+ return functions->GetLocalVariableTable(this, method, entry_count_ptr, table_ptr);
+ }
+
+ jvmtiError GetBytecodes(jmethodID method,
+ jint* bytecode_count_ptr,
+ unsigned char** bytecodes_ptr) {
+ return functions->GetBytecodes(this, method, bytecode_count_ptr, bytecodes_ptr);
+ }
+
+ jvmtiError IsMethodNative(jmethodID method,
+ jboolean* is_native_ptr) {
+ return functions->IsMethodNative(this, method, is_native_ptr);
+ }
+
+ jvmtiError IsMethodSynthetic(jmethodID method,
+ jboolean* is_synthetic_ptr) {
+ return functions->IsMethodSynthetic(this, method, is_synthetic_ptr);
+ }
+
+ jvmtiError IsMethodObsolete(jmethodID method,
+ jboolean* is_obsolete_ptr) {
+ return functions->IsMethodObsolete(this, method, is_obsolete_ptr);
+ }
+
+ jvmtiError SetNativeMethodPrefix(const char* prefix) {
+ return functions->SetNativeMethodPrefix(this, prefix);
+ }
+
+ jvmtiError SetNativeMethodPrefixes(jint prefix_count,
+ char** prefixes) {
+ return functions->SetNativeMethodPrefixes(this, prefix_count, prefixes);
+ }
+
+ jvmtiError CreateRawMonitor(const char* name,
+ jrawMonitorID* monitor_ptr) {
+ return functions->CreateRawMonitor(this, name, monitor_ptr);
+ }
+
+ jvmtiError DestroyRawMonitor(jrawMonitorID monitor) {
+ return functions->DestroyRawMonitor(this, monitor);
+ }
+
+ jvmtiError RawMonitorEnter(jrawMonitorID monitor) {
+ return functions->RawMonitorEnter(this, monitor);
+ }
+
+ jvmtiError RawMonitorExit(jrawMonitorID monitor) {
+ return functions->RawMonitorExit(this, monitor);
+ }
+
+ jvmtiError RawMonitorWait(jrawMonitorID monitor,
+ jlong millis) {
+ return functions->RawMonitorWait(this, monitor, millis);
+ }
+
+ jvmtiError RawMonitorNotify(jrawMonitorID monitor) {
+ return functions->RawMonitorNotify(this, monitor);
+ }
+
+ jvmtiError RawMonitorNotifyAll(jrawMonitorID monitor) {
+ return functions->RawMonitorNotifyAll(this, monitor);
+ }
+
+ jvmtiError SetJNIFunctionTable(const jniNativeInterface* function_table) {
+ return functions->SetJNIFunctionTable(this, function_table);
+ }
+
+ jvmtiError GetJNIFunctionTable(jniNativeInterface** function_table) {
+ return functions->GetJNIFunctionTable(this, function_table);
+ }
+
+ jvmtiError SetEventCallbacks(const jvmtiEventCallbacks* callbacks,
+ jint size_of_callbacks) {
+ return functions->SetEventCallbacks(this, callbacks, size_of_callbacks);
+ }
+
+ jvmtiError SetEventNotificationMode(jvmtiEventMode mode,
+ jvmtiEvent event_type,
+ jthread event_thread,
+ ...) {
+ return functions->SetEventNotificationMode(this, mode, event_type, event_thread);
+ }
+
+ jvmtiError GenerateEvents(jvmtiEvent event_type) {
+ return functions->GenerateEvents(this, event_type);
+ }
+
+ jvmtiError GetExtensionFunctions(jint* extension_count_ptr,
+ jvmtiExtensionFunctionInfo** extensions) {
+ return functions->GetExtensionFunctions(this, extension_count_ptr, extensions);
+ }
+
+ jvmtiError GetExtensionEvents(jint* extension_count_ptr,
+ jvmtiExtensionEventInfo** extensions) {
+ return functions->GetExtensionEvents(this, extension_count_ptr, extensions);
+ }
+
+ jvmtiError SetExtensionEventCallback(jint extension_event_index,
+ jvmtiExtensionEvent callback) {
+ return functions->SetExtensionEventCallback(this, extension_event_index, callback);
+ }
+
+ jvmtiError GetPotentialCapabilities(jvmtiCapabilities* capabilities_ptr) {
+ return functions->GetPotentialCapabilities(this, capabilities_ptr);
+ }
+
+ jvmtiError AddCapabilities(const jvmtiCapabilities* capabilities_ptr) {
+ return functions->AddCapabilities(this, capabilities_ptr);
+ }
+
+ jvmtiError RelinquishCapabilities(const jvmtiCapabilities* capabilities_ptr) {
+ return functions->RelinquishCapabilities(this, capabilities_ptr);
+ }
+
+ jvmtiError GetCapabilities(jvmtiCapabilities* capabilities_ptr) {
+ return functions->GetCapabilities(this, capabilities_ptr);
+ }
+
+ jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) {
+ return functions->GetCurrentThreadCpuTimerInfo(this, info_ptr);
+ }
+
+ jvmtiError GetCurrentThreadCpuTime(jlong* nanos_ptr) {
+ return functions->GetCurrentThreadCpuTime(this, nanos_ptr);
+ }
+
+ jvmtiError GetThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) {
+ return functions->GetThreadCpuTimerInfo(this, info_ptr);
+ }
+
+ jvmtiError GetThreadCpuTime(jthread thread,
+ jlong* nanos_ptr) {
+ return functions->GetThreadCpuTime(this, thread, nanos_ptr);
+ }
+
+ jvmtiError GetTimerInfo(jvmtiTimerInfo* info_ptr) {
+ return functions->GetTimerInfo(this, info_ptr);
+ }
+
+ jvmtiError GetTime(jlong* nanos_ptr) {
+ return functions->GetTime(this, nanos_ptr);
+ }
+
+ jvmtiError GetAvailableProcessors(jint* processor_count_ptr) {
+ return functions->GetAvailableProcessors(this, processor_count_ptr);
+ }
+
+ jvmtiError AddToBootstrapClassLoaderSearch(const char* segment) {
+ return functions->AddToBootstrapClassLoaderSearch(this, segment);
+ }
+
+ jvmtiError AddToSystemClassLoaderSearch(const char* segment) {
+ return functions->AddToSystemClassLoaderSearch(this, segment);
+ }
+
+ jvmtiError GetSystemProperties(jint* count_ptr,
+ char*** property_ptr) {
+ return functions->GetSystemProperties(this, count_ptr, property_ptr);
+ }
+
+ jvmtiError GetSystemProperty(const char* property,
+ char** value_ptr) {
+ return functions->GetSystemProperty(this, property, value_ptr);
+ }
+
+ jvmtiError SetSystemProperty(const char* property,
+ const char* value) {
+ return functions->SetSystemProperty(this, property, value);
+ }
+
+ jvmtiError GetPhase(jvmtiPhase* phase_ptr) {
+ return functions->GetPhase(this, phase_ptr);
+ }
+
+ jvmtiError DisposeEnvironment() {
+ return functions->DisposeEnvironment(this);
+ }
+
+ jvmtiError SetEnvironmentLocalStorage(const void* data) {
+ return functions->SetEnvironmentLocalStorage(this, data);
+ }
+
+ jvmtiError GetEnvironmentLocalStorage(void** data_ptr) {
+ return functions->GetEnvironmentLocalStorage(this, data_ptr);
+ }
+
+ jvmtiError GetVersionNumber(jint* version_ptr) {
+ return functions->GetVersionNumber(this, version_ptr);
+ }
+
+ jvmtiError GetErrorName(jvmtiError error,
+ char** name_ptr) {
+ return functions->GetErrorName(this, error, name_ptr);
+ }
+
+ jvmtiError SetVerboseFlag(jvmtiVerboseFlag flag,
+ jboolean value) {
+ return functions->SetVerboseFlag(this, flag, value);
+ }
+
+ jvmtiError GetJLocationFormat(jvmtiJlocationFormat* format_ptr) {
+ return functions->GetJLocationFormat(this, format_ptr);
+ }
+
+#endif /* __cplusplus */
+};
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* !_JAVA_JVMTI_H_ */
+
diff --git a/cpp/wiiuse/include/win32/jawt_md.h b/cpp/wiiuse/include/win32/jawt_md.h
new file mode 100644
index 0000000..82ba034
--- /dev/null
+++ b/cpp/wiiuse/include/win32/jawt_md.h
@@ -0,0 +1,41 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+#ifndef _JAVASOFT_JAWT_MD_H_
+#define _JAVASOFT_JAWT_MD_H_
+
+#include
+#include "jawt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Win32-specific declarations for AWT native interface.
+ * See notes in jawt.h for an example of use.
+ */
+typedef struct jawt_Win32DrawingSurfaceInfo {
+ /* Native window, DDB, or DIB handle */
+ union {
+ HWND hwnd;
+ HBITMAP hbitmap;
+ void* pbits;
+ };
+ /*
+ * This HDC should always be used instead of the HDC returned from
+ * BeginPaint() or any calls to GetDC().
+ */
+ HDC hdc;
+ HPALETTE hpalette;
+} JAWT_Win32DrawingSurfaceInfo;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_JAVASOFT_JAWT_MD_H_ */
diff --git a/cpp/wiiuse/include/win32/jni_md.h b/cpp/wiiuse/include/win32/jni_md.h
new file mode 100644
index 0000000..9ac4718
--- /dev/null
+++ b/cpp/wiiuse/include/win32/jni_md.h
@@ -0,0 +1,19 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+#ifndef _JAVASOFT_JNI_MD_H_
+#define _JAVASOFT_JNI_MD_H_
+
+#define JNIEXPORT __declspec(dllexport)
+#define JNIIMPORT __declspec(dllimport)
+#define JNICALL __stdcall
+
+typedef long jint;
+typedef __int64 jlong;
+typedef signed char jbyte;
+
+#endif /* !_JAVASOFT_JNI_MD_H_ */
diff --git a/cpp/wiiusej/include/classfile_constants.h b/cpp/wiiusej/include/classfile_constants.h
new file mode 100644
index 0000000..30e839e
--- /dev/null
+++ b/cpp/wiiusej/include/classfile_constants.h
@@ -0,0 +1,523 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ *
+ */
+
+#ifndef CLASSFILE_CONSTANTS_H
+#define CLASSFILE_CONSTANTS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Flags */
+
+enum {
+ JVM_ACC_PUBLIC = 0x0001,
+ JVM_ACC_PRIVATE = 0x0002,
+ JVM_ACC_PROTECTED = 0x0004,
+ JVM_ACC_STATIC = 0x0008,
+ JVM_ACC_FINAL = 0x0010,
+ JVM_ACC_SYNCHRONIZED = 0x0020,
+ JVM_ACC_SUPER = 0x0020,
+ JVM_ACC_VOLATILE = 0x0040,
+ JVM_ACC_BRIDGE = 0x0040,
+ JVM_ACC_TRANSIENT = 0x0080,
+ JVM_ACC_VARARGS = 0x0080,
+ JVM_ACC_NATIVE = 0x0100,
+ JVM_ACC_INTERFACE = 0x0200,
+ JVM_ACC_ABSTRACT = 0x0400,
+ JVM_ACC_STRICT = 0x0800,
+ JVM_ACC_SYNTHETIC = 0x1000,
+ JVM_ACC_ANNOTATION = 0x2000,
+ JVM_ACC_ENUM = 0x4000
+};
+
+/* Used in newarray instruction. */
+
+enum {
+ JVM_T_BOOLEAN = 4,
+ JVM_T_CHAR = 5,
+ JVM_T_FLOAT = 6,
+ JVM_T_DOUBLE = 7,
+ JVM_T_BYTE = 8,
+ JVM_T_SHORT = 9,
+ JVM_T_INT = 10,
+ JVM_T_LONG = 11
+};
+
+/* Constant Pool Entries */
+
+enum {
+ JVM_CONSTANT_Utf8 = 1,
+ JVM_CONSTANT_Unicode = 2, /* unused */
+ JVM_CONSTANT_Integer = 3,
+ JVM_CONSTANT_Float = 4,
+ JVM_CONSTANT_Long = 5,
+ JVM_CONSTANT_Double = 6,
+ JVM_CONSTANT_Class = 7,
+ JVM_CONSTANT_String = 8,
+ JVM_CONSTANT_Fieldref = 9,
+ JVM_CONSTANT_Methodref = 10,
+ JVM_CONSTANT_InterfaceMethodref = 11,
+ JVM_CONSTANT_NameAndType = 12
+};
+
+/* StackMapTable type item numbers */
+
+enum {
+ JVM_ITEM_Top = 0,
+ JVM_ITEM_Integer = 1,
+ JVM_ITEM_Float = 2,
+ JVM_ITEM_Double = 3,
+ JVM_ITEM_Long = 4,
+ JVM_ITEM_Null = 5,
+ JVM_ITEM_UninitializedThis = 6,
+ JVM_ITEM_Object = 7,
+ JVM_ITEM_Uninitialized = 8
+};
+
+/* Type signatures */
+
+enum {
+ JVM_SIGNATURE_ARRAY = '[',
+ JVM_SIGNATURE_BYTE = 'B',
+ JVM_SIGNATURE_CHAR = 'C',
+ JVM_SIGNATURE_CLASS = 'L',
+ JVM_SIGNATURE_ENDCLASS = ';',
+ JVM_SIGNATURE_ENUM = 'E',
+ JVM_SIGNATURE_FLOAT = 'F',
+ JVM_SIGNATURE_DOUBLE = 'D',
+ JVM_SIGNATURE_FUNC = '(',
+ JVM_SIGNATURE_ENDFUNC = ')',
+ JVM_SIGNATURE_INT = 'I',
+ JVM_SIGNATURE_LONG = 'J',
+ JVM_SIGNATURE_SHORT = 'S',
+ JVM_SIGNATURE_VOID = 'V',
+ JVM_SIGNATURE_BOOLEAN = 'Z'
+};
+
+/* Opcodes */
+
+enum {
+ JVM_OPC_nop = 0,
+ JVM_OPC_aconst_null = 1,
+ JVM_OPC_iconst_m1 = 2,
+ JVM_OPC_iconst_0 = 3,
+ JVM_OPC_iconst_1 = 4,
+ JVM_OPC_iconst_2 = 5,
+ JVM_OPC_iconst_3 = 6,
+ JVM_OPC_iconst_4 = 7,
+ JVM_OPC_iconst_5 = 8,
+ JVM_OPC_lconst_0 = 9,
+ JVM_OPC_lconst_1 = 10,
+ JVM_OPC_fconst_0 = 11,
+ JVM_OPC_fconst_1 = 12,
+ JVM_OPC_fconst_2 = 13,
+ JVM_OPC_dconst_0 = 14,
+ JVM_OPC_dconst_1 = 15,
+ JVM_OPC_bipush = 16,
+ JVM_OPC_sipush = 17,
+ JVM_OPC_ldc = 18,
+ JVM_OPC_ldc_w = 19,
+ JVM_OPC_ldc2_w = 20,
+ JVM_OPC_iload = 21,
+ JVM_OPC_lload = 22,
+ JVM_OPC_fload = 23,
+ JVM_OPC_dload = 24,
+ JVM_OPC_aload = 25,
+ JVM_OPC_iload_0 = 26,
+ JVM_OPC_iload_1 = 27,
+ JVM_OPC_iload_2 = 28,
+ JVM_OPC_iload_3 = 29,
+ JVM_OPC_lload_0 = 30,
+ JVM_OPC_lload_1 = 31,
+ JVM_OPC_lload_2 = 32,
+ JVM_OPC_lload_3 = 33,
+ JVM_OPC_fload_0 = 34,
+ JVM_OPC_fload_1 = 35,
+ JVM_OPC_fload_2 = 36,
+ JVM_OPC_fload_3 = 37,
+ JVM_OPC_dload_0 = 38,
+ JVM_OPC_dload_1 = 39,
+ JVM_OPC_dload_2 = 40,
+ JVM_OPC_dload_3 = 41,
+ JVM_OPC_aload_0 = 42,
+ JVM_OPC_aload_1 = 43,
+ JVM_OPC_aload_2 = 44,
+ JVM_OPC_aload_3 = 45,
+ JVM_OPC_iaload = 46,
+ JVM_OPC_laload = 47,
+ JVM_OPC_faload = 48,
+ JVM_OPC_daload = 49,
+ JVM_OPC_aaload = 50,
+ JVM_OPC_baload = 51,
+ JVM_OPC_caload = 52,
+ JVM_OPC_saload = 53,
+ JVM_OPC_istore = 54,
+ JVM_OPC_lstore = 55,
+ JVM_OPC_fstore = 56,
+ JVM_OPC_dstore = 57,
+ JVM_OPC_astore = 58,
+ JVM_OPC_istore_0 = 59,
+ JVM_OPC_istore_1 = 60,
+ JVM_OPC_istore_2 = 61,
+ JVM_OPC_istore_3 = 62,
+ JVM_OPC_lstore_0 = 63,
+ JVM_OPC_lstore_1 = 64,
+ JVM_OPC_lstore_2 = 65,
+ JVM_OPC_lstore_3 = 66,
+ JVM_OPC_fstore_0 = 67,
+ JVM_OPC_fstore_1 = 68,
+ JVM_OPC_fstore_2 = 69,
+ JVM_OPC_fstore_3 = 70,
+ JVM_OPC_dstore_0 = 71,
+ JVM_OPC_dstore_1 = 72,
+ JVM_OPC_dstore_2 = 73,
+ JVM_OPC_dstore_3 = 74,
+ JVM_OPC_astore_0 = 75,
+ JVM_OPC_astore_1 = 76,
+ JVM_OPC_astore_2 = 77,
+ JVM_OPC_astore_3 = 78,
+ JVM_OPC_iastore = 79,
+ JVM_OPC_lastore = 80,
+ JVM_OPC_fastore = 81,
+ JVM_OPC_dastore = 82,
+ JVM_OPC_aastore = 83,
+ JVM_OPC_bastore = 84,
+ JVM_OPC_castore = 85,
+ JVM_OPC_sastore = 86,
+ JVM_OPC_pop = 87,
+ JVM_OPC_pop2 = 88,
+ JVM_OPC_dup = 89,
+ JVM_OPC_dup_x1 = 90,
+ JVM_OPC_dup_x2 = 91,
+ JVM_OPC_dup2 = 92,
+ JVM_OPC_dup2_x1 = 93,
+ JVM_OPC_dup2_x2 = 94,
+ JVM_OPC_swap = 95,
+ JVM_OPC_iadd = 96,
+ JVM_OPC_ladd = 97,
+ JVM_OPC_fadd = 98,
+ JVM_OPC_dadd = 99,
+ JVM_OPC_isub = 100,
+ JVM_OPC_lsub = 101,
+ JVM_OPC_fsub = 102,
+ JVM_OPC_dsub = 103,
+ JVM_OPC_imul = 104,
+ JVM_OPC_lmul = 105,
+ JVM_OPC_fmul = 106,
+ JVM_OPC_dmul = 107,
+ JVM_OPC_idiv = 108,
+ JVM_OPC_ldiv = 109,
+ JVM_OPC_fdiv = 110,
+ JVM_OPC_ddiv = 111,
+ JVM_OPC_irem = 112,
+ JVM_OPC_lrem = 113,
+ JVM_OPC_frem = 114,
+ JVM_OPC_drem = 115,
+ JVM_OPC_ineg = 116,
+ JVM_OPC_lneg = 117,
+ JVM_OPC_fneg = 118,
+ JVM_OPC_dneg = 119,
+ JVM_OPC_ishl = 120,
+ JVM_OPC_lshl = 121,
+ JVM_OPC_ishr = 122,
+ JVM_OPC_lshr = 123,
+ JVM_OPC_iushr = 124,
+ JVM_OPC_lushr = 125,
+ JVM_OPC_iand = 126,
+ JVM_OPC_land = 127,
+ JVM_OPC_ior = 128,
+ JVM_OPC_lor = 129,
+ JVM_OPC_ixor = 130,
+ JVM_OPC_lxor = 131,
+ JVM_OPC_iinc = 132,
+ JVM_OPC_i2l = 133,
+ JVM_OPC_i2f = 134,
+ JVM_OPC_i2d = 135,
+ JVM_OPC_l2i = 136,
+ JVM_OPC_l2f = 137,
+ JVM_OPC_l2d = 138,
+ JVM_OPC_f2i = 139,
+ JVM_OPC_f2l = 140,
+ JVM_OPC_f2d = 141,
+ JVM_OPC_d2i = 142,
+ JVM_OPC_d2l = 143,
+ JVM_OPC_d2f = 144,
+ JVM_OPC_i2b = 145,
+ JVM_OPC_i2c = 146,
+ JVM_OPC_i2s = 147,
+ JVM_OPC_lcmp = 148,
+ JVM_OPC_fcmpl = 149,
+ JVM_OPC_fcmpg = 150,
+ JVM_OPC_dcmpl = 151,
+ JVM_OPC_dcmpg = 152,
+ JVM_OPC_ifeq = 153,
+ JVM_OPC_ifne = 154,
+ JVM_OPC_iflt = 155,
+ JVM_OPC_ifge = 156,
+ JVM_OPC_ifgt = 157,
+ JVM_OPC_ifle = 158,
+ JVM_OPC_if_icmpeq = 159,
+ JVM_OPC_if_icmpne = 160,
+ JVM_OPC_if_icmplt = 161,
+ JVM_OPC_if_icmpge = 162,
+ JVM_OPC_if_icmpgt = 163,
+ JVM_OPC_if_icmple = 164,
+ JVM_OPC_if_acmpeq = 165,
+ JVM_OPC_if_acmpne = 166,
+ JVM_OPC_goto = 167,
+ JVM_OPC_jsr = 168,
+ JVM_OPC_ret = 169,
+ JVM_OPC_tableswitch = 170,
+ JVM_OPC_lookupswitch = 171,
+ JVM_OPC_ireturn = 172,
+ JVM_OPC_lreturn = 173,
+ JVM_OPC_freturn = 174,
+ JVM_OPC_dreturn = 175,
+ JVM_OPC_areturn = 176,
+ JVM_OPC_return = 177,
+ JVM_OPC_getstatic = 178,
+ JVM_OPC_putstatic = 179,
+ JVM_OPC_getfield = 180,
+ JVM_OPC_putfield = 181,
+ JVM_OPC_invokevirtual = 182,
+ JVM_OPC_invokespecial = 183,
+ JVM_OPC_invokestatic = 184,
+ JVM_OPC_invokeinterface = 185,
+ JVM_OPC_xxxunusedxxx = 186,
+ JVM_OPC_new = 187,
+ JVM_OPC_newarray = 188,
+ JVM_OPC_anewarray = 189,
+ JVM_OPC_arraylength = 190,
+ JVM_OPC_athrow = 191,
+ JVM_OPC_checkcast = 192,
+ JVM_OPC_instanceof = 193,
+ JVM_OPC_monitorenter = 194,
+ JVM_OPC_monitorexit = 195,
+ JVM_OPC_wide = 196,
+ JVM_OPC_multianewarray = 197,
+ JVM_OPC_ifnull = 198,
+ JVM_OPC_ifnonnull = 199,
+ JVM_OPC_goto_w = 200,
+ JVM_OPC_jsr_w = 201,
+ JVM_OPC_MAX = 201
+};
+
+/* Opcode length initializer, use with something like:
+ * unsigned char opcode_length[JVM_OPC_MAX+1] = JVM_OPCODE_LENGTH_INITIALIZER;
+ */
+#define JVM_OPCODE_LENGTH_INITIALIZER { \
+ 1, /* nop */ \
+ 1, /* aconst_null */ \
+ 1, /* iconst_m1 */ \
+ 1, /* iconst_0 */ \
+ 1, /* iconst_1 */ \
+ 1, /* iconst_2 */ \
+ 1, /* iconst_3 */ \
+ 1, /* iconst_4 */ \
+ 1, /* iconst_5 */ \
+ 1, /* lconst_0 */ \
+ 1, /* lconst_1 */ \
+ 1, /* fconst_0 */ \
+ 1, /* fconst_1 */ \
+ 1, /* fconst_2 */ \
+ 1, /* dconst_0 */ \
+ 1, /* dconst_1 */ \
+ 2, /* bipush */ \
+ 3, /* sipush */ \
+ 2, /* ldc */ \
+ 3, /* ldc_w */ \
+ 3, /* ldc2_w */ \
+ 2, /* iload */ \
+ 2, /* lload */ \
+ 2, /* fload */ \
+ 2, /* dload */ \
+ 2, /* aload */ \
+ 1, /* iload_0 */ \
+ 1, /* iload_1 */ \
+ 1, /* iload_2 */ \
+ 1, /* iload_3 */ \
+ 1, /* lload_0 */ \
+ 1, /* lload_1 */ \
+ 1, /* lload_2 */ \
+ 1, /* lload_3 */ \
+ 1, /* fload_0 */ \
+ 1, /* fload_1 */ \
+ 1, /* fload_2 */ \
+ 1, /* fload_3 */ \
+ 1, /* dload_0 */ \
+ 1, /* dload_1 */ \
+ 1, /* dload_2 */ \
+ 1, /* dload_3 */ \
+ 1, /* aload_0 */ \
+ 1, /* aload_1 */ \
+ 1, /* aload_2 */ \
+ 1, /* aload_3 */ \
+ 1, /* iaload */ \
+ 1, /* laload */ \
+ 1, /* faload */ \
+ 1, /* daload */ \
+ 1, /* aaload */ \
+ 1, /* baload */ \
+ 1, /* caload */ \
+ 1, /* saload */ \
+ 2, /* istore */ \
+ 2, /* lstore */ \
+ 2, /* fstore */ \
+ 2, /* dstore */ \
+ 2, /* astore */ \
+ 1, /* istore_0 */ \
+ 1, /* istore_1 */ \
+ 1, /* istore_2 */ \
+ 1, /* istore_3 */ \
+ 1, /* lstore_0 */ \
+ 1, /* lstore_1 */ \
+ 1, /* lstore_2 */ \
+ 1, /* lstore_3 */ \
+ 1, /* fstore_0 */ \
+ 1, /* fstore_1 */ \
+ 1, /* fstore_2 */ \
+ 1, /* fstore_3 */ \
+ 1, /* dstore_0 */ \
+ 1, /* dstore_1 */ \
+ 1, /* dstore_2 */ \
+ 1, /* dstore_3 */ \
+ 1, /* astore_0 */ \
+ 1, /* astore_1 */ \
+ 1, /* astore_2 */ \
+ 1, /* astore_3 */ \
+ 1, /* iastore */ \
+ 1, /* lastore */ \
+ 1, /* fastore */ \
+ 1, /* dastore */ \
+ 1, /* aastore */ \
+ 1, /* bastore */ \
+ 1, /* castore */ \
+ 1, /* sastore */ \
+ 1, /* pop */ \
+ 1, /* pop2 */ \
+ 1, /* dup */ \
+ 1, /* dup_x1 */ \
+ 1, /* dup_x2 */ \
+ 1, /* dup2 */ \
+ 1, /* dup2_x1 */ \
+ 1, /* dup2_x2 */ \
+ 1, /* swap */ \
+ 1, /* iadd */ \
+ 1, /* ladd */ \
+ 1, /* fadd */ \
+ 1, /* dadd */ \
+ 1, /* isub */ \
+ 1, /* lsub */ \
+ 1, /* fsub */ \
+ 1, /* dsub */ \
+ 1, /* imul */ \
+ 1, /* lmul */ \
+ 1, /* fmul */ \
+ 1, /* dmul */ \
+ 1, /* idiv */ \
+ 1, /* ldiv */ \
+ 1, /* fdiv */ \
+ 1, /* ddiv */ \
+ 1, /* irem */ \
+ 1, /* lrem */ \
+ 1, /* frem */ \
+ 1, /* drem */ \
+ 1, /* ineg */ \
+ 1, /* lneg */ \
+ 1, /* fneg */ \
+ 1, /* dneg */ \
+ 1, /* ishl */ \
+ 1, /* lshl */ \
+ 1, /* ishr */ \
+ 1, /* lshr */ \
+ 1, /* iushr */ \
+ 1, /* lushr */ \
+ 1, /* iand */ \
+ 1, /* land */ \
+ 1, /* ior */ \
+ 1, /* lor */ \
+ 1, /* ixor */ \
+ 1, /* lxor */ \
+ 3, /* iinc */ \
+ 1, /* i2l */ \
+ 1, /* i2f */ \
+ 1, /* i2d */ \
+ 1, /* l2i */ \
+ 1, /* l2f */ \
+ 1, /* l2d */ \
+ 1, /* f2i */ \
+ 1, /* f2l */ \
+ 1, /* f2d */ \
+ 1, /* d2i */ \
+ 1, /* d2l */ \
+ 1, /* d2f */ \
+ 1, /* i2b */ \
+ 1, /* i2c */ \
+ 1, /* i2s */ \
+ 1, /* lcmp */ \
+ 1, /* fcmpl */ \
+ 1, /* fcmpg */ \
+ 1, /* dcmpl */ \
+ 1, /* dcmpg */ \
+ 3, /* ifeq */ \
+ 3, /* ifne */ \
+ 3, /* iflt */ \
+ 3, /* ifge */ \
+ 3, /* ifgt */ \
+ 3, /* ifle */ \
+ 3, /* if_icmpeq */ \
+ 3, /* if_icmpne */ \
+ 3, /* if_icmplt */ \
+ 3, /* if_icmpge */ \
+ 3, /* if_icmpgt */ \
+ 3, /* if_icmple */ \
+ 3, /* if_acmpeq */ \
+ 3, /* if_acmpne */ \
+ 3, /* goto */ \
+ 3, /* jsr */ \
+ 2, /* ret */ \
+ 99, /* tableswitch */ \
+ 99, /* lookupswitch */ \
+ 1, /* ireturn */ \
+ 1, /* lreturn */ \
+ 1, /* freturn */ \
+ 1, /* dreturn */ \
+ 1, /* areturn */ \
+ 1, /* return */ \
+ 3, /* getstatic */ \
+ 3, /* putstatic */ \
+ 3, /* getfield */ \
+ 3, /* putfield */ \
+ 3, /* invokevirtual */ \
+ 3, /* invokespecial */ \
+ 3, /* invokestatic */ \
+ 5, /* invokeinterface */ \
+ 0, /* xxxunusedxxx */ \
+ 3, /* new */ \
+ 2, /* newarray */ \
+ 3, /* anewarray */ \
+ 1, /* arraylength */ \
+ 1, /* athrow */ \
+ 3, /* checkcast */ \
+ 3, /* instanceof */ \
+ 1, /* monitorenter */ \
+ 1, /* monitorexit */ \
+ 0, /* wide */ \
+ 4, /* multianewarray */ \
+ 3, /* ifnull */ \
+ 3, /* ifnonnull */ \
+ 5, /* goto_w */ \
+ 5 /* jsr_w */ \
+}
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* CLASSFILE_CONSTANTS */
diff --git a/cpp/wiiusej/include/jawt.h b/cpp/wiiusej/include/jawt.h
new file mode 100644
index 0000000..87e0b18
--- /dev/null
+++ b/cpp/wiiusej/include/jawt.h
@@ -0,0 +1,278 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+#ifndef _JAVASOFT_JAWT_H_
+#define _JAVASOFT_JAWT_H_
+
+#include "jni.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * AWT native interface (new in JDK 1.3)
+ *
+ * The AWT native interface allows a native C or C++ application a means
+ * by which to access native structures in AWT. This is to facilitate moving
+ * legacy C and C++ applications to Java and to target the needs of the
+ * community who, at present, wish to do their own native rendering to canvases
+ * for performance reasons. Standard extensions such as Java3D also require a
+ * means to access the underlying native data structures of AWT.
+ *
+ * There may be future extensions to this API depending on demand.
+ *
+ * A VM does not have to implement this API in order to pass the JCK.
+ * It is recommended, however, that this API is implemented on VMs that support
+ * standard extensions, such as Java3D.
+ *
+ * Since this is a native API, any program which uses it cannot be considered
+ * 100% pure java.
+ */
+
+/*
+ * AWT Native Drawing Surface (JAWT_DrawingSurface).
+ *
+ * For each platform, there is a native drawing surface structure. This
+ * platform-specific structure can be found in jawt_md.h. It is recommended
+ * that additional platforms follow the same model. It is also recommended
+ * that VMs on Win32 and Solaris support the existing structures in jawt_md.h.
+ *
+ *******************
+ * EXAMPLE OF USAGE:
+ *******************
+ *
+ * In Win32, a programmer wishes to access the HWND of a canvas to perform
+ * native rendering into it. The programmer has declared the paint() method
+ * for their canvas subclass to be native:
+ *
+ *
+ * MyCanvas.java:
+ *
+ * import java.awt.*;
+ *
+ * public class MyCanvas extends Canvas {
+ *
+ * static {
+ * System.loadLibrary("mylib");
+ * }
+ *
+ * public native void paint(Graphics g);
+ * }
+ *
+ *
+ * myfile.c:
+ *
+ * #include "jawt_md.h"
+ * #include
+ *
+ * JNIEXPORT void JNICALL
+ * Java_MyCanvas_paint(JNIEnv* env, jobject canvas, jobject graphics)
+ * {
+ * JAWT awt;
+ * JAWT_DrawingSurface* ds;
+ * JAWT_DrawingSurfaceInfo* dsi;
+ * JAWT_Win32DrawingSurfaceInfo* dsi_win;
+ * jboolean result;
+ * jint lock;
+ *
+ * // Get the AWT
+ * awt.version = JAWT_VERSION_1_3;
+ * result = JAWT_GetAWT(env, &awt);
+ * assert(result != JNI_FALSE);
+ *
+ * // Get the drawing surface
+ * ds = awt.GetDrawingSurface(env, canvas);
+ * assert(ds != NULL);
+ *
+ * // Lock the drawing surface
+ * lock = ds->Lock(ds);
+ * assert((lock & JAWT_LOCK_ERROR) == 0);
+ *
+ * // Get the drawing surface info
+ * dsi = ds->GetDrawingSurfaceInfo(ds);
+ *
+ * // Get the platform-specific drawing info
+ * dsi_win = (JAWT_Win32DrawingSurfaceInfo*)dsi->platformInfo;
+ *
+ * //////////////////////////////
+ * // !!! DO PAINTING HERE !!! //
+ * //////////////////////////////
+ *
+ * // Free the drawing surface info
+ * ds->FreeDrawingSurfaceInfo(dsi);
+ *
+ * // Unlock the drawing surface
+ * ds->Unlock(ds);
+ *
+ * // Free the drawing surface
+ * awt.FreeDrawingSurface(ds);
+ * }
+ *
+ */
+
+/*
+ * JAWT_Rectangle
+ * Structure for a native rectangle.
+ */
+typedef struct jawt_Rectangle {
+ jint x;
+ jint y;
+ jint width;
+ jint height;
+} JAWT_Rectangle;
+
+struct jawt_DrawingSurface;
+
+/*
+ * JAWT_DrawingSurfaceInfo
+ * Structure for containing the underlying drawing information of a component.
+ */
+typedef struct jawt_DrawingSurfaceInfo {
+ /*
+ * Pointer to the platform-specific information. This can be safely
+ * cast to a JAWT_Win32DrawingSurfaceInfo on Windows or a
+ * JAWT_X11DrawingSurfaceInfo on Solaris. See jawt_md.h for details.
+ */
+ void* platformInfo;
+ /* Cached pointer to the underlying drawing surface */
+ struct jawt_DrawingSurface* ds;
+ /* Bounding rectangle of the drawing surface */
+ JAWT_Rectangle bounds;
+ /* Number of rectangles in the clip */
+ jint clipSize;
+ /* Clip rectangle array */
+ JAWT_Rectangle* clip;
+} JAWT_DrawingSurfaceInfo;
+
+#define JAWT_LOCK_ERROR 0x00000001
+#define JAWT_LOCK_CLIP_CHANGED 0x00000002
+#define JAWT_LOCK_BOUNDS_CHANGED 0x00000004
+#define JAWT_LOCK_SURFACE_CHANGED 0x00000008
+
+/*
+ * JAWT_DrawingSurface
+ * Structure for containing the underlying drawing information of a component.
+ * All operations on a JAWT_DrawingSurface MUST be performed from the same
+ * thread as the call to GetDrawingSurface.
+ */
+typedef struct jawt_DrawingSurface {
+ /*
+ * Cached reference to the Java environment of the calling thread.
+ * If Lock(), Unlock(), GetDrawingSurfaceInfo() or
+ * FreeDrawingSurfaceInfo() are called from a different thread,
+ * this data member should be set before calling those functions.
+ */
+ JNIEnv* env;
+ /* Cached reference to the target object */
+ jobject target;
+ /*
+ * Lock the surface of the target component for native rendering.
+ * When finished drawing, the surface must be unlocked with
+ * Unlock(). This function returns a bitmask with one or more of the
+ * following values:
+ *
+ * JAWT_LOCK_ERROR - When an error has occurred and the surface could not
+ * be locked.
+ *
+ * JAWT_LOCK_CLIP_CHANGED - When the clip region has changed.
+ *
+ * JAWT_LOCK_BOUNDS_CHANGED - When the bounds of the surface have changed.
+ *
+ * JAWT_LOCK_SURFACE_CHANGED - When the surface itself has changed
+ */
+ jint (JNICALL *Lock)
+ (struct jawt_DrawingSurface* ds);
+ /*
+ * Get the drawing surface info.
+ * The value returned may be cached, but the values may change if
+ * additional calls to Lock() or Unlock() are made.
+ * Lock() must be called before this can return a valid value.
+ * Returns NULL if an error has occurred.
+ * When finished with the returned value, FreeDrawingSurfaceInfo must be
+ * called.
+ */
+ JAWT_DrawingSurfaceInfo* (JNICALL *GetDrawingSurfaceInfo)
+ (struct jawt_DrawingSurface* ds);
+ /*
+ * Free the drawing surface info.
+ */
+ void (JNICALL *FreeDrawingSurfaceInfo)
+ (JAWT_DrawingSurfaceInfo* dsi);
+ /*
+ * Unlock the drawing surface of the target component for native rendering.
+ */
+ void (JNICALL *Unlock)
+ (struct jawt_DrawingSurface* ds);
+} JAWT_DrawingSurface;
+
+/*
+ * JAWT
+ * Structure for containing native AWT functions.
+ */
+typedef struct jawt {
+ /*
+ * Version of this structure. This must always be set before
+ * calling JAWT_GetAWT()
+ */
+ jint version;
+ /*
+ * Return a drawing surface from a target jobject. This value
+ * may be cached.
+ * Returns NULL if an error has occurred.
+ * Target must be a java.awt.Component (should be a Canvas
+ * or Window for native rendering).
+ * FreeDrawingSurface() must be called when finished with the
+ * returned JAWT_DrawingSurface.
+ */
+ JAWT_DrawingSurface* (JNICALL *GetDrawingSurface)
+ (JNIEnv* env, jobject target);
+ /*
+ * Free the drawing surface allocated in GetDrawingSurface.
+ */
+ void (JNICALL *FreeDrawingSurface)
+ (JAWT_DrawingSurface* ds);
+ /*
+ * Since 1.4
+ * Locks the entire AWT for synchronization purposes
+ */
+ void (JNICALL *Lock)(JNIEnv* env);
+ /*
+ * Since 1.4
+ * Unlocks the entire AWT for synchronization purposes
+ */
+ void (JNICALL *Unlock)(JNIEnv* env);
+ /*
+ * Since 1.4
+ * Returns a reference to a java.awt.Component from a native
+ * platform handle. On Windows, this corresponds to an HWND;
+ * on Solaris and Linux, this is a Drawable. For other platforms,
+ * see the appropriate machine-dependent header file for a description.
+ * The reference returned by this function is a local
+ * reference that is only valid in this environment.
+ * This function returns a NULL reference if no component could be
+ * found with matching platform information.
+ */
+ jobject (JNICALL *GetComponent)(JNIEnv* env, void* platformInfo);
+
+} JAWT;
+
+/*
+ * Get the AWT native structure. This function returns JNI_FALSE if
+ * an error occurs.
+ */
+_JNI_IMPORT_OR_EXPORT_
+jboolean JNICALL JAWT_GetAWT(JNIEnv* env, JAWT* awt);
+
+#define JAWT_VERSION_1_3 0x00010003
+#define JAWT_VERSION_1_4 0x00010004
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif /* !_JAVASOFT_JAWT_H_ */
diff --git a/cpp/wiiusej/include/jdwpTransport.h b/cpp/wiiusej/include/jdwpTransport.h
new file mode 100644
index 0000000..eae435a
--- /dev/null
+++ b/cpp/wiiusej/include/jdwpTransport.h
@@ -0,0 +1,237 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+/*
+ * Java Debug Wire Protocol Transport Service Provider Interface.
+ */
+
+#ifndef JDWPTRANSPORT_H
+#define JDWPTRANSPORT_H
+
+#include "jni.h"
+
+enum {
+ JDWPTRANSPORT_VERSION_1_0 = 0x00010000
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct jdwpTransportNativeInterface_;
+
+struct _jdwpTransportEnv;
+
+#ifdef __cplusplus
+typedef _jdwpTransportEnv jdwpTransportEnv;
+#else
+typedef const struct jdwpTransportNativeInterface_ *jdwpTransportEnv;
+#endif /* __cplusplus */
+
+/*
+ * Errors. Universal errors with JVMTI/JVMDI equivalents keep the
+ * values the same.
+ */
+typedef enum {
+ JDWPTRANSPORT_ERROR_NONE = 0,
+ JDWPTRANSPORT_ERROR_ILLEGAL_ARGUMENT = 103,
+ JDWPTRANSPORT_ERROR_OUT_OF_MEMORY = 110,
+ JDWPTRANSPORT_ERROR_INTERNAL = 113,
+ JDWPTRANSPORT_ERROR_ILLEGAL_STATE = 201,
+ JDWPTRANSPORT_ERROR_IO_ERROR = 202,
+ JDWPTRANSPORT_ERROR_TIMEOUT = 203,
+ JDWPTRANSPORT_ERROR_MSG_NOT_AVAILABLE = 204
+} jdwpTransportError;
+
+
+/*
+ * Structure to define capabilities
+ */
+typedef struct {
+ unsigned int can_timeout_attach :1;
+ unsigned int can_timeout_accept :1;
+ unsigned int can_timeout_handshake :1;
+ unsigned int reserved3 :1;
+ unsigned int reserved4 :1;
+ unsigned int reserved5 :1;
+ unsigned int reserved6 :1;
+ unsigned int reserved7 :1;
+ unsigned int reserved8 :1;
+ unsigned int reserved9 :1;
+ unsigned int reserved10 :1;
+ unsigned int reserved11 :1;
+ unsigned int reserved12 :1;
+ unsigned int reserved13 :1;
+ unsigned int reserved14 :1;
+ unsigned int reserved15 :1;
+} JDWPTransportCapabilities;
+
+
+/*
+ * Structures to define packet layout.
+ *
+ * See: http://java.sun.com/j2se/1.5/docs/guide/jpda/jdwp-spec.html
+ */
+
+enum {
+ JDWPTRANSPORT_FLAGS_NONE = 0x0,
+ JDWPTRANSPORT_FLAGS_REPLY = 0x80
+};
+
+typedef struct {
+ jint len;
+ jint id;
+ jbyte flags;
+ jbyte cmdSet;
+ jbyte cmd;
+ jbyte *data;
+} jdwpCmdPacket;
+
+typedef struct {
+ jint len;
+ jint id;
+ jbyte flags;
+ jshort errorCode;
+ jbyte *data;
+} jdwpReplyPacket;
+
+typedef struct {
+ union {
+ jdwpCmdPacket cmd;
+ jdwpReplyPacket reply;
+ } type;
+} jdwpPacket;
+
+/*
+ * JDWP functions called by the transport.
+ */
+typedef struct jdwpTransportCallback {
+ void *(*alloc)(jint numBytes); /* Call this for all allocations */
+ void (*free)(void *buffer); /* Call this for all deallocations */
+} jdwpTransportCallback;
+
+typedef jint (JNICALL *jdwpTransport_OnLoad_t)(JavaVM *jvm,
+ jdwpTransportCallback *callback,
+ jint version,
+ jdwpTransportEnv** env);
+
+
+
+/* Function Interface */
+
+struct jdwpTransportNativeInterface_ {
+ /* 1 : RESERVED */
+ void *reserved1;
+
+ /* 2 : Get Capabilities */
+ jdwpTransportError (JNICALL *GetCapabilities)(jdwpTransportEnv* env,
+ JDWPTransportCapabilities *capabilities_ptr);
+
+ /* 3 : Attach */
+ jdwpTransportError (JNICALL *Attach)(jdwpTransportEnv* env,
+ const char* address,
+ jlong attach_timeout,
+ jlong handshake_timeout);
+
+ /* 4: StartListening */
+ jdwpTransportError (JNICALL *StartListening)(jdwpTransportEnv* env,
+ const char* address,
+ char** actual_address);
+
+ /* 5: StopListening */
+ jdwpTransportError (JNICALL *StopListening)(jdwpTransportEnv* env);
+
+ /* 6: Accept */
+ jdwpTransportError (JNICALL *Accept)(jdwpTransportEnv* env,
+ jlong accept_timeout,
+ jlong handshake_timeout);
+
+ /* 7: IsOpen */
+ jboolean (JNICALL *IsOpen)(jdwpTransportEnv* env);
+
+ /* 8: Close */
+ jdwpTransportError (JNICALL *Close)(jdwpTransportEnv* env);
+
+ /* 9: ReadPacket */
+ jdwpTransportError (JNICALL *ReadPacket)(jdwpTransportEnv* env,
+ jdwpPacket *pkt);
+
+ /* 10: Write Packet */
+ jdwpTransportError (JNICALL *WritePacket)(jdwpTransportEnv* env,
+ const jdwpPacket* pkt);
+
+ /* 11: GetLastError */
+ jdwpTransportError (JNICALL *GetLastError)(jdwpTransportEnv* env,
+ char** error);
+
+};
+
+
+/*
+ * Use inlined functions so that C++ code can use syntax such as
+ * env->Attach("mymachine:5000", 10*1000, 0);
+ *
+ * rather than using C's :-
+ *
+ * (*env)->Attach(env, "mymachine:5000", 10*1000, 0);
+ */
+struct _jdwpTransportEnv {
+ const struct jdwpTransportNativeInterface_ *functions;
+#ifdef __cplusplus
+
+ jdwpTransportError GetCapabilities(JDWPTransportCapabilities *capabilities_ptr) {
+ return functions->GetCapabilities(this, capabilities_ptr);
+ }
+
+ jdwpTransportError Attach(const char* address, jlong attach_timeout,
+ jlong handshake_timeout) {
+ return functions->Attach(this, address, attach_timeout, handshake_timeout);
+ }
+
+ jdwpTransportError StartListening(const char* address,
+ char** actual_address) {
+ return functions->StartListening(this, address, actual_address);
+ }
+
+ jdwpTransportError StopListening(void) {
+ return functions->StopListening(this);
+ }
+
+ jdwpTransportError Accept(jlong accept_timeout, jlong handshake_timeout) {
+ return functions->Accept(this, accept_timeout, handshake_timeout);
+ }
+
+ jboolean IsOpen(void) {
+ return functions->IsOpen(this);
+ }
+
+ jdwpTransportError Close(void) {
+ return functions->Close(this);
+ }
+
+ jdwpTransportError ReadPacket(jdwpPacket *pkt) {
+ return functions->ReadPacket(this, pkt);
+ }
+
+ jdwpTransportError WritePacket(const jdwpPacket* pkt) {
+ return functions->WritePacket(this, pkt);
+ }
+
+ jdwpTransportError GetLastError(char** error) {
+ return functions->GetLastError(this, error);
+ }
+
+
+#endif /* __cplusplus */
+};
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* JDWPTRANSPORT_H */
+
diff --git a/cpp/wiiusej/include/jni.h b/cpp/wiiusej/include/jni.h
new file mode 100644
index 0000000..8ed7366
--- /dev/null
+++ b/cpp/wiiusej/include/jni.h
@@ -0,0 +1,1944 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+/*
+ * We used part of Netscape's Java Runtime Interface (JRI) as the starting
+ * point of our design and implementation.
+ */
+
+/******************************************************************************
+ * Java Runtime Interface
+ * Copyright (c) 1996 Netscape Communications Corporation. All rights reserved.
+ *****************************************************************************/
+
+#ifndef _JAVASOFT_JNI_H_
+#define _JAVASOFT_JNI_H_
+
+#include
+#include
+
+/* jni_md.h contains the machine-dependent typedefs for jbyte, jint
+ and jlong */
+
+#include "jni_md.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * JNI Types
+ */
+
+#ifndef JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H
+
+typedef unsigned char jboolean;
+typedef unsigned short jchar;
+typedef short jshort;
+typedef float jfloat;
+typedef double jdouble;
+
+typedef jint jsize;
+
+#ifdef __cplusplus
+
+class _jobject {};
+class _jclass : public _jobject {};
+class _jthrowable : public _jobject {};
+class _jstring : public _jobject {};
+class _jarray : public _jobject {};
+class _jbooleanArray : public _jarray {};
+class _jbyteArray : public _jarray {};
+class _jcharArray : public _jarray {};
+class _jshortArray : public _jarray {};
+class _jintArray : public _jarray {};
+class _jlongArray : public _jarray {};
+class _jfloatArray : public _jarray {};
+class _jdoubleArray : public _jarray {};
+class _jobjectArray : public _jarray {};
+
+typedef _jobject *jobject;
+typedef _jclass *jclass;
+typedef _jthrowable *jthrowable;
+typedef _jstring *jstring;
+typedef _jarray *jarray;
+typedef _jbooleanArray *jbooleanArray;
+typedef _jbyteArray *jbyteArray;
+typedef _jcharArray *jcharArray;
+typedef _jshortArray *jshortArray;
+typedef _jintArray *jintArray;
+typedef _jlongArray *jlongArray;
+typedef _jfloatArray *jfloatArray;
+typedef _jdoubleArray *jdoubleArray;
+typedef _jobjectArray *jobjectArray;
+
+#else
+
+struct _jobject;
+
+typedef struct _jobject *jobject;
+typedef jobject jclass;
+typedef jobject jthrowable;
+typedef jobject jstring;
+typedef jobject jarray;
+typedef jarray jbooleanArray;
+typedef jarray jbyteArray;
+typedef jarray jcharArray;
+typedef jarray jshortArray;
+typedef jarray jintArray;
+typedef jarray jlongArray;
+typedef jarray jfloatArray;
+typedef jarray jdoubleArray;
+typedef jarray jobjectArray;
+
+#endif
+
+typedef jobject jweak;
+
+typedef union jvalue {
+ jboolean z;
+ jbyte b;
+ jchar c;
+ jshort s;
+ jint i;
+ jlong j;
+ jfloat f;
+ jdouble d;
+ jobject l;
+} jvalue;
+
+struct _jfieldID;
+typedef struct _jfieldID *jfieldID;
+
+struct _jmethodID;
+typedef struct _jmethodID *jmethodID;
+
+/* Return values from jobjectRefType */
+typedef enum _jobjectType {
+ JNIInvalidRefType = 0,
+ JNILocalRefType = 1,
+ JNIGlobalRefType = 2,
+ JNIWeakGlobalRefType = 3
+} jobjectRefType;
+
+
+#endif /* JNI_TYPES_ALREADY_DEFINED_IN_JNI_MD_H */
+
+/*
+ * jboolean constants
+ */
+
+#define JNI_FALSE 0
+#define JNI_TRUE 1
+
+/*
+ * possible return values for JNI functions.
+ */
+
+#define JNI_OK 0 /* success */
+#define JNI_ERR (-1) /* unknown error */
+#define JNI_EDETACHED (-2) /* thread detached from the VM */
+#define JNI_EVERSION (-3) /* JNI version error */
+#define JNI_ENOMEM (-4) /* not enough memory */
+#define JNI_EEXIST (-5) /* VM already created */
+#define JNI_EINVAL (-6) /* invalid arguments */
+
+/*
+ * used in ReleaseScalarArrayElements
+ */
+
+#define JNI_COMMIT 1
+#define JNI_ABORT 2
+
+/*
+ * used in RegisterNatives to describe native method name, signature,
+ * and function pointer.
+ */
+
+typedef struct {
+ char *name;
+ char *signature;
+ void *fnPtr;
+} JNINativeMethod;
+
+/*
+ * JNI Native Method Interface.
+ */
+
+struct JNINativeInterface_;
+
+struct JNIEnv_;
+
+#ifdef __cplusplus
+typedef JNIEnv_ JNIEnv;
+#else
+typedef const struct JNINativeInterface_ *JNIEnv;
+#endif
+
+/*
+ * JNI Invocation Interface.
+ */
+
+struct JNIInvokeInterface_;
+
+struct JavaVM_;
+
+#ifdef __cplusplus
+typedef JavaVM_ JavaVM;
+#else
+typedef const struct JNIInvokeInterface_ *JavaVM;
+#endif
+
+struct JNINativeInterface_ {
+ void *reserved0;
+ void *reserved1;
+ void *reserved2;
+
+ void *reserved3;
+ jint (JNICALL *GetVersion)(JNIEnv *env);
+
+ jclass (JNICALL *DefineClass)
+ (JNIEnv *env, const char *name, jobject loader, const jbyte *buf,
+ jsize len);
+ jclass (JNICALL *FindClass)
+ (JNIEnv *env, const char *name);
+
+ jmethodID (JNICALL *FromReflectedMethod)
+ (JNIEnv *env, jobject method);
+ jfieldID (JNICALL *FromReflectedField)
+ (JNIEnv *env, jobject field);
+
+ jobject (JNICALL *ToReflectedMethod)
+ (JNIEnv *env, jclass cls, jmethodID methodID, jboolean isStatic);
+
+ jclass (JNICALL *GetSuperclass)
+ (JNIEnv *env, jclass sub);
+ jboolean (JNICALL *IsAssignableFrom)
+ (JNIEnv *env, jclass sub, jclass sup);
+
+ jobject (JNICALL *ToReflectedField)
+ (JNIEnv *env, jclass cls, jfieldID fieldID, jboolean isStatic);
+
+ jint (JNICALL *Throw)
+ (JNIEnv *env, jthrowable obj);
+ jint (JNICALL *ThrowNew)
+ (JNIEnv *env, jclass clazz, const char *msg);
+ jthrowable (JNICALL *ExceptionOccurred)
+ (JNIEnv *env);
+ void (JNICALL *ExceptionDescribe)
+ (JNIEnv *env);
+ void (JNICALL *ExceptionClear)
+ (JNIEnv *env);
+ void (JNICALL *FatalError)
+ (JNIEnv *env, const char *msg);
+
+ jint (JNICALL *PushLocalFrame)
+ (JNIEnv *env, jint capacity);
+ jobject (JNICALL *PopLocalFrame)
+ (JNIEnv *env, jobject result);
+
+ jobject (JNICALL *NewGlobalRef)
+ (JNIEnv *env, jobject lobj);
+ void (JNICALL *DeleteGlobalRef)
+ (JNIEnv *env, jobject gref);
+ void (JNICALL *DeleteLocalRef)
+ (JNIEnv *env, jobject obj);
+ jboolean (JNICALL *IsSameObject)
+ (JNIEnv *env, jobject obj1, jobject obj2);
+ jobject (JNICALL *NewLocalRef)
+ (JNIEnv *env, jobject ref);
+ jint (JNICALL *EnsureLocalCapacity)
+ (JNIEnv *env, jint capacity);
+
+ jobject (JNICALL *AllocObject)
+ (JNIEnv *env, jclass clazz);
+ jobject (JNICALL *NewObject)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jobject (JNICALL *NewObjectV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jobject (JNICALL *NewObjectA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jclass (JNICALL *GetObjectClass)
+ (JNIEnv *env, jobject obj);
+ jboolean (JNICALL *IsInstanceOf)
+ (JNIEnv *env, jobject obj, jclass clazz);
+
+ jmethodID (JNICALL *GetMethodID)
+ (JNIEnv *env, jclass clazz, const char *name, const char *sig);
+
+ jobject (JNICALL *CallObjectMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jobject (JNICALL *CallObjectMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jobject (JNICALL *CallObjectMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);
+
+ jboolean (JNICALL *CallBooleanMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jboolean (JNICALL *CallBooleanMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jboolean (JNICALL *CallBooleanMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);
+
+ jbyte (JNICALL *CallByteMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jbyte (JNICALL *CallByteMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jbyte (JNICALL *CallByteMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jchar (JNICALL *CallCharMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jchar (JNICALL *CallCharMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jchar (JNICALL *CallCharMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jshort (JNICALL *CallShortMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jshort (JNICALL *CallShortMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jshort (JNICALL *CallShortMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jint (JNICALL *CallIntMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jint (JNICALL *CallIntMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jint (JNICALL *CallIntMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jlong (JNICALL *CallLongMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jlong (JNICALL *CallLongMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jlong (JNICALL *CallLongMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jfloat (JNICALL *CallFloatMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jfloat (JNICALL *CallFloatMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jfloat (JNICALL *CallFloatMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ jdouble (JNICALL *CallDoubleMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ jdouble (JNICALL *CallDoubleMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ jdouble (JNICALL *CallDoubleMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue *args);
+
+ void (JNICALL *CallVoidMethod)
+ (JNIEnv *env, jobject obj, jmethodID methodID, ...);
+ void (JNICALL *CallVoidMethodV)
+ (JNIEnv *env, jobject obj, jmethodID methodID, va_list args);
+ void (JNICALL *CallVoidMethodA)
+ (JNIEnv *env, jobject obj, jmethodID methodID, const jvalue * args);
+
+ jobject (JNICALL *CallNonvirtualObjectMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jobject (JNICALL *CallNonvirtualObjectMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jobject (JNICALL *CallNonvirtualObjectMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue * args);
+
+ jboolean (JNICALL *CallNonvirtualBooleanMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jboolean (JNICALL *CallNonvirtualBooleanMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jboolean (JNICALL *CallNonvirtualBooleanMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue * args);
+
+ jbyte (JNICALL *CallNonvirtualByteMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jbyte (JNICALL *CallNonvirtualByteMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jbyte (JNICALL *CallNonvirtualByteMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jchar (JNICALL *CallNonvirtualCharMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jchar (JNICALL *CallNonvirtualCharMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jchar (JNICALL *CallNonvirtualCharMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jshort (JNICALL *CallNonvirtualShortMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jshort (JNICALL *CallNonvirtualShortMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jshort (JNICALL *CallNonvirtualShortMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jint (JNICALL *CallNonvirtualIntMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jint (JNICALL *CallNonvirtualIntMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jint (JNICALL *CallNonvirtualIntMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jlong (JNICALL *CallNonvirtualLongMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jlong (JNICALL *CallNonvirtualLongMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jlong (JNICALL *CallNonvirtualLongMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jfloat (JNICALL *CallNonvirtualFloatMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jfloat (JNICALL *CallNonvirtualFloatMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jfloat (JNICALL *CallNonvirtualFloatMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ jdouble (JNICALL *CallNonvirtualDoubleMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ jdouble (JNICALL *CallNonvirtualDoubleMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ jdouble (JNICALL *CallNonvirtualDoubleMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue *args);
+
+ void (JNICALL *CallNonvirtualVoidMethod)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID, ...);
+ void (JNICALL *CallNonvirtualVoidMethodV)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ va_list args);
+ void (JNICALL *CallNonvirtualVoidMethodA)
+ (JNIEnv *env, jobject obj, jclass clazz, jmethodID methodID,
+ const jvalue * args);
+
+ jfieldID (JNICALL *GetFieldID)
+ (JNIEnv *env, jclass clazz, const char *name, const char *sig);
+
+ jobject (JNICALL *GetObjectField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jboolean (JNICALL *GetBooleanField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jbyte (JNICALL *GetByteField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jchar (JNICALL *GetCharField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jshort (JNICALL *GetShortField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jint (JNICALL *GetIntField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jlong (JNICALL *GetLongField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jfloat (JNICALL *GetFloatField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+ jdouble (JNICALL *GetDoubleField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID);
+
+ void (JNICALL *SetObjectField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jobject val);
+ void (JNICALL *SetBooleanField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jboolean val);
+ void (JNICALL *SetByteField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jbyte val);
+ void (JNICALL *SetCharField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jchar val);
+ void (JNICALL *SetShortField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jshort val);
+ void (JNICALL *SetIntField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jint val);
+ void (JNICALL *SetLongField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jlong val);
+ void (JNICALL *SetFloatField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jfloat val);
+ void (JNICALL *SetDoubleField)
+ (JNIEnv *env, jobject obj, jfieldID fieldID, jdouble val);
+
+ jmethodID (JNICALL *GetStaticMethodID)
+ (JNIEnv *env, jclass clazz, const char *name, const char *sig);
+
+ jobject (JNICALL *CallStaticObjectMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jobject (JNICALL *CallStaticObjectMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jobject (JNICALL *CallStaticObjectMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jboolean (JNICALL *CallStaticBooleanMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jboolean (JNICALL *CallStaticBooleanMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jboolean (JNICALL *CallStaticBooleanMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jbyte (JNICALL *CallStaticByteMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jbyte (JNICALL *CallStaticByteMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jbyte (JNICALL *CallStaticByteMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jchar (JNICALL *CallStaticCharMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jchar (JNICALL *CallStaticCharMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jchar (JNICALL *CallStaticCharMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jshort (JNICALL *CallStaticShortMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jshort (JNICALL *CallStaticShortMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jshort (JNICALL *CallStaticShortMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jint (JNICALL *CallStaticIntMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jint (JNICALL *CallStaticIntMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jint (JNICALL *CallStaticIntMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jlong (JNICALL *CallStaticLongMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jlong (JNICALL *CallStaticLongMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jlong (JNICALL *CallStaticLongMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jfloat (JNICALL *CallStaticFloatMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jfloat (JNICALL *CallStaticFloatMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jfloat (JNICALL *CallStaticFloatMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ jdouble (JNICALL *CallStaticDoubleMethod)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, ...);
+ jdouble (JNICALL *CallStaticDoubleMethodV)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, va_list args);
+ jdouble (JNICALL *CallStaticDoubleMethodA)
+ (JNIEnv *env, jclass clazz, jmethodID methodID, const jvalue *args);
+
+ void (JNICALL *CallStaticVoidMethod)
+ (JNIEnv *env, jclass cls, jmethodID methodID, ...);
+ void (JNICALL *CallStaticVoidMethodV)
+ (JNIEnv *env, jclass cls, jmethodID methodID, va_list args);
+ void (JNICALL *CallStaticVoidMethodA)
+ (JNIEnv *env, jclass cls, jmethodID methodID, const jvalue * args);
+
+ jfieldID (JNICALL *GetStaticFieldID)
+ (JNIEnv *env, jclass clazz, const char *name, const char *sig);
+ jobject (JNICALL *GetStaticObjectField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jboolean (JNICALL *GetStaticBooleanField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jbyte (JNICALL *GetStaticByteField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jchar (JNICALL *GetStaticCharField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jshort (JNICALL *GetStaticShortField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jint (JNICALL *GetStaticIntField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jlong (JNICALL *GetStaticLongField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jfloat (JNICALL *GetStaticFloatField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+ jdouble (JNICALL *GetStaticDoubleField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID);
+
+ void (JNICALL *SetStaticObjectField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jobject value);
+ void (JNICALL *SetStaticBooleanField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jboolean value);
+ void (JNICALL *SetStaticByteField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jbyte value);
+ void (JNICALL *SetStaticCharField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jchar value);
+ void (JNICALL *SetStaticShortField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jshort value);
+ void (JNICALL *SetStaticIntField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jint value);
+ void (JNICALL *SetStaticLongField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jlong value);
+ void (JNICALL *SetStaticFloatField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jfloat value);
+ void (JNICALL *SetStaticDoubleField)
+ (JNIEnv *env, jclass clazz, jfieldID fieldID, jdouble value);
+
+ jstring (JNICALL *NewString)
+ (JNIEnv *env, const jchar *unicode, jsize len);
+ jsize (JNICALL *GetStringLength)
+ (JNIEnv *env, jstring str);
+ const jchar *(JNICALL *GetStringChars)
+ (JNIEnv *env, jstring str, jboolean *isCopy);
+ void (JNICALL *ReleaseStringChars)
+ (JNIEnv *env, jstring str, const jchar *chars);
+
+ jstring (JNICALL *NewStringUTF)
+ (JNIEnv *env, const char *utf);
+ jsize (JNICALL *GetStringUTFLength)
+ (JNIEnv *env, jstring str);
+ const char* (JNICALL *GetStringUTFChars)
+ (JNIEnv *env, jstring str, jboolean *isCopy);
+ void (JNICALL *ReleaseStringUTFChars)
+ (JNIEnv *env, jstring str, const char* chars);
+
+
+ jsize (JNICALL *GetArrayLength)
+ (JNIEnv *env, jarray array);
+
+ jobjectArray (JNICALL *NewObjectArray)
+ (JNIEnv *env, jsize len, jclass clazz, jobject init);
+ jobject (JNICALL *GetObjectArrayElement)
+ (JNIEnv *env, jobjectArray array, jsize index);
+ void (JNICALL *SetObjectArrayElement)
+ (JNIEnv *env, jobjectArray array, jsize index, jobject val);
+
+ jbooleanArray (JNICALL *NewBooleanArray)
+ (JNIEnv *env, jsize len);
+ jbyteArray (JNICALL *NewByteArray)
+ (JNIEnv *env, jsize len);
+ jcharArray (JNICALL *NewCharArray)
+ (JNIEnv *env, jsize len);
+ jshortArray (JNICALL *NewShortArray)
+ (JNIEnv *env, jsize len);
+ jintArray (JNICALL *NewIntArray)
+ (JNIEnv *env, jsize len);
+ jlongArray (JNICALL *NewLongArray)
+ (JNIEnv *env, jsize len);
+ jfloatArray (JNICALL *NewFloatArray)
+ (JNIEnv *env, jsize len);
+ jdoubleArray (JNICALL *NewDoubleArray)
+ (JNIEnv *env, jsize len);
+
+ jboolean * (JNICALL *GetBooleanArrayElements)
+ (JNIEnv *env, jbooleanArray array, jboolean *isCopy);
+ jbyte * (JNICALL *GetByteArrayElements)
+ (JNIEnv *env, jbyteArray array, jboolean *isCopy);
+ jchar * (JNICALL *GetCharArrayElements)
+ (JNIEnv *env, jcharArray array, jboolean *isCopy);
+ jshort * (JNICALL *GetShortArrayElements)
+ (JNIEnv *env, jshortArray array, jboolean *isCopy);
+ jint * (JNICALL *GetIntArrayElements)
+ (JNIEnv *env, jintArray array, jboolean *isCopy);
+ jlong * (JNICALL *GetLongArrayElements)
+ (JNIEnv *env, jlongArray array, jboolean *isCopy);
+ jfloat * (JNICALL *GetFloatArrayElements)
+ (JNIEnv *env, jfloatArray array, jboolean *isCopy);
+ jdouble * (JNICALL *GetDoubleArrayElements)
+ (JNIEnv *env, jdoubleArray array, jboolean *isCopy);
+
+ void (JNICALL *ReleaseBooleanArrayElements)
+ (JNIEnv *env, jbooleanArray array, jboolean *elems, jint mode);
+ void (JNICALL *ReleaseByteArrayElements)
+ (JNIEnv *env, jbyteArray array, jbyte *elems, jint mode);
+ void (JNICALL *ReleaseCharArrayElements)
+ (JNIEnv *env, jcharArray array, jchar *elems, jint mode);
+ void (JNICALL *ReleaseShortArrayElements)
+ (JNIEnv *env, jshortArray array, jshort *elems, jint mode);
+ void (JNICALL *ReleaseIntArrayElements)
+ (JNIEnv *env, jintArray array, jint *elems, jint mode);
+ void (JNICALL *ReleaseLongArrayElements)
+ (JNIEnv *env, jlongArray array, jlong *elems, jint mode);
+ void (JNICALL *ReleaseFloatArrayElements)
+ (JNIEnv *env, jfloatArray array, jfloat *elems, jint mode);
+ void (JNICALL *ReleaseDoubleArrayElements)
+ (JNIEnv *env, jdoubleArray array, jdouble *elems, jint mode);
+
+ void (JNICALL *GetBooleanArrayRegion)
+ (JNIEnv *env, jbooleanArray array, jsize start, jsize l, jboolean *buf);
+ void (JNICALL *GetByteArrayRegion)
+ (JNIEnv *env, jbyteArray array, jsize start, jsize len, jbyte *buf);
+ void (JNICALL *GetCharArrayRegion)
+ (JNIEnv *env, jcharArray array, jsize start, jsize len, jchar *buf);
+ void (JNICALL *GetShortArrayRegion)
+ (JNIEnv *env, jshortArray array, jsize start, jsize len, jshort *buf);
+ void (JNICALL *GetIntArrayRegion)
+ (JNIEnv *env, jintArray array, jsize start, jsize len, jint *buf);
+ void (JNICALL *GetLongArrayRegion)
+ (JNIEnv *env, jlongArray array, jsize start, jsize len, jlong *buf);
+ void (JNICALL *GetFloatArrayRegion)
+ (JNIEnv *env, jfloatArray array, jsize start, jsize len, jfloat *buf);
+ void (JNICALL *GetDoubleArrayRegion)
+ (JNIEnv *env, jdoubleArray array, jsize start, jsize len, jdouble *buf);
+
+ void (JNICALL *SetBooleanArrayRegion)
+ (JNIEnv *env, jbooleanArray array, jsize start, jsize l, const jboolean *buf);
+ void (JNICALL *SetByteArrayRegion)
+ (JNIEnv *env, jbyteArray array, jsize start, jsize len, const jbyte *buf);
+ void (JNICALL *SetCharArrayRegion)
+ (JNIEnv *env, jcharArray array, jsize start, jsize len, const jchar *buf);
+ void (JNICALL *SetShortArrayRegion)
+ (JNIEnv *env, jshortArray array, jsize start, jsize len, const jshort *buf);
+ void (JNICALL *SetIntArrayRegion)
+ (JNIEnv *env, jintArray array, jsize start, jsize len, const jint *buf);
+ void (JNICALL *SetLongArrayRegion)
+ (JNIEnv *env, jlongArray array, jsize start, jsize len, const jlong *buf);
+ void (JNICALL *SetFloatArrayRegion)
+ (JNIEnv *env, jfloatArray array, jsize start, jsize len, const jfloat *buf);
+ void (JNICALL *SetDoubleArrayRegion)
+ (JNIEnv *env, jdoubleArray array, jsize start, jsize len, const jdouble *buf);
+
+ jint (JNICALL *RegisterNatives)
+ (JNIEnv *env, jclass clazz, const JNINativeMethod *methods,
+ jint nMethods);
+ jint (JNICALL *UnregisterNatives)
+ (JNIEnv *env, jclass clazz);
+
+ jint (JNICALL *MonitorEnter)
+ (JNIEnv *env, jobject obj);
+ jint (JNICALL *MonitorExit)
+ (JNIEnv *env, jobject obj);
+
+ jint (JNICALL *GetJavaVM)
+ (JNIEnv *env, JavaVM **vm);
+
+ void (JNICALL *GetStringRegion)
+ (JNIEnv *env, jstring str, jsize start, jsize len, jchar *buf);
+ void (JNICALL *GetStringUTFRegion)
+ (JNIEnv *env, jstring str, jsize start, jsize len, char *buf);
+
+ void * (JNICALL *GetPrimitiveArrayCritical)
+ (JNIEnv *env, jarray array, jboolean *isCopy);
+ void (JNICALL *ReleasePrimitiveArrayCritical)
+ (JNIEnv *env, jarray array, void *carray, jint mode);
+
+ const jchar * (JNICALL *GetStringCritical)
+ (JNIEnv *env, jstring string, jboolean *isCopy);
+ void (JNICALL *ReleaseStringCritical)
+ (JNIEnv *env, jstring string, const jchar *cstring);
+
+ jweak (JNICALL *NewWeakGlobalRef)
+ (JNIEnv *env, jobject obj);
+ void (JNICALL *DeleteWeakGlobalRef)
+ (JNIEnv *env, jweak ref);
+
+ jboolean (JNICALL *ExceptionCheck)
+ (JNIEnv *env);
+
+ jobject (JNICALL *NewDirectByteBuffer)
+ (JNIEnv* env, void* address, jlong capacity);
+ void* (JNICALL *GetDirectBufferAddress)
+ (JNIEnv* env, jobject buf);
+ jlong (JNICALL *GetDirectBufferCapacity)
+ (JNIEnv* env, jobject buf);
+
+ /* New JNI 1.6 Features */
+
+ jobjectRefType (JNICALL *GetObjectRefType)
+ (JNIEnv* env, jobject obj);
+};
+
+/*
+ * We use inlined functions for C++ so that programmers can write:
+ *
+ * env->FindClass("java/lang/String")
+ *
+ * in C++ rather than:
+ *
+ * (*env)->FindClass(env, "java/lang/String")
+ *
+ * in C.
+ */
+
+struct JNIEnv_ {
+ const struct JNINativeInterface_ *functions;
+#ifdef __cplusplus
+
+ jint GetVersion() {
+ return functions->GetVersion(this);
+ }
+ jclass DefineClass(const char *name, jobject loader, const jbyte *buf,
+ jsize len) {
+ return functions->DefineClass(this, name, loader, buf, len);
+ }
+ jclass FindClass(const char *name) {
+ return functions->FindClass(this, name);
+ }
+ jmethodID FromReflectedMethod(jobject method) {
+ return functions->FromReflectedMethod(this,method);
+ }
+ jfieldID FromReflectedField(jobject field) {
+ return functions->FromReflectedField(this,field);
+ }
+
+ jobject ToReflectedMethod(jclass cls, jmethodID methodID, jboolean isStatic) {
+ return functions->ToReflectedMethod(this, cls, methodID, isStatic);
+ }
+
+ jclass GetSuperclass(jclass sub) {
+ return functions->GetSuperclass(this, sub);
+ }
+ jboolean IsAssignableFrom(jclass sub, jclass sup) {
+ return functions->IsAssignableFrom(this, sub, sup);
+ }
+
+ jobject ToReflectedField(jclass cls, jfieldID fieldID, jboolean isStatic) {
+ return functions->ToReflectedField(this,cls,fieldID,isStatic);
+ }
+
+ jint Throw(jthrowable obj) {
+ return functions->Throw(this, obj);
+ }
+ jint ThrowNew(jclass clazz, const char *msg) {
+ return functions->ThrowNew(this, clazz, msg);
+ }
+ jthrowable ExceptionOccurred() {
+ return functions->ExceptionOccurred(this);
+ }
+ void ExceptionDescribe() {
+ functions->ExceptionDescribe(this);
+ }
+ void ExceptionClear() {
+ functions->ExceptionClear(this);
+ }
+ void FatalError(const char *msg) {
+ functions->FatalError(this, msg);
+ }
+
+ jint PushLocalFrame(jint capacity) {
+ return functions->PushLocalFrame(this,capacity);
+ }
+ jobject PopLocalFrame(jobject result) {
+ return functions->PopLocalFrame(this,result);
+ }
+
+ jobject NewGlobalRef(jobject lobj) {
+ return functions->NewGlobalRef(this,lobj);
+ }
+ void DeleteGlobalRef(jobject gref) {
+ functions->DeleteGlobalRef(this,gref);
+ }
+ void DeleteLocalRef(jobject obj) {
+ functions->DeleteLocalRef(this, obj);
+ }
+
+ jboolean IsSameObject(jobject obj1, jobject obj2) {
+ return functions->IsSameObject(this,obj1,obj2);
+ }
+
+ jobject NewLocalRef(jobject ref) {
+ return functions->NewLocalRef(this,ref);
+ }
+ jint EnsureLocalCapacity(jint capacity) {
+ return functions->EnsureLocalCapacity(this,capacity);
+ }
+
+ jobject AllocObject(jclass clazz) {
+ return functions->AllocObject(this,clazz);
+ }
+ jobject NewObject(jclass clazz, jmethodID methodID, ...) {
+ va_list args;
+ jobject result;
+ va_start(args, methodID);
+ result = functions->NewObjectV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jobject NewObjectV(jclass clazz, jmethodID methodID,
+ va_list args) {
+ return functions->NewObjectV(this,clazz,methodID,args);
+ }
+ jobject NewObjectA(jclass clazz, jmethodID methodID,
+ const jvalue *args) {
+ return functions->NewObjectA(this,clazz,methodID,args);
+ }
+
+ jclass GetObjectClass(jobject obj) {
+ return functions->GetObjectClass(this,obj);
+ }
+ jboolean IsInstanceOf(jobject obj, jclass clazz) {
+ return functions->IsInstanceOf(this,obj,clazz);
+ }
+
+ jmethodID GetMethodID(jclass clazz, const char *name,
+ const char *sig) {
+ return functions->GetMethodID(this,clazz,name,sig);
+ }
+
+ jobject CallObjectMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jobject result;
+ va_start(args,methodID);
+ result = functions->CallObjectMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jobject CallObjectMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallObjectMethodV(this,obj,methodID,args);
+ }
+ jobject CallObjectMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallObjectMethodA(this,obj,methodID,args);
+ }
+
+ jboolean CallBooleanMethod(jobject obj,
+ jmethodID methodID, ...) {
+ va_list args;
+ jboolean result;
+ va_start(args,methodID);
+ result = functions->CallBooleanMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jboolean CallBooleanMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallBooleanMethodV(this,obj,methodID,args);
+ }
+ jboolean CallBooleanMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallBooleanMethodA(this,obj,methodID, args);
+ }
+
+ jbyte CallByteMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jbyte result;
+ va_start(args,methodID);
+ result = functions->CallByteMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jbyte CallByteMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallByteMethodV(this,obj,methodID,args);
+ }
+ jbyte CallByteMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallByteMethodA(this,obj,methodID,args);
+ }
+
+ jchar CallCharMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jchar result;
+ va_start(args,methodID);
+ result = functions->CallCharMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jchar CallCharMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallCharMethodV(this,obj,methodID,args);
+ }
+ jchar CallCharMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallCharMethodA(this,obj,methodID,args);
+ }
+
+ jshort CallShortMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jshort result;
+ va_start(args,methodID);
+ result = functions->CallShortMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jshort CallShortMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallShortMethodV(this,obj,methodID,args);
+ }
+ jshort CallShortMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallShortMethodA(this,obj,methodID,args);
+ }
+
+ jint CallIntMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jint result;
+ va_start(args,methodID);
+ result = functions->CallIntMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jint CallIntMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallIntMethodV(this,obj,methodID,args);
+ }
+ jint CallIntMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallIntMethodA(this,obj,methodID,args);
+ }
+
+ jlong CallLongMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jlong result;
+ va_start(args,methodID);
+ result = functions->CallLongMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jlong CallLongMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallLongMethodV(this,obj,methodID,args);
+ }
+ jlong CallLongMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallLongMethodA(this,obj,methodID,args);
+ }
+
+ jfloat CallFloatMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jfloat result;
+ va_start(args,methodID);
+ result = functions->CallFloatMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jfloat CallFloatMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallFloatMethodV(this,obj,methodID,args);
+ }
+ jfloat CallFloatMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallFloatMethodA(this,obj,methodID,args);
+ }
+
+ jdouble CallDoubleMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ jdouble result;
+ va_start(args,methodID);
+ result = functions->CallDoubleMethodV(this,obj,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jdouble CallDoubleMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ return functions->CallDoubleMethodV(this,obj,methodID,args);
+ }
+ jdouble CallDoubleMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallDoubleMethodA(this,obj,methodID,args);
+ }
+
+ void CallVoidMethod(jobject obj, jmethodID methodID, ...) {
+ va_list args;
+ va_start(args,methodID);
+ functions->CallVoidMethodV(this,obj,methodID,args);
+ va_end(args);
+ }
+ void CallVoidMethodV(jobject obj, jmethodID methodID,
+ va_list args) {
+ functions->CallVoidMethodV(this,obj,methodID,args);
+ }
+ void CallVoidMethodA(jobject obj, jmethodID methodID,
+ const jvalue * args) {
+ functions->CallVoidMethodA(this,obj,methodID,args);
+ }
+
+ jobject CallNonvirtualObjectMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jobject result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualObjectMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jobject CallNonvirtualObjectMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualObjectMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jobject CallNonvirtualObjectMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualObjectMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jboolean CallNonvirtualBooleanMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jboolean result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualBooleanMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jboolean CallNonvirtualBooleanMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualBooleanMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jboolean CallNonvirtualBooleanMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualBooleanMethodA(this,obj,clazz,
+ methodID, args);
+ }
+
+ jbyte CallNonvirtualByteMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jbyte result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualByteMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jbyte CallNonvirtualByteMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualByteMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jbyte CallNonvirtualByteMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualByteMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jchar CallNonvirtualCharMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jchar result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualCharMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jchar CallNonvirtualCharMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualCharMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jchar CallNonvirtualCharMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualCharMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jshort CallNonvirtualShortMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jshort result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualShortMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jshort CallNonvirtualShortMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualShortMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jshort CallNonvirtualShortMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualShortMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jint CallNonvirtualIntMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jint result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualIntMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jint CallNonvirtualIntMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualIntMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jint CallNonvirtualIntMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualIntMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jlong CallNonvirtualLongMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jlong result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualLongMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jlong CallNonvirtualLongMethodV(jobject obj, jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallNonvirtualLongMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jlong CallNonvirtualLongMethodA(jobject obj, jclass clazz,
+ jmethodID methodID, const jvalue * args) {
+ return functions->CallNonvirtualLongMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jfloat CallNonvirtualFloatMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jfloat result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualFloatMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jfloat CallNonvirtualFloatMethodV(jobject obj, jclass clazz,
+ jmethodID methodID,
+ va_list args) {
+ return functions->CallNonvirtualFloatMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jfloat CallNonvirtualFloatMethodA(jobject obj, jclass clazz,
+ jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallNonvirtualFloatMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ jdouble CallNonvirtualDoubleMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jdouble result;
+ va_start(args,methodID);
+ result = functions->CallNonvirtualDoubleMethodV(this,obj,clazz,
+ methodID,args);
+ va_end(args);
+ return result;
+ }
+ jdouble CallNonvirtualDoubleMethodV(jobject obj, jclass clazz,
+ jmethodID methodID,
+ va_list args) {
+ return functions->CallNonvirtualDoubleMethodV(this,obj,clazz,
+ methodID,args);
+ }
+ jdouble CallNonvirtualDoubleMethodA(jobject obj, jclass clazz,
+ jmethodID methodID,
+ const jvalue * args) {
+ return functions->CallNonvirtualDoubleMethodA(this,obj,clazz,
+ methodID,args);
+ }
+
+ void CallNonvirtualVoidMethod(jobject obj, jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ va_start(args,methodID);
+ functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args);
+ va_end(args);
+ }
+ void CallNonvirtualVoidMethodV(jobject obj, jclass clazz,
+ jmethodID methodID,
+ va_list args) {
+ functions->CallNonvirtualVoidMethodV(this,obj,clazz,methodID,args);
+ }
+ void CallNonvirtualVoidMethodA(jobject obj, jclass clazz,
+ jmethodID methodID,
+ const jvalue * args) {
+ functions->CallNonvirtualVoidMethodA(this,obj,clazz,methodID,args);
+ }
+
+ jfieldID GetFieldID(jclass clazz, const char *name,
+ const char *sig) {
+ return functions->GetFieldID(this,clazz,name,sig);
+ }
+
+ jobject GetObjectField(jobject obj, jfieldID fieldID) {
+ return functions->GetObjectField(this,obj,fieldID);
+ }
+ jboolean GetBooleanField(jobject obj, jfieldID fieldID) {
+ return functions->GetBooleanField(this,obj,fieldID);
+ }
+ jbyte GetByteField(jobject obj, jfieldID fieldID) {
+ return functions->GetByteField(this,obj,fieldID);
+ }
+ jchar GetCharField(jobject obj, jfieldID fieldID) {
+ return functions->GetCharField(this,obj,fieldID);
+ }
+ jshort GetShortField(jobject obj, jfieldID fieldID) {
+ return functions->GetShortField(this,obj,fieldID);
+ }
+ jint GetIntField(jobject obj, jfieldID fieldID) {
+ return functions->GetIntField(this,obj,fieldID);
+ }
+ jlong GetLongField(jobject obj, jfieldID fieldID) {
+ return functions->GetLongField(this,obj,fieldID);
+ }
+ jfloat GetFloatField(jobject obj, jfieldID fieldID) {
+ return functions->GetFloatField(this,obj,fieldID);
+ }
+ jdouble GetDoubleField(jobject obj, jfieldID fieldID) {
+ return functions->GetDoubleField(this,obj,fieldID);
+ }
+
+ void SetObjectField(jobject obj, jfieldID fieldID, jobject val) {
+ functions->SetObjectField(this,obj,fieldID,val);
+ }
+ void SetBooleanField(jobject obj, jfieldID fieldID,
+ jboolean val) {
+ functions->SetBooleanField(this,obj,fieldID,val);
+ }
+ void SetByteField(jobject obj, jfieldID fieldID,
+ jbyte val) {
+ functions->SetByteField(this,obj,fieldID,val);
+ }
+ void SetCharField(jobject obj, jfieldID fieldID,
+ jchar val) {
+ functions->SetCharField(this,obj,fieldID,val);
+ }
+ void SetShortField(jobject obj, jfieldID fieldID,
+ jshort val) {
+ functions->SetShortField(this,obj,fieldID,val);
+ }
+ void SetIntField(jobject obj, jfieldID fieldID,
+ jint val) {
+ functions->SetIntField(this,obj,fieldID,val);
+ }
+ void SetLongField(jobject obj, jfieldID fieldID,
+ jlong val) {
+ functions->SetLongField(this,obj,fieldID,val);
+ }
+ void SetFloatField(jobject obj, jfieldID fieldID,
+ jfloat val) {
+ functions->SetFloatField(this,obj,fieldID,val);
+ }
+ void SetDoubleField(jobject obj, jfieldID fieldID,
+ jdouble val) {
+ functions->SetDoubleField(this,obj,fieldID,val);
+ }
+
+ jmethodID GetStaticMethodID(jclass clazz, const char *name,
+ const char *sig) {
+ return functions->GetStaticMethodID(this,clazz,name,sig);
+ }
+
+ jobject CallStaticObjectMethod(jclass clazz, jmethodID methodID,
+ ...) {
+ va_list args;
+ jobject result;
+ va_start(args,methodID);
+ result = functions->CallStaticObjectMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jobject CallStaticObjectMethodV(jclass clazz, jmethodID methodID,
+ va_list args) {
+ return functions->CallStaticObjectMethodV(this,clazz,methodID,args);
+ }
+ jobject CallStaticObjectMethodA(jclass clazz, jmethodID methodID,
+ const jvalue *args) {
+ return functions->CallStaticObjectMethodA(this,clazz,methodID,args);
+ }
+
+ jboolean CallStaticBooleanMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jboolean result;
+ va_start(args,methodID);
+ result = functions->CallStaticBooleanMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jboolean CallStaticBooleanMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticBooleanMethodV(this,clazz,methodID,args);
+ }
+ jboolean CallStaticBooleanMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticBooleanMethodA(this,clazz,methodID,args);
+ }
+
+ jbyte CallStaticByteMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jbyte result;
+ va_start(args,methodID);
+ result = functions->CallStaticByteMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jbyte CallStaticByteMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticByteMethodV(this,clazz,methodID,args);
+ }
+ jbyte CallStaticByteMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticByteMethodA(this,clazz,methodID,args);
+ }
+
+ jchar CallStaticCharMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jchar result;
+ va_start(args,methodID);
+ result = functions->CallStaticCharMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jchar CallStaticCharMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticCharMethodV(this,clazz,methodID,args);
+ }
+ jchar CallStaticCharMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticCharMethodA(this,clazz,methodID,args);
+ }
+
+ jshort CallStaticShortMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jshort result;
+ va_start(args,methodID);
+ result = functions->CallStaticShortMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jshort CallStaticShortMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticShortMethodV(this,clazz,methodID,args);
+ }
+ jshort CallStaticShortMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticShortMethodA(this,clazz,methodID,args);
+ }
+
+ jint CallStaticIntMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jint result;
+ va_start(args,methodID);
+ result = functions->CallStaticIntMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jint CallStaticIntMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticIntMethodV(this,clazz,methodID,args);
+ }
+ jint CallStaticIntMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticIntMethodA(this,clazz,methodID,args);
+ }
+
+ jlong CallStaticLongMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jlong result;
+ va_start(args,methodID);
+ result = functions->CallStaticLongMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jlong CallStaticLongMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticLongMethodV(this,clazz,methodID,args);
+ }
+ jlong CallStaticLongMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticLongMethodA(this,clazz,methodID,args);
+ }
+
+ jfloat CallStaticFloatMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jfloat result;
+ va_start(args,methodID);
+ result = functions->CallStaticFloatMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jfloat CallStaticFloatMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticFloatMethodV(this,clazz,methodID,args);
+ }
+ jfloat CallStaticFloatMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticFloatMethodA(this,clazz,methodID,args);
+ }
+
+ jdouble CallStaticDoubleMethod(jclass clazz,
+ jmethodID methodID, ...) {
+ va_list args;
+ jdouble result;
+ va_start(args,methodID);
+ result = functions->CallStaticDoubleMethodV(this,clazz,methodID,args);
+ va_end(args);
+ return result;
+ }
+ jdouble CallStaticDoubleMethodV(jclass clazz,
+ jmethodID methodID, va_list args) {
+ return functions->CallStaticDoubleMethodV(this,clazz,methodID,args);
+ }
+ jdouble CallStaticDoubleMethodA(jclass clazz,
+ jmethodID methodID, const jvalue *args) {
+ return functions->CallStaticDoubleMethodA(this,clazz,methodID,args);
+ }
+
+ void CallStaticVoidMethod(jclass cls, jmethodID methodID, ...) {
+ va_list args;
+ va_start(args,methodID);
+ functions->CallStaticVoidMethodV(this,cls,methodID,args);
+ va_end(args);
+ }
+ void CallStaticVoidMethodV(jclass cls, jmethodID methodID,
+ va_list args) {
+ functions->CallStaticVoidMethodV(this,cls,methodID,args);
+ }
+ void CallStaticVoidMethodA(jclass cls, jmethodID methodID,
+ const jvalue * args) {
+ functions->CallStaticVoidMethodA(this,cls,methodID,args);
+ }
+
+ jfieldID GetStaticFieldID(jclass clazz, const char *name,
+ const char *sig) {
+ return functions->GetStaticFieldID(this,clazz,name,sig);
+ }
+ jobject GetStaticObjectField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticObjectField(this,clazz,fieldID);
+ }
+ jboolean GetStaticBooleanField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticBooleanField(this,clazz,fieldID);
+ }
+ jbyte GetStaticByteField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticByteField(this,clazz,fieldID);
+ }
+ jchar GetStaticCharField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticCharField(this,clazz,fieldID);
+ }
+ jshort GetStaticShortField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticShortField(this,clazz,fieldID);
+ }
+ jint GetStaticIntField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticIntField(this,clazz,fieldID);
+ }
+ jlong GetStaticLongField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticLongField(this,clazz,fieldID);
+ }
+ jfloat GetStaticFloatField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticFloatField(this,clazz,fieldID);
+ }
+ jdouble GetStaticDoubleField(jclass clazz, jfieldID fieldID) {
+ return functions->GetStaticDoubleField(this,clazz,fieldID);
+ }
+
+ void SetStaticObjectField(jclass clazz, jfieldID fieldID,
+ jobject value) {
+ functions->SetStaticObjectField(this,clazz,fieldID,value);
+ }
+ void SetStaticBooleanField(jclass clazz, jfieldID fieldID,
+ jboolean value) {
+ functions->SetStaticBooleanField(this,clazz,fieldID,value);
+ }
+ void SetStaticByteField(jclass clazz, jfieldID fieldID,
+ jbyte value) {
+ functions->SetStaticByteField(this,clazz,fieldID,value);
+ }
+ void SetStaticCharField(jclass clazz, jfieldID fieldID,
+ jchar value) {
+ functions->SetStaticCharField(this,clazz,fieldID,value);
+ }
+ void SetStaticShortField(jclass clazz, jfieldID fieldID,
+ jshort value) {
+ functions->SetStaticShortField(this,clazz,fieldID,value);
+ }
+ void SetStaticIntField(jclass clazz, jfieldID fieldID,
+ jint value) {
+ functions->SetStaticIntField(this,clazz,fieldID,value);
+ }
+ void SetStaticLongField(jclass clazz, jfieldID fieldID,
+ jlong value) {
+ functions->SetStaticLongField(this,clazz,fieldID,value);
+ }
+ void SetStaticFloatField(jclass clazz, jfieldID fieldID,
+ jfloat value) {
+ functions->SetStaticFloatField(this,clazz,fieldID,value);
+ }
+ void SetStaticDoubleField(jclass clazz, jfieldID fieldID,
+ jdouble value) {
+ functions->SetStaticDoubleField(this,clazz,fieldID,value);
+ }
+
+ jstring NewString(const jchar *unicode, jsize len) {
+ return functions->NewString(this,unicode,len);
+ }
+ jsize GetStringLength(jstring str) {
+ return functions->GetStringLength(this,str);
+ }
+ const jchar *GetStringChars(jstring str, jboolean *isCopy) {
+ return functions->GetStringChars(this,str,isCopy);
+ }
+ void ReleaseStringChars(jstring str, const jchar *chars) {
+ functions->ReleaseStringChars(this,str,chars);
+ }
+
+ jstring NewStringUTF(const char *utf) {
+ return functions->NewStringUTF(this,utf);
+ }
+ jsize GetStringUTFLength(jstring str) {
+ return functions->GetStringUTFLength(this,str);
+ }
+ const char* GetStringUTFChars(jstring str, jboolean *isCopy) {
+ return functions->GetStringUTFChars(this,str,isCopy);
+ }
+ void ReleaseStringUTFChars(jstring str, const char* chars) {
+ functions->ReleaseStringUTFChars(this,str,chars);
+ }
+
+ jsize GetArrayLength(jarray array) {
+ return functions->GetArrayLength(this,array);
+ }
+
+ jobjectArray NewObjectArray(jsize len, jclass clazz,
+ jobject init) {
+ return functions->NewObjectArray(this,len,clazz,init);
+ }
+ jobject GetObjectArrayElement(jobjectArray array, jsize index) {
+ return functions->GetObjectArrayElement(this,array,index);
+ }
+ void SetObjectArrayElement(jobjectArray array, jsize index,
+ jobject val) {
+ functions->SetObjectArrayElement(this,array,index,val);
+ }
+
+ jbooleanArray NewBooleanArray(jsize len) {
+ return functions->NewBooleanArray(this,len);
+ }
+ jbyteArray NewByteArray(jsize len) {
+ return functions->NewByteArray(this,len);
+ }
+ jcharArray NewCharArray(jsize len) {
+ return functions->NewCharArray(this,len);
+ }
+ jshortArray NewShortArray(jsize len) {
+ return functions->NewShortArray(this,len);
+ }
+ jintArray NewIntArray(jsize len) {
+ return functions->NewIntArray(this,len);
+ }
+ jlongArray NewLongArray(jsize len) {
+ return functions->NewLongArray(this,len);
+ }
+ jfloatArray NewFloatArray(jsize len) {
+ return functions->NewFloatArray(this,len);
+ }
+ jdoubleArray NewDoubleArray(jsize len) {
+ return functions->NewDoubleArray(this,len);
+ }
+
+ jboolean * GetBooleanArrayElements(jbooleanArray array, jboolean *isCopy) {
+ return functions->GetBooleanArrayElements(this,array,isCopy);
+ }
+ jbyte * GetByteArrayElements(jbyteArray array, jboolean *isCopy) {
+ return functions->GetByteArrayElements(this,array,isCopy);
+ }
+ jchar * GetCharArrayElements(jcharArray array, jboolean *isCopy) {
+ return functions->GetCharArrayElements(this,array,isCopy);
+ }
+ jshort * GetShortArrayElements(jshortArray array, jboolean *isCopy) {
+ return functions->GetShortArrayElements(this,array,isCopy);
+ }
+ jint * GetIntArrayElements(jintArray array, jboolean *isCopy) {
+ return functions->GetIntArrayElements(this,array,isCopy);
+ }
+ jlong * GetLongArrayElements(jlongArray array, jboolean *isCopy) {
+ return functions->GetLongArrayElements(this,array,isCopy);
+ }
+ jfloat * GetFloatArrayElements(jfloatArray array, jboolean *isCopy) {
+ return functions->GetFloatArrayElements(this,array,isCopy);
+ }
+ jdouble * GetDoubleArrayElements(jdoubleArray array, jboolean *isCopy) {
+ return functions->GetDoubleArrayElements(this,array,isCopy);
+ }
+
+ void ReleaseBooleanArrayElements(jbooleanArray array,
+ jboolean *elems,
+ jint mode) {
+ functions->ReleaseBooleanArrayElements(this,array,elems,mode);
+ }
+ void ReleaseByteArrayElements(jbyteArray array,
+ jbyte *elems,
+ jint mode) {
+ functions->ReleaseByteArrayElements(this,array,elems,mode);
+ }
+ void ReleaseCharArrayElements(jcharArray array,
+ jchar *elems,
+ jint mode) {
+ functions->ReleaseCharArrayElements(this,array,elems,mode);
+ }
+ void ReleaseShortArrayElements(jshortArray array,
+ jshort *elems,
+ jint mode) {
+ functions->ReleaseShortArrayElements(this,array,elems,mode);
+ }
+ void ReleaseIntArrayElements(jintArray array,
+ jint *elems,
+ jint mode) {
+ functions->ReleaseIntArrayElements(this,array,elems,mode);
+ }
+ void ReleaseLongArrayElements(jlongArray array,
+ jlong *elems,
+ jint mode) {
+ functions->ReleaseLongArrayElements(this,array,elems,mode);
+ }
+ void ReleaseFloatArrayElements(jfloatArray array,
+ jfloat *elems,
+ jint mode) {
+ functions->ReleaseFloatArrayElements(this,array,elems,mode);
+ }
+ void ReleaseDoubleArrayElements(jdoubleArray array,
+ jdouble *elems,
+ jint mode) {
+ functions->ReleaseDoubleArrayElements(this,array,elems,mode);
+ }
+
+ void GetBooleanArrayRegion(jbooleanArray array,
+ jsize start, jsize len, jboolean *buf) {
+ functions->GetBooleanArrayRegion(this,array,start,len,buf);
+ }
+ void GetByteArrayRegion(jbyteArray array,
+ jsize start, jsize len, jbyte *buf) {
+ functions->GetByteArrayRegion(this,array,start,len,buf);
+ }
+ void GetCharArrayRegion(jcharArray array,
+ jsize start, jsize len, jchar *buf) {
+ functions->GetCharArrayRegion(this,array,start,len,buf);
+ }
+ void GetShortArrayRegion(jshortArray array,
+ jsize start, jsize len, jshort *buf) {
+ functions->GetShortArrayRegion(this,array,start,len,buf);
+ }
+ void GetIntArrayRegion(jintArray array,
+ jsize start, jsize len, jint *buf) {
+ functions->GetIntArrayRegion(this,array,start,len,buf);
+ }
+ void GetLongArrayRegion(jlongArray array,
+ jsize start, jsize len, jlong *buf) {
+ functions->GetLongArrayRegion(this,array,start,len,buf);
+ }
+ void GetFloatArrayRegion(jfloatArray array,
+ jsize start, jsize len, jfloat *buf) {
+ functions->GetFloatArrayRegion(this,array,start,len,buf);
+ }
+ void GetDoubleArrayRegion(jdoubleArray array,
+ jsize start, jsize len, jdouble *buf) {
+ functions->GetDoubleArrayRegion(this,array,start,len,buf);
+ }
+
+ void SetBooleanArrayRegion(jbooleanArray array, jsize start, jsize len,
+ const jboolean *buf) {
+ functions->SetBooleanArrayRegion(this,array,start,len,buf);
+ }
+ void SetByteArrayRegion(jbyteArray array, jsize start, jsize len,
+ const jbyte *buf) {
+ functions->SetByteArrayRegion(this,array,start,len,buf);
+ }
+ void SetCharArrayRegion(jcharArray array, jsize start, jsize len,
+ const jchar *buf) {
+ functions->SetCharArrayRegion(this,array,start,len,buf);
+ }
+ void SetShortArrayRegion(jshortArray array, jsize start, jsize len,
+ const jshort *buf) {
+ functions->SetShortArrayRegion(this,array,start,len,buf);
+ }
+ void SetIntArrayRegion(jintArray array, jsize start, jsize len,
+ const jint *buf) {
+ functions->SetIntArrayRegion(this,array,start,len,buf);
+ }
+ void SetLongArrayRegion(jlongArray array, jsize start, jsize len,
+ const jlong *buf) {
+ functions->SetLongArrayRegion(this,array,start,len,buf);
+ }
+ void SetFloatArrayRegion(jfloatArray array, jsize start, jsize len,
+ const jfloat *buf) {
+ functions->SetFloatArrayRegion(this,array,start,len,buf);
+ }
+ void SetDoubleArrayRegion(jdoubleArray array, jsize start, jsize len,
+ const jdouble *buf) {
+ functions->SetDoubleArrayRegion(this,array,start,len,buf);
+ }
+
+ jint RegisterNatives(jclass clazz, const JNINativeMethod *methods,
+ jint nMethods) {
+ return functions->RegisterNatives(this,clazz,methods,nMethods);
+ }
+ jint UnregisterNatives(jclass clazz) {
+ return functions->UnregisterNatives(this,clazz);
+ }
+
+ jint MonitorEnter(jobject obj) {
+ return functions->MonitorEnter(this,obj);
+ }
+ jint MonitorExit(jobject obj) {
+ return functions->MonitorExit(this,obj);
+ }
+
+ jint GetJavaVM(JavaVM **vm) {
+ return functions->GetJavaVM(this,vm);
+ }
+
+ void GetStringRegion(jstring str, jsize start, jsize len, jchar *buf) {
+ functions->GetStringRegion(this,str,start,len,buf);
+ }
+ void GetStringUTFRegion(jstring str, jsize start, jsize len, char *buf) {
+ functions->GetStringUTFRegion(this,str,start,len,buf);
+ }
+
+ void * GetPrimitiveArrayCritical(jarray array, jboolean *isCopy) {
+ return functions->GetPrimitiveArrayCritical(this,array,isCopy);
+ }
+ void ReleasePrimitiveArrayCritical(jarray array, void *carray, jint mode) {
+ functions->ReleasePrimitiveArrayCritical(this,array,carray,mode);
+ }
+
+ const jchar * GetStringCritical(jstring string, jboolean *isCopy) {
+ return functions->GetStringCritical(this,string,isCopy);
+ }
+ void ReleaseStringCritical(jstring string, const jchar *cstring) {
+ functions->ReleaseStringCritical(this,string,cstring);
+ }
+
+ jweak NewWeakGlobalRef(jobject obj) {
+ return functions->NewWeakGlobalRef(this,obj);
+ }
+ void DeleteWeakGlobalRef(jweak ref) {
+ functions->DeleteWeakGlobalRef(this,ref);
+ }
+
+ jboolean ExceptionCheck() {
+ return functions->ExceptionCheck(this);
+ }
+
+ jobject NewDirectByteBuffer(void* address, jlong capacity) {
+ return functions->NewDirectByteBuffer(this, address, capacity);
+ }
+ void* GetDirectBufferAddress(jobject buf) {
+ return functions->GetDirectBufferAddress(this, buf);
+ }
+ jlong GetDirectBufferCapacity(jobject buf) {
+ return functions->GetDirectBufferCapacity(this, buf);
+ }
+ jobjectRefType GetObjectRefType(jobject obj) {
+ return functions->GetObjectRefType(this, obj);
+ }
+
+#endif /* __cplusplus */
+};
+
+typedef struct JavaVMOption {
+ char *optionString;
+ void *extraInfo;
+} JavaVMOption;
+
+typedef struct JavaVMInitArgs {
+ jint version;
+
+ jint nOptions;
+ JavaVMOption *options;
+ jboolean ignoreUnrecognized;
+} JavaVMInitArgs;
+
+typedef struct JavaVMAttachArgs {
+ jint version;
+
+ char *name;
+ jobject group;
+} JavaVMAttachArgs;
+
+/* These will be VM-specific. */
+
+#define JDK1_2
+#define JDK1_4
+
+/* End VM-specific. */
+
+struct JNIInvokeInterface_ {
+ void *reserved0;
+ void *reserved1;
+ void *reserved2;
+
+ jint (JNICALL *DestroyJavaVM)(JavaVM *vm);
+
+ jint (JNICALL *AttachCurrentThread)(JavaVM *vm, void **penv, void *args);
+
+ jint (JNICALL *DetachCurrentThread)(JavaVM *vm);
+
+ jint (JNICALL *GetEnv)(JavaVM *vm, void **penv, jint version);
+
+ jint (JNICALL *AttachCurrentThreadAsDaemon)(JavaVM *vm, void **penv, void *args);
+};
+
+struct JavaVM_ {
+ const struct JNIInvokeInterface_ *functions;
+#ifdef __cplusplus
+
+ jint DestroyJavaVM() {
+ return functions->DestroyJavaVM(this);
+ }
+ jint AttachCurrentThread(void **penv, void *args) {
+ return functions->AttachCurrentThread(this, penv, args);
+ }
+ jint DetachCurrentThread() {
+ return functions->DetachCurrentThread(this);
+ }
+
+ jint GetEnv(void **penv, jint version) {
+ return functions->GetEnv(this, penv, version);
+ }
+ jint AttachCurrentThreadAsDaemon(void **penv, void *args) {
+ return functions->AttachCurrentThreadAsDaemon(this, penv, args);
+ }
+#endif
+};
+
+#ifdef _JNI_IMPLEMENTATION_
+#define _JNI_IMPORT_OR_EXPORT_ JNIEXPORT
+#else
+#define _JNI_IMPORT_OR_EXPORT_ JNIIMPORT
+#endif
+_JNI_IMPORT_OR_EXPORT_ jint JNICALL
+JNI_GetDefaultJavaVMInitArgs(void *args);
+
+_JNI_IMPORT_OR_EXPORT_ jint JNICALL
+JNI_CreateJavaVM(JavaVM **pvm, void **penv, void *args);
+
+_JNI_IMPORT_OR_EXPORT_ jint JNICALL
+JNI_GetCreatedJavaVMs(JavaVM **, jsize, jsize *);
+
+/* Defined by native libraries. */
+JNIEXPORT jint JNICALL
+JNI_OnLoad(JavaVM *vm, void *reserved);
+
+JNIEXPORT void JNICALL
+JNI_OnUnload(JavaVM *vm, void *reserved);
+
+#define JNI_VERSION_1_1 0x00010001
+#define JNI_VERSION_1_2 0x00010002
+#define JNI_VERSION_1_4 0x00010004
+#define JNI_VERSION_1_6 0x00010006
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* !_JAVASOFT_JNI_H_ */
+
+
+
diff --git a/cpp/wiiusej/include/jvmti.h b/cpp/wiiusej/include/jvmti.h
new file mode 100644
index 0000000..865f21e
--- /dev/null
+++ b/cpp/wiiusej/include/jvmti.h
@@ -0,0 +1,2504 @@
+#ifdef USE_PRAGMA_IDENT_HDR
+#pragma ident "@(#)jvmtiLib.xsl 1.38 06/08/02 23:22:31 JVM"
+#endif
+/*
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+ /* AUTOMATICALLY GENERATED FILE - DO NOT EDIT */
+
+
+ /* Include file for the Java(tm) Virtual Machine Tool Interface */
+
+#ifndef _JAVA_JVMTI_H_
+#define _JAVA_JVMTI_H_
+
+#include "jni.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ JVMTI_VERSION_1 = 0x30010000,
+ JVMTI_VERSION_1_0 = 0x30010000,
+ JVMTI_VERSION_1_1 = 0x30010100,
+
+ JVMTI_VERSION = 0x30000000 + (1 * 0x10000) + (1 * 0x100) + 102 /* version: 1.1.102 */
+};
+
+JNIEXPORT jint JNICALL
+Agent_OnLoad(JavaVM *vm, char *options, void *reserved);
+
+JNIEXPORT jint JNICALL
+Agent_OnAttach(JavaVM* vm, char* options, void* reserved);
+
+JNIEXPORT void JNICALL
+Agent_OnUnload(JavaVM *vm);
+
+ /* Forward declaration of the environment */
+
+struct _jvmtiEnv;
+
+struct jvmtiInterface_1_;
+
+#ifdef __cplusplus
+typedef _jvmtiEnv jvmtiEnv;
+#else
+typedef const struct jvmtiInterface_1_ *jvmtiEnv;
+#endif /* __cplusplus */
+
+/* Derived Base Types */
+
+typedef jobject jthread;
+typedef jobject jthreadGroup;
+typedef jlong jlocation;
+struct _jrawMonitorID;
+typedef struct _jrawMonitorID *jrawMonitorID;
+typedef struct JNINativeInterface_ jniNativeInterface;
+
+ /* Constants */
+
+
+ /* Thread State Flags */
+
+enum {
+ JVMTI_THREAD_STATE_ALIVE = 0x0001,
+ JVMTI_THREAD_STATE_TERMINATED = 0x0002,
+ JVMTI_THREAD_STATE_RUNNABLE = 0x0004,
+ JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER = 0x0400,
+ JVMTI_THREAD_STATE_WAITING = 0x0080,
+ JVMTI_THREAD_STATE_WAITING_INDEFINITELY = 0x0010,
+ JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT = 0x0020,
+ JVMTI_THREAD_STATE_SLEEPING = 0x0040,
+ JVMTI_THREAD_STATE_IN_OBJECT_WAIT = 0x0100,
+ JVMTI_THREAD_STATE_PARKED = 0x0200,
+ JVMTI_THREAD_STATE_SUSPENDED = 0x100000,
+ JVMTI_THREAD_STATE_INTERRUPTED = 0x200000,
+ JVMTI_THREAD_STATE_IN_NATIVE = 0x400000,
+ JVMTI_THREAD_STATE_VENDOR_1 = 0x10000000,
+ JVMTI_THREAD_STATE_VENDOR_2 = 0x20000000,
+ JVMTI_THREAD_STATE_VENDOR_3 = 0x40000000
+};
+
+ /* java.lang.Thread.State Conversion Masks */
+
+enum {
+ JVMTI_JAVA_LANG_THREAD_STATE_MASK = JVMTI_THREAD_STATE_TERMINATED | JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT,
+ JVMTI_JAVA_LANG_THREAD_STATE_NEW = 0,
+ JVMTI_JAVA_LANG_THREAD_STATE_TERMINATED = JVMTI_THREAD_STATE_TERMINATED,
+ JVMTI_JAVA_LANG_THREAD_STATE_RUNNABLE = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_RUNNABLE,
+ JVMTI_JAVA_LANG_THREAD_STATE_BLOCKED = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_BLOCKED_ON_MONITOR_ENTER,
+ JVMTI_JAVA_LANG_THREAD_STATE_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_INDEFINITELY,
+ JVMTI_JAVA_LANG_THREAD_STATE_TIMED_WAITING = JVMTI_THREAD_STATE_ALIVE | JVMTI_THREAD_STATE_WAITING | JVMTI_THREAD_STATE_WAITING_WITH_TIMEOUT
+};
+
+ /* Thread Priority Constants */
+
+enum {
+ JVMTI_THREAD_MIN_PRIORITY = 1,
+ JVMTI_THREAD_NORM_PRIORITY = 5,
+ JVMTI_THREAD_MAX_PRIORITY = 10
+};
+
+ /* Heap Filter Flags */
+
+enum {
+ JVMTI_HEAP_FILTER_TAGGED = 0x4,
+ JVMTI_HEAP_FILTER_UNTAGGED = 0x8,
+ JVMTI_HEAP_FILTER_CLASS_TAGGED = 0x10,
+ JVMTI_HEAP_FILTER_CLASS_UNTAGGED = 0x20
+};
+
+ /* Heap Visit Control Flags */
+
+enum {
+ JVMTI_VISIT_OBJECTS = 0x100,
+ JVMTI_VISIT_ABORT = 0x8000
+};
+
+ /* Heap Reference Enumeration */
+
+typedef enum {
+ JVMTI_HEAP_REFERENCE_CLASS = 1,
+ JVMTI_HEAP_REFERENCE_FIELD = 2,
+ JVMTI_HEAP_REFERENCE_ARRAY_ELEMENT = 3,
+ JVMTI_HEAP_REFERENCE_CLASS_LOADER = 4,
+ JVMTI_HEAP_REFERENCE_SIGNERS = 5,
+ JVMTI_HEAP_REFERENCE_PROTECTION_DOMAIN = 6,
+ JVMTI_HEAP_REFERENCE_INTERFACE = 7,
+ JVMTI_HEAP_REFERENCE_STATIC_FIELD = 8,
+ JVMTI_HEAP_REFERENCE_CONSTANT_POOL = 9,
+ JVMTI_HEAP_REFERENCE_SUPERCLASS = 10,
+ JVMTI_HEAP_REFERENCE_JNI_GLOBAL = 21,
+ JVMTI_HEAP_REFERENCE_SYSTEM_CLASS = 22,
+ JVMTI_HEAP_REFERENCE_MONITOR = 23,
+ JVMTI_HEAP_REFERENCE_STACK_LOCAL = 24,
+ JVMTI_HEAP_REFERENCE_JNI_LOCAL = 25,
+ JVMTI_HEAP_REFERENCE_THREAD = 26,
+ JVMTI_HEAP_REFERENCE_OTHER = 27
+} jvmtiHeapReferenceKind;
+
+ /* Primitive Type Enumeration */
+
+typedef enum {
+ JVMTI_PRIMITIVE_TYPE_BOOLEAN = 90,
+ JVMTI_PRIMITIVE_TYPE_BYTE = 66,
+ JVMTI_PRIMITIVE_TYPE_CHAR = 67,
+ JVMTI_PRIMITIVE_TYPE_SHORT = 83,
+ JVMTI_PRIMITIVE_TYPE_INT = 73,
+ JVMTI_PRIMITIVE_TYPE_LONG = 74,
+ JVMTI_PRIMITIVE_TYPE_FLOAT = 70,
+ JVMTI_PRIMITIVE_TYPE_DOUBLE = 68
+} jvmtiPrimitiveType;
+
+ /* Heap Object Filter Enumeration */
+
+typedef enum {
+ JVMTI_HEAP_OBJECT_TAGGED = 1,
+ JVMTI_HEAP_OBJECT_UNTAGGED = 2,
+ JVMTI_HEAP_OBJECT_EITHER = 3
+} jvmtiHeapObjectFilter;
+
+ /* Heap Root Kind Enumeration */
+
+typedef enum {
+ JVMTI_HEAP_ROOT_JNI_GLOBAL = 1,
+ JVMTI_HEAP_ROOT_SYSTEM_CLASS = 2,
+ JVMTI_HEAP_ROOT_MONITOR = 3,
+ JVMTI_HEAP_ROOT_STACK_LOCAL = 4,
+ JVMTI_HEAP_ROOT_JNI_LOCAL = 5,
+ JVMTI_HEAP_ROOT_THREAD = 6,
+ JVMTI_HEAP_ROOT_OTHER = 7
+} jvmtiHeapRootKind;
+
+ /* Object Reference Enumeration */
+
+typedef enum {
+ JVMTI_REFERENCE_CLASS = 1,
+ JVMTI_REFERENCE_FIELD = 2,
+ JVMTI_REFERENCE_ARRAY_ELEMENT = 3,
+ JVMTI_REFERENCE_CLASS_LOADER = 4,
+ JVMTI_REFERENCE_SIGNERS = 5,
+ JVMTI_REFERENCE_PROTECTION_DOMAIN = 6,
+ JVMTI_REFERENCE_INTERFACE = 7,
+ JVMTI_REFERENCE_STATIC_FIELD = 8,
+ JVMTI_REFERENCE_CONSTANT_POOL = 9
+} jvmtiObjectReferenceKind;
+
+ /* Iteration Control Enumeration */
+
+typedef enum {
+ JVMTI_ITERATION_CONTINUE = 1,
+ JVMTI_ITERATION_IGNORE = 2,
+ JVMTI_ITERATION_ABORT = 0
+} jvmtiIterationControl;
+
+ /* Class Status Flags */
+
+enum {
+ JVMTI_CLASS_STATUS_VERIFIED = 1,
+ JVMTI_CLASS_STATUS_PREPARED = 2,
+ JVMTI_CLASS_STATUS_INITIALIZED = 4,
+ JVMTI_CLASS_STATUS_ERROR = 8,
+ JVMTI_CLASS_STATUS_ARRAY = 16,
+ JVMTI_CLASS_STATUS_PRIMITIVE = 32
+};
+
+ /* Event Enable/Disable */
+
+typedef enum {
+ JVMTI_ENABLE = 1,
+ JVMTI_DISABLE = 0
+} jvmtiEventMode;
+
+ /* Extension Function/Event Parameter Types */
+
+typedef enum {
+ JVMTI_TYPE_JBYTE = 101,
+ JVMTI_TYPE_JCHAR = 102,
+ JVMTI_TYPE_JSHORT = 103,
+ JVMTI_TYPE_JINT = 104,
+ JVMTI_TYPE_JLONG = 105,
+ JVMTI_TYPE_JFLOAT = 106,
+ JVMTI_TYPE_JDOUBLE = 107,
+ JVMTI_TYPE_JBOOLEAN = 108,
+ JVMTI_TYPE_JOBJECT = 109,
+ JVMTI_TYPE_JTHREAD = 110,
+ JVMTI_TYPE_JCLASS = 111,
+ JVMTI_TYPE_JVALUE = 112,
+ JVMTI_TYPE_JFIELDID = 113,
+ JVMTI_TYPE_JMETHODID = 114,
+ JVMTI_TYPE_CCHAR = 115,
+ JVMTI_TYPE_CVOID = 116,
+ JVMTI_TYPE_JNIENV = 117
+} jvmtiParamTypes;
+
+ /* Extension Function/Event Parameter Kinds */
+
+typedef enum {
+ JVMTI_KIND_IN = 91,
+ JVMTI_KIND_IN_PTR = 92,
+ JVMTI_KIND_IN_BUF = 93,
+ JVMTI_KIND_ALLOC_BUF = 94,
+ JVMTI_KIND_ALLOC_ALLOC_BUF = 95,
+ JVMTI_KIND_OUT = 96,
+ JVMTI_KIND_OUT_BUF = 97
+} jvmtiParamKind;
+
+ /* Timer Kinds */
+
+typedef enum {
+ JVMTI_TIMER_USER_CPU = 30,
+ JVMTI_TIMER_TOTAL_CPU = 31,
+ JVMTI_TIMER_ELAPSED = 32
+} jvmtiTimerKind;
+
+ /* Phases of execution */
+
+typedef enum {
+ JVMTI_PHASE_ONLOAD = 1,
+ JVMTI_PHASE_PRIMORDIAL = 2,
+ JVMTI_PHASE_START = 6,
+ JVMTI_PHASE_LIVE = 4,
+ JVMTI_PHASE_DEAD = 8
+} jvmtiPhase;
+
+ /* Version Interface Types */
+
+enum {
+ JVMTI_VERSION_INTERFACE_JNI = 0x00000000,
+ JVMTI_VERSION_INTERFACE_JVMTI = 0x30000000
+};
+
+ /* Version Masks */
+
+enum {
+ JVMTI_VERSION_MASK_INTERFACE_TYPE = 0x70000000,
+ JVMTI_VERSION_MASK_MAJOR = 0x0FFF0000,
+ JVMTI_VERSION_MASK_MINOR = 0x0000FF00,
+ JVMTI_VERSION_MASK_MICRO = 0x000000FF
+};
+
+ /* Version Shifts */
+
+enum {
+ JVMTI_VERSION_SHIFT_MAJOR = 16,
+ JVMTI_VERSION_SHIFT_MINOR = 8,
+ JVMTI_VERSION_SHIFT_MICRO = 0
+};
+
+ /* Verbose Flag Enumeration */
+
+typedef enum {
+ JVMTI_VERBOSE_OTHER = 0,
+ JVMTI_VERBOSE_GC = 1,
+ JVMTI_VERBOSE_CLASS = 2,
+ JVMTI_VERBOSE_JNI = 4
+} jvmtiVerboseFlag;
+
+ /* JLocation Format Enumeration */
+
+typedef enum {
+ JVMTI_JLOCATION_JVMBCI = 1,
+ JVMTI_JLOCATION_MACHINEPC = 2,
+ JVMTI_JLOCATION_OTHER = 0
+} jvmtiJlocationFormat;
+
+ /* Resource Exhaustion Flags */
+
+enum {
+ JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR = 0x0001,
+ JVMTI_RESOURCE_EXHAUSTED_JAVA_HEAP = 0x0002,
+ JVMTI_RESOURCE_EXHAUSTED_THREADS = 0x0004
+};
+
+ /* Errors */
+
+typedef enum {
+ JVMTI_ERROR_NONE = 0,
+ JVMTI_ERROR_INVALID_THREAD = 10,
+ JVMTI_ERROR_INVALID_THREAD_GROUP = 11,
+ JVMTI_ERROR_INVALID_PRIORITY = 12,
+ JVMTI_ERROR_THREAD_NOT_SUSPENDED = 13,
+ JVMTI_ERROR_THREAD_SUSPENDED = 14,
+ JVMTI_ERROR_THREAD_NOT_ALIVE = 15,
+ JVMTI_ERROR_INVALID_OBJECT = 20,
+ JVMTI_ERROR_INVALID_CLASS = 21,
+ JVMTI_ERROR_CLASS_NOT_PREPARED = 22,
+ JVMTI_ERROR_INVALID_METHODID = 23,
+ JVMTI_ERROR_INVALID_LOCATION = 24,
+ JVMTI_ERROR_INVALID_FIELDID = 25,
+ JVMTI_ERROR_NO_MORE_FRAMES = 31,
+ JVMTI_ERROR_OPAQUE_FRAME = 32,
+ JVMTI_ERROR_TYPE_MISMATCH = 34,
+ JVMTI_ERROR_INVALID_SLOT = 35,
+ JVMTI_ERROR_DUPLICATE = 40,
+ JVMTI_ERROR_NOT_FOUND = 41,
+ JVMTI_ERROR_INVALID_MONITOR = 50,
+ JVMTI_ERROR_NOT_MONITOR_OWNER = 51,
+ JVMTI_ERROR_INTERRUPT = 52,
+ JVMTI_ERROR_INVALID_CLASS_FORMAT = 60,
+ JVMTI_ERROR_CIRCULAR_CLASS_DEFINITION = 61,
+ JVMTI_ERROR_FAILS_VERIFICATION = 62,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_ADDED = 63,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_SCHEMA_CHANGED = 64,
+ JVMTI_ERROR_INVALID_TYPESTATE = 65,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_HIERARCHY_CHANGED = 66,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_DELETED = 67,
+ JVMTI_ERROR_UNSUPPORTED_VERSION = 68,
+ JVMTI_ERROR_NAMES_DONT_MATCH = 69,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_CLASS_MODIFIERS_CHANGED = 70,
+ JVMTI_ERROR_UNSUPPORTED_REDEFINITION_METHOD_MODIFIERS_CHANGED = 71,
+ JVMTI_ERROR_UNMODIFIABLE_CLASS = 79,
+ JVMTI_ERROR_NOT_AVAILABLE = 98,
+ JVMTI_ERROR_MUST_POSSESS_CAPABILITY = 99,
+ JVMTI_ERROR_NULL_POINTER = 100,
+ JVMTI_ERROR_ABSENT_INFORMATION = 101,
+ JVMTI_ERROR_INVALID_EVENT_TYPE = 102,
+ JVMTI_ERROR_ILLEGAL_ARGUMENT = 103,
+ JVMTI_ERROR_NATIVE_METHOD = 104,
+ JVMTI_ERROR_CLASS_LOADER_UNSUPPORTED = 106,
+ JVMTI_ERROR_OUT_OF_MEMORY = 110,
+ JVMTI_ERROR_ACCESS_DENIED = 111,
+ JVMTI_ERROR_WRONG_PHASE = 112,
+ JVMTI_ERROR_INTERNAL = 113,
+ JVMTI_ERROR_UNATTACHED_THREAD = 115,
+ JVMTI_ERROR_INVALID_ENVIRONMENT = 116,
+ JVMTI_ERROR_MAX = 116
+} jvmtiError;
+
+ /* Event IDs */
+
+typedef enum {
+ JVMTI_MIN_EVENT_TYPE_VAL = 50,
+ JVMTI_EVENT_VM_INIT = 50,
+ JVMTI_EVENT_VM_DEATH = 51,
+ JVMTI_EVENT_THREAD_START = 52,
+ JVMTI_EVENT_THREAD_END = 53,
+ JVMTI_EVENT_CLASS_FILE_LOAD_HOOK = 54,
+ JVMTI_EVENT_CLASS_LOAD = 55,
+ JVMTI_EVENT_CLASS_PREPARE = 56,
+ JVMTI_EVENT_VM_START = 57,
+ JVMTI_EVENT_EXCEPTION = 58,
+ JVMTI_EVENT_EXCEPTION_CATCH = 59,
+ JVMTI_EVENT_SINGLE_STEP = 60,
+ JVMTI_EVENT_FRAME_POP = 61,
+ JVMTI_EVENT_BREAKPOINT = 62,
+ JVMTI_EVENT_FIELD_ACCESS = 63,
+ JVMTI_EVENT_FIELD_MODIFICATION = 64,
+ JVMTI_EVENT_METHOD_ENTRY = 65,
+ JVMTI_EVENT_METHOD_EXIT = 66,
+ JVMTI_EVENT_NATIVE_METHOD_BIND = 67,
+ JVMTI_EVENT_COMPILED_METHOD_LOAD = 68,
+ JVMTI_EVENT_COMPILED_METHOD_UNLOAD = 69,
+ JVMTI_EVENT_DYNAMIC_CODE_GENERATED = 70,
+ JVMTI_EVENT_DATA_DUMP_REQUEST = 71,
+ JVMTI_EVENT_MONITOR_WAIT = 73,
+ JVMTI_EVENT_MONITOR_WAITED = 74,
+ JVMTI_EVENT_MONITOR_CONTENDED_ENTER = 75,
+ JVMTI_EVENT_MONITOR_CONTENDED_ENTERED = 76,
+ JVMTI_EVENT_RESOURCE_EXHAUSTED = 80,
+ JVMTI_EVENT_GARBAGE_COLLECTION_START = 81,
+ JVMTI_EVENT_GARBAGE_COLLECTION_FINISH = 82,
+ JVMTI_EVENT_OBJECT_FREE = 83,
+ JVMTI_EVENT_VM_OBJECT_ALLOC = 84,
+ JVMTI_MAX_EVENT_TYPE_VAL = 84
+} jvmtiEvent;
+
+
+ /* Pre-Declarations */
+struct _jvmtiThreadInfo;
+typedef struct _jvmtiThreadInfo jvmtiThreadInfo;
+struct _jvmtiMonitorStackDepthInfo;
+typedef struct _jvmtiMonitorStackDepthInfo jvmtiMonitorStackDepthInfo;
+struct _jvmtiThreadGroupInfo;
+typedef struct _jvmtiThreadGroupInfo jvmtiThreadGroupInfo;
+struct _jvmtiFrameInfo;
+typedef struct _jvmtiFrameInfo jvmtiFrameInfo;
+struct _jvmtiStackInfo;
+typedef struct _jvmtiStackInfo jvmtiStackInfo;
+struct _jvmtiHeapReferenceInfoField;
+typedef struct _jvmtiHeapReferenceInfoField jvmtiHeapReferenceInfoField;
+struct _jvmtiHeapReferenceInfoArray;
+typedef struct _jvmtiHeapReferenceInfoArray jvmtiHeapReferenceInfoArray;
+struct _jvmtiHeapReferenceInfoConstantPool;
+typedef struct _jvmtiHeapReferenceInfoConstantPool jvmtiHeapReferenceInfoConstantPool;
+struct _jvmtiHeapReferenceInfoStackLocal;
+typedef struct _jvmtiHeapReferenceInfoStackLocal jvmtiHeapReferenceInfoStackLocal;
+struct _jvmtiHeapReferenceInfoJniLocal;
+typedef struct _jvmtiHeapReferenceInfoJniLocal jvmtiHeapReferenceInfoJniLocal;
+struct _jvmtiHeapReferenceInfoReserved;
+typedef struct _jvmtiHeapReferenceInfoReserved jvmtiHeapReferenceInfoReserved;
+union _jvmtiHeapReferenceInfo;
+typedef union _jvmtiHeapReferenceInfo jvmtiHeapReferenceInfo;
+struct _jvmtiHeapCallbacks;
+typedef struct _jvmtiHeapCallbacks jvmtiHeapCallbacks;
+struct _jvmtiClassDefinition;
+typedef struct _jvmtiClassDefinition jvmtiClassDefinition;
+struct _jvmtiMonitorUsage;
+typedef struct _jvmtiMonitorUsage jvmtiMonitorUsage;
+struct _jvmtiLineNumberEntry;
+typedef struct _jvmtiLineNumberEntry jvmtiLineNumberEntry;
+struct _jvmtiLocalVariableEntry;
+typedef struct _jvmtiLocalVariableEntry jvmtiLocalVariableEntry;
+struct _jvmtiParamInfo;
+typedef struct _jvmtiParamInfo jvmtiParamInfo;
+struct _jvmtiExtensionFunctionInfo;
+typedef struct _jvmtiExtensionFunctionInfo jvmtiExtensionFunctionInfo;
+struct _jvmtiExtensionEventInfo;
+typedef struct _jvmtiExtensionEventInfo jvmtiExtensionEventInfo;
+struct _jvmtiTimerInfo;
+typedef struct _jvmtiTimerInfo jvmtiTimerInfo;
+struct _jvmtiAddrLocationMap;
+typedef struct _jvmtiAddrLocationMap jvmtiAddrLocationMap;
+
+ /* Function Types */
+
+typedef void (JNICALL *jvmtiStartFunction)
+ (jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg);
+
+typedef jint (JNICALL *jvmtiHeapIterationCallback)
+ (jlong class_tag, jlong size, jlong* tag_ptr, jint length, void* user_data);
+
+typedef jint (JNICALL *jvmtiHeapReferenceCallback)
+ (jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info, jlong class_tag, jlong referrer_class_tag, jlong size, jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data);
+
+typedef jint (JNICALL *jvmtiPrimitiveFieldCallback)
+ (jvmtiHeapReferenceKind kind, const jvmtiHeapReferenceInfo* info, jlong object_class_tag, jlong* object_tag_ptr, jvalue value, jvmtiPrimitiveType value_type, void* user_data);
+
+typedef jint (JNICALL *jvmtiArrayPrimitiveValueCallback)
+ (jlong class_tag, jlong size, jlong* tag_ptr, jint element_count, jvmtiPrimitiveType element_type, const void* elements, void* user_data);
+
+typedef jint (JNICALL *jvmtiStringPrimitiveValueCallback)
+ (jlong class_tag, jlong size, jlong* tag_ptr, const jchar* value, jint value_length, void* user_data);
+
+typedef jint (JNICALL *jvmtiReservedCallback)
+ ();
+
+typedef jvmtiIterationControl (JNICALL *jvmtiHeapObjectCallback)
+ (jlong class_tag, jlong size, jlong* tag_ptr, void* user_data);
+
+typedef jvmtiIterationControl (JNICALL *jvmtiHeapRootCallback)
+ (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, void* user_data);
+
+typedef jvmtiIterationControl (JNICALL *jvmtiStackReferenceCallback)
+ (jvmtiHeapRootKind root_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong thread_tag, jint depth, jmethodID method, jint slot, void* user_data);
+
+typedef jvmtiIterationControl (JNICALL *jvmtiObjectReferenceCallback)
+ (jvmtiObjectReferenceKind reference_kind, jlong class_tag, jlong size, jlong* tag_ptr, jlong referrer_tag, jint referrer_index, void* user_data);
+
+typedef jvmtiError (JNICALL *jvmtiExtensionFunction)
+ (jvmtiEnv* jvmti_env, ...);
+
+typedef void (JNICALL *jvmtiExtensionEvent)
+ (jvmtiEnv* jvmti_env, ...);
+
+
+ /* Structure Types */
+struct _jvmtiThreadInfo {
+ char* name;
+ jint priority;
+ jboolean is_daemon;
+ jthreadGroup thread_group;
+ jobject context_class_loader;
+};
+struct _jvmtiMonitorStackDepthInfo {
+ jobject monitor;
+ jint stack_depth;
+};
+struct _jvmtiThreadGroupInfo {
+ jthreadGroup parent;
+ char* name;
+ jint max_priority;
+ jboolean is_daemon;
+};
+struct _jvmtiFrameInfo {
+ jmethodID method;
+ jlocation location;
+};
+struct _jvmtiStackInfo {
+ jthread thread;
+ jint state;
+ jvmtiFrameInfo* frame_buffer;
+ jint frame_count;
+};
+struct _jvmtiHeapReferenceInfoField {
+ jint index;
+};
+struct _jvmtiHeapReferenceInfoArray {
+ jint index;
+};
+struct _jvmtiHeapReferenceInfoConstantPool {
+ jint index;
+};
+struct _jvmtiHeapReferenceInfoStackLocal {
+ jlong thread_tag;
+ jlong thread_id;
+ jint depth;
+ jmethodID method;
+ jlocation location;
+ jint slot;
+};
+struct _jvmtiHeapReferenceInfoJniLocal {
+ jlong thread_tag;
+ jlong thread_id;
+ jint depth;
+ jmethodID method;
+};
+struct _jvmtiHeapReferenceInfoReserved {
+ jlong reserved1;
+ jlong reserved2;
+ jlong reserved3;
+ jlong reserved4;
+ jlong reserved5;
+ jlong reserved6;
+ jlong reserved7;
+ jlong reserved8;
+};
+union _jvmtiHeapReferenceInfo {
+ jvmtiHeapReferenceInfoField field;
+ jvmtiHeapReferenceInfoArray array;
+ jvmtiHeapReferenceInfoConstantPool constant_pool;
+ jvmtiHeapReferenceInfoStackLocal stack_local;
+ jvmtiHeapReferenceInfoJniLocal jni_local;
+ jvmtiHeapReferenceInfoReserved other;
+};
+struct _jvmtiHeapCallbacks {
+ jvmtiHeapIterationCallback heap_iteration_callback;
+ jvmtiHeapReferenceCallback heap_reference_callback;
+ jvmtiPrimitiveFieldCallback primitive_field_callback;
+ jvmtiArrayPrimitiveValueCallback array_primitive_value_callback;
+ jvmtiStringPrimitiveValueCallback string_primitive_value_callback;
+ jvmtiReservedCallback reserved5;
+ jvmtiReservedCallback reserved6;
+ jvmtiReservedCallback reserved7;
+ jvmtiReservedCallback reserved8;
+ jvmtiReservedCallback reserved9;
+ jvmtiReservedCallback reserved10;
+ jvmtiReservedCallback reserved11;
+ jvmtiReservedCallback reserved12;
+ jvmtiReservedCallback reserved13;
+ jvmtiReservedCallback reserved14;
+ jvmtiReservedCallback reserved15;
+};
+struct _jvmtiClassDefinition {
+ jclass klass;
+ jint class_byte_count;
+ const unsigned char* class_bytes;
+};
+struct _jvmtiMonitorUsage {
+ jthread owner;
+ jint entry_count;
+ jint waiter_count;
+ jthread* waiters;
+ jint notify_waiter_count;
+ jthread* notify_waiters;
+};
+struct _jvmtiLineNumberEntry {
+ jlocation start_location;
+ jint line_number;
+};
+struct _jvmtiLocalVariableEntry {
+ jlocation start_location;
+ jint length;
+ char* name;
+ char* signature;
+ char* generic_signature;
+ jint slot;
+};
+struct _jvmtiParamInfo {
+ char* name;
+ jvmtiParamKind kind;
+ jvmtiParamTypes base_type;
+ jboolean null_ok;
+};
+struct _jvmtiExtensionFunctionInfo {
+ jvmtiExtensionFunction func;
+ char* id;
+ char* short_description;
+ jint param_count;
+ jvmtiParamInfo* params;
+ jint error_count;
+ jvmtiError* errors;
+};
+struct _jvmtiExtensionEventInfo {
+ jint extension_event_index;
+ char* id;
+ char* short_description;
+ jint param_count;
+ jvmtiParamInfo* params;
+};
+struct _jvmtiTimerInfo {
+ jlong max_value;
+ jboolean may_skip_forward;
+ jboolean may_skip_backward;
+ jvmtiTimerKind kind;
+ jlong reserved1;
+ jlong reserved2;
+};
+struct _jvmtiAddrLocationMap {
+ const void* start_address;
+ jlocation location;
+};
+
+typedef struct {
+ unsigned int can_tag_objects : 1;
+ unsigned int can_generate_field_modification_events : 1;
+ unsigned int can_generate_field_access_events : 1;
+ unsigned int can_get_bytecodes : 1;
+ unsigned int can_get_synthetic_attribute : 1;
+ unsigned int can_get_owned_monitor_info : 1;
+ unsigned int can_get_current_contended_monitor : 1;
+ unsigned int can_get_monitor_info : 1;
+ unsigned int can_pop_frame : 1;
+ unsigned int can_redefine_classes : 1;
+ unsigned int can_signal_thread : 1;
+ unsigned int can_get_source_file_name : 1;
+ unsigned int can_get_line_numbers : 1;
+ unsigned int can_get_source_debug_extension : 1;
+ unsigned int can_access_local_variables : 1;
+ unsigned int can_maintain_original_method_order : 1;
+ unsigned int can_generate_single_step_events : 1;
+ unsigned int can_generate_exception_events : 1;
+ unsigned int can_generate_frame_pop_events : 1;
+ unsigned int can_generate_breakpoint_events : 1;
+ unsigned int can_suspend : 1;
+ unsigned int can_redefine_any_class : 1;
+ unsigned int can_get_current_thread_cpu_time : 1;
+ unsigned int can_get_thread_cpu_time : 1;
+ unsigned int can_generate_method_entry_events : 1;
+ unsigned int can_generate_method_exit_events : 1;
+ unsigned int can_generate_all_class_hook_events : 1;
+ unsigned int can_generate_compiled_method_load_events : 1;
+ unsigned int can_generate_monitor_events : 1;
+ unsigned int can_generate_vm_object_alloc_events : 1;
+ unsigned int can_generate_native_method_bind_events : 1;
+ unsigned int can_generate_garbage_collection_events : 1;
+ unsigned int can_generate_object_free_events : 1;
+ unsigned int can_force_early_return : 1;
+ unsigned int can_get_owned_monitor_stack_depth_info : 1;
+ unsigned int can_get_constant_pool : 1;
+ unsigned int can_set_native_method_prefix : 1;
+ unsigned int can_retransform_classes : 1;
+ unsigned int can_retransform_any_class : 1;
+ unsigned int can_generate_resource_exhaustion_heap_events : 1;
+ unsigned int can_generate_resource_exhaustion_threads_events : 1;
+ unsigned int : 7;
+ unsigned int : 16;
+ unsigned int : 16;
+ unsigned int : 16;
+ unsigned int : 16;
+ unsigned int : 16;
+} jvmtiCapabilities;
+
+
+ /* Event Definitions */
+
+typedef void (JNICALL *jvmtiEventReserved)(void);
+
+
+typedef void (JNICALL *jvmtiEventBreakpoint)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location);
+
+typedef void (JNICALL *jvmtiEventClassFileLoadHook)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jclass class_being_redefined,
+ jobject loader,
+ const char* name,
+ jobject protection_domain,
+ jint class_data_len,
+ const unsigned char* class_data,
+ jint* new_class_data_len,
+ unsigned char** new_class_data);
+
+typedef void (JNICALL *jvmtiEventClassLoad)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jclass klass);
+
+typedef void (JNICALL *jvmtiEventClassPrepare)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jclass klass);
+
+typedef void (JNICALL *jvmtiEventCompiledMethodLoad)
+ (jvmtiEnv *jvmti_env,
+ jmethodID method,
+ jint code_size,
+ const void* code_addr,
+ jint map_length,
+ const jvmtiAddrLocationMap* map,
+ const void* compile_info);
+
+typedef void (JNICALL *jvmtiEventCompiledMethodUnload)
+ (jvmtiEnv *jvmti_env,
+ jmethodID method,
+ const void* code_addr);
+
+typedef void (JNICALL *jvmtiEventDataDumpRequest)
+ (jvmtiEnv *jvmti_env);
+
+typedef void (JNICALL *jvmtiEventDynamicCodeGenerated)
+ (jvmtiEnv *jvmti_env,
+ const char* name,
+ const void* address,
+ jint length);
+
+typedef void (JNICALL *jvmtiEventException)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location,
+ jobject exception,
+ jmethodID catch_method,
+ jlocation catch_location);
+
+typedef void (JNICALL *jvmtiEventExceptionCatch)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location,
+ jobject exception);
+
+typedef void (JNICALL *jvmtiEventFieldAccess)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location,
+ jclass field_klass,
+ jobject object,
+ jfieldID field);
+
+typedef void (JNICALL *jvmtiEventFieldModification)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location,
+ jclass field_klass,
+ jobject object,
+ jfieldID field,
+ char signature_type,
+ jvalue new_value);
+
+typedef void (JNICALL *jvmtiEventFramePop)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jboolean was_popped_by_exception);
+
+typedef void (JNICALL *jvmtiEventGarbageCollectionFinish)
+ (jvmtiEnv *jvmti_env);
+
+typedef void (JNICALL *jvmtiEventGarbageCollectionStart)
+ (jvmtiEnv *jvmti_env);
+
+typedef void (JNICALL *jvmtiEventMethodEntry)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method);
+
+typedef void (JNICALL *jvmtiEventMethodExit)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jboolean was_popped_by_exception,
+ jvalue return_value);
+
+typedef void (JNICALL *jvmtiEventMonitorContendedEnter)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jobject object);
+
+typedef void (JNICALL *jvmtiEventMonitorContendedEntered)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jobject object);
+
+typedef void (JNICALL *jvmtiEventMonitorWait)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jobject object,
+ jlong timeout);
+
+typedef void (JNICALL *jvmtiEventMonitorWaited)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jobject object,
+ jboolean timed_out);
+
+typedef void (JNICALL *jvmtiEventNativeMethodBind)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ void* address,
+ void** new_address_ptr);
+
+typedef void (JNICALL *jvmtiEventObjectFree)
+ (jvmtiEnv *jvmti_env,
+ jlong tag);
+
+typedef void (JNICALL *jvmtiEventResourceExhausted)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jint flags,
+ const void* reserved,
+ const char* description);
+
+typedef void (JNICALL *jvmtiEventSingleStep)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jmethodID method,
+ jlocation location);
+
+typedef void (JNICALL *jvmtiEventThreadEnd)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread);
+
+typedef void (JNICALL *jvmtiEventThreadStart)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread);
+
+typedef void (JNICALL *jvmtiEventVMDeath)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env);
+
+typedef void (JNICALL *jvmtiEventVMInit)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread);
+
+typedef void (JNICALL *jvmtiEventVMObjectAlloc)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env,
+ jthread thread,
+ jobject object,
+ jclass object_klass,
+ jlong size);
+
+typedef void (JNICALL *jvmtiEventVMStart)
+ (jvmtiEnv *jvmti_env,
+ JNIEnv* jni_env);
+
+ /* Event Callback Structure */
+
+typedef struct {
+ /* 50 : VM Initialization Event */
+ jvmtiEventVMInit VMInit;
+ /* 51 : VM Death Event */
+ jvmtiEventVMDeath VMDeath;
+ /* 52 : Thread Start */
+ jvmtiEventThreadStart ThreadStart;
+ /* 53 : Thread End */
+ jvmtiEventThreadEnd ThreadEnd;
+ /* 54 : Class File Load Hook */
+ jvmtiEventClassFileLoadHook ClassFileLoadHook;
+ /* 55 : Class Load */
+ jvmtiEventClassLoad ClassLoad;
+ /* 56 : Class Prepare */
+ jvmtiEventClassPrepare ClassPrepare;
+ /* 57 : VM Start Event */
+ jvmtiEventVMStart VMStart;
+ /* 58 : Exception */
+ jvmtiEventException Exception;
+ /* 59 : Exception Catch */
+ jvmtiEventExceptionCatch ExceptionCatch;
+ /* 60 : Single Step */
+ jvmtiEventSingleStep SingleStep;
+ /* 61 : Frame Pop */
+ jvmtiEventFramePop FramePop;
+ /* 62 : Breakpoint */
+ jvmtiEventBreakpoint Breakpoint;
+ /* 63 : Field Access */
+ jvmtiEventFieldAccess FieldAccess;
+ /* 64 : Field Modification */
+ jvmtiEventFieldModification FieldModification;
+ /* 65 : Method Entry */
+ jvmtiEventMethodEntry MethodEntry;
+ /* 66 : Method Exit */
+ jvmtiEventMethodExit MethodExit;
+ /* 67 : Native Method Bind */
+ jvmtiEventNativeMethodBind NativeMethodBind;
+ /* 68 : Compiled Method Load */
+ jvmtiEventCompiledMethodLoad CompiledMethodLoad;
+ /* 69 : Compiled Method Unload */
+ jvmtiEventCompiledMethodUnload CompiledMethodUnload;
+ /* 70 : Dynamic Code Generated */
+ jvmtiEventDynamicCodeGenerated DynamicCodeGenerated;
+ /* 71 : Data Dump Request */
+ jvmtiEventDataDumpRequest DataDumpRequest;
+ /* 72 */
+ jvmtiEventReserved reserved72;
+ /* 73 : Monitor Wait */
+ jvmtiEventMonitorWait MonitorWait;
+ /* 74 : Monitor Waited */
+ jvmtiEventMonitorWaited MonitorWaited;
+ /* 75 : Monitor Contended Enter */
+ jvmtiEventMonitorContendedEnter MonitorContendedEnter;
+ /* 76 : Monitor Contended Entered */
+ jvmtiEventMonitorContendedEntered MonitorContendedEntered;
+ /* 77 */
+ jvmtiEventReserved reserved77;
+ /* 78 */
+ jvmtiEventReserved reserved78;
+ /* 79 */
+ jvmtiEventReserved reserved79;
+ /* 80 : Resource Exhausted */
+ jvmtiEventResourceExhausted ResourceExhausted;
+ /* 81 : Garbage Collection Start */
+ jvmtiEventGarbageCollectionStart GarbageCollectionStart;
+ /* 82 : Garbage Collection Finish */
+ jvmtiEventGarbageCollectionFinish GarbageCollectionFinish;
+ /* 83 : Object Free */
+ jvmtiEventObjectFree ObjectFree;
+ /* 84 : VM Object Allocation */
+ jvmtiEventVMObjectAlloc VMObjectAlloc;
+} jvmtiEventCallbacks;
+
+
+ /* Function Interface */
+
+typedef struct jvmtiInterface_1_ {
+
+ /* 1 : RESERVED */
+ void *reserved1;
+
+ /* 2 : Set Event Notification Mode */
+ jvmtiError (JNICALL *SetEventNotificationMode) (jvmtiEnv* env,
+ jvmtiEventMode mode,
+ jvmtiEvent event_type,
+ jthread event_thread,
+ ...);
+
+ /* 3 : RESERVED */
+ void *reserved3;
+
+ /* 4 : Get All Threads */
+ jvmtiError (JNICALL *GetAllThreads) (jvmtiEnv* env,
+ jint* threads_count_ptr,
+ jthread** threads_ptr);
+
+ /* 5 : Suspend Thread */
+ jvmtiError (JNICALL *SuspendThread) (jvmtiEnv* env,
+ jthread thread);
+
+ /* 6 : Resume Thread */
+ jvmtiError (JNICALL *ResumeThread) (jvmtiEnv* env,
+ jthread thread);
+
+ /* 7 : Stop Thread */
+ jvmtiError (JNICALL *StopThread) (jvmtiEnv* env,
+ jthread thread,
+ jobject exception);
+
+ /* 8 : Interrupt Thread */
+ jvmtiError (JNICALL *InterruptThread) (jvmtiEnv* env,
+ jthread thread);
+
+ /* 9 : Get Thread Info */
+ jvmtiError (JNICALL *GetThreadInfo) (jvmtiEnv* env,
+ jthread thread,
+ jvmtiThreadInfo* info_ptr);
+
+ /* 10 : Get Owned Monitor Info */
+ jvmtiError (JNICALL *GetOwnedMonitorInfo) (jvmtiEnv* env,
+ jthread thread,
+ jint* owned_monitor_count_ptr,
+ jobject** owned_monitors_ptr);
+
+ /* 11 : Get Current Contended Monitor */
+ jvmtiError (JNICALL *GetCurrentContendedMonitor) (jvmtiEnv* env,
+ jthread thread,
+ jobject* monitor_ptr);
+
+ /* 12 : Run Agent Thread */
+ jvmtiError (JNICALL *RunAgentThread) (jvmtiEnv* env,
+ jthread thread,
+ jvmtiStartFunction proc,
+ const void* arg,
+ jint priority);
+
+ /* 13 : Get Top Thread Groups */
+ jvmtiError (JNICALL *GetTopThreadGroups) (jvmtiEnv* env,
+ jint* group_count_ptr,
+ jthreadGroup** groups_ptr);
+
+ /* 14 : Get Thread Group Info */
+ jvmtiError (JNICALL *GetThreadGroupInfo) (jvmtiEnv* env,
+ jthreadGroup group,
+ jvmtiThreadGroupInfo* info_ptr);
+
+ /* 15 : Get Thread Group Children */
+ jvmtiError (JNICALL *GetThreadGroupChildren) (jvmtiEnv* env,
+ jthreadGroup group,
+ jint* thread_count_ptr,
+ jthread** threads_ptr,
+ jint* group_count_ptr,
+ jthreadGroup** groups_ptr);
+
+ /* 16 : Get Frame Count */
+ jvmtiError (JNICALL *GetFrameCount) (jvmtiEnv* env,
+ jthread thread,
+ jint* count_ptr);
+
+ /* 17 : Get Thread State */
+ jvmtiError (JNICALL *GetThreadState) (jvmtiEnv* env,
+ jthread thread,
+ jint* thread_state_ptr);
+
+ /* 18 : Get Current Thread */
+ jvmtiError (JNICALL *GetCurrentThread) (jvmtiEnv* env,
+ jthread* thread_ptr);
+
+ /* 19 : Get Frame Location */
+ jvmtiError (JNICALL *GetFrameLocation) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jmethodID* method_ptr,
+ jlocation* location_ptr);
+
+ /* 20 : Notify Frame Pop */
+ jvmtiError (JNICALL *NotifyFramePop) (jvmtiEnv* env,
+ jthread thread,
+ jint depth);
+
+ /* 21 : Get Local Variable - Object */
+ jvmtiError (JNICALL *GetLocalObject) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jobject* value_ptr);
+
+ /* 22 : Get Local Variable - Int */
+ jvmtiError (JNICALL *GetLocalInt) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jint* value_ptr);
+
+ /* 23 : Get Local Variable - Long */
+ jvmtiError (JNICALL *GetLocalLong) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jlong* value_ptr);
+
+ /* 24 : Get Local Variable - Float */
+ jvmtiError (JNICALL *GetLocalFloat) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jfloat* value_ptr);
+
+ /* 25 : Get Local Variable - Double */
+ jvmtiError (JNICALL *GetLocalDouble) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jdouble* value_ptr);
+
+ /* 26 : Set Local Variable - Object */
+ jvmtiError (JNICALL *SetLocalObject) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jobject value);
+
+ /* 27 : Set Local Variable - Int */
+ jvmtiError (JNICALL *SetLocalInt) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jint value);
+
+ /* 28 : Set Local Variable - Long */
+ jvmtiError (JNICALL *SetLocalLong) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jlong value);
+
+ /* 29 : Set Local Variable - Float */
+ jvmtiError (JNICALL *SetLocalFloat) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jfloat value);
+
+ /* 30 : Set Local Variable - Double */
+ jvmtiError (JNICALL *SetLocalDouble) (jvmtiEnv* env,
+ jthread thread,
+ jint depth,
+ jint slot,
+ jdouble value);
+
+ /* 31 : Create Raw Monitor */
+ jvmtiError (JNICALL *CreateRawMonitor) (jvmtiEnv* env,
+ const char* name,
+ jrawMonitorID* monitor_ptr);
+
+ /* 32 : Destroy Raw Monitor */
+ jvmtiError (JNICALL *DestroyRawMonitor) (jvmtiEnv* env,
+ jrawMonitorID monitor);
+
+ /* 33 : Raw Monitor Enter */
+ jvmtiError (JNICALL *RawMonitorEnter) (jvmtiEnv* env,
+ jrawMonitorID monitor);
+
+ /* 34 : Raw Monitor Exit */
+ jvmtiError (JNICALL *RawMonitorExit) (jvmtiEnv* env,
+ jrawMonitorID monitor);
+
+ /* 35 : Raw Monitor Wait */
+ jvmtiError (JNICALL *RawMonitorWait) (jvmtiEnv* env,
+ jrawMonitorID monitor,
+ jlong millis);
+
+ /* 36 : Raw Monitor Notify */
+ jvmtiError (JNICALL *RawMonitorNotify) (jvmtiEnv* env,
+ jrawMonitorID monitor);
+
+ /* 37 : Raw Monitor Notify All */
+ jvmtiError (JNICALL *RawMonitorNotifyAll) (jvmtiEnv* env,
+ jrawMonitorID monitor);
+
+ /* 38 : Set Breakpoint */
+ jvmtiError (JNICALL *SetBreakpoint) (jvmtiEnv* env,
+ jmethodID method,
+ jlocation location);
+
+ /* 39 : Clear Breakpoint */
+ jvmtiError (JNICALL *ClearBreakpoint) (jvmtiEnv* env,
+ jmethodID method,
+ jlocation location);
+
+ /* 40 : RESERVED */
+ void *reserved40;
+
+ /* 41 : Set Field Access Watch */
+ jvmtiError (JNICALL *SetFieldAccessWatch) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field);
+
+ /* 42 : Clear Field Access Watch */
+ jvmtiError (JNICALL *ClearFieldAccessWatch) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field);
+
+ /* 43 : Set Field Modification Watch */
+ jvmtiError (JNICALL *SetFieldModificationWatch) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field);
+
+ /* 44 : Clear Field Modification Watch */
+ jvmtiError (JNICALL *ClearFieldModificationWatch) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field);
+
+ /* 45 : Is Modifiable Class */
+ jvmtiError (JNICALL *IsModifiableClass) (jvmtiEnv* env,
+ jclass klass,
+ jboolean* is_modifiable_class_ptr);
+
+ /* 46 : Allocate */
+ jvmtiError (JNICALL *Allocate) (jvmtiEnv* env,
+ jlong size,
+ unsigned char** mem_ptr);
+
+ /* 47 : Deallocate */
+ jvmtiError (JNICALL *Deallocate) (jvmtiEnv* env,
+ unsigned char* mem);
+
+ /* 48 : Get Class Signature */
+ jvmtiError (JNICALL *GetClassSignature) (jvmtiEnv* env,
+ jclass klass,
+ char** signature_ptr,
+ char** generic_ptr);
+
+ /* 49 : Get Class Status */
+ jvmtiError (JNICALL *GetClassStatus) (jvmtiEnv* env,
+ jclass klass,
+ jint* status_ptr);
+
+ /* 50 : Get Source File Name */
+ jvmtiError (JNICALL *GetSourceFileName) (jvmtiEnv* env,
+ jclass klass,
+ char** source_name_ptr);
+
+ /* 51 : Get Class Modifiers */
+ jvmtiError (JNICALL *GetClassModifiers) (jvmtiEnv* env,
+ jclass klass,
+ jint* modifiers_ptr);
+
+ /* 52 : Get Class Methods */
+ jvmtiError (JNICALL *GetClassMethods) (jvmtiEnv* env,
+ jclass klass,
+ jint* method_count_ptr,
+ jmethodID** methods_ptr);
+
+ /* 53 : Get Class Fields */
+ jvmtiError (JNICALL *GetClassFields) (jvmtiEnv* env,
+ jclass klass,
+ jint* field_count_ptr,
+ jfieldID** fields_ptr);
+
+ /* 54 : Get Implemented Interfaces */
+ jvmtiError (JNICALL *GetImplementedInterfaces) (jvmtiEnv* env,
+ jclass klass,
+ jint* interface_count_ptr,
+ jclass** interfaces_ptr);
+
+ /* 55 : Is Interface */
+ jvmtiError (JNICALL *IsInterface) (jvmtiEnv* env,
+ jclass klass,
+ jboolean* is_interface_ptr);
+
+ /* 56 : Is Array Class */
+ jvmtiError (JNICALL *IsArrayClass) (jvmtiEnv* env,
+ jclass klass,
+ jboolean* is_array_class_ptr);
+
+ /* 57 : Get Class Loader */
+ jvmtiError (JNICALL *GetClassLoader) (jvmtiEnv* env,
+ jclass klass,
+ jobject* classloader_ptr);
+
+ /* 58 : Get Object Hash Code */
+ jvmtiError (JNICALL *GetObjectHashCode) (jvmtiEnv* env,
+ jobject object,
+ jint* hash_code_ptr);
+
+ /* 59 : Get Object Monitor Usage */
+ jvmtiError (JNICALL *GetObjectMonitorUsage) (jvmtiEnv* env,
+ jobject object,
+ jvmtiMonitorUsage* info_ptr);
+
+ /* 60 : Get Field Name (and Signature) */
+ jvmtiError (JNICALL *GetFieldName) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr);
+
+ /* 61 : Get Field Declaring Class */
+ jvmtiError (JNICALL *GetFieldDeclaringClass) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jclass* declaring_class_ptr);
+
+ /* 62 : Get Field Modifiers */
+ jvmtiError (JNICALL *GetFieldModifiers) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jint* modifiers_ptr);
+
+ /* 63 : Is Field Synthetic */
+ jvmtiError (JNICALL *IsFieldSynthetic) (jvmtiEnv* env,
+ jclass klass,
+ jfieldID field,
+ jboolean* is_synthetic_ptr);
+
+ /* 64 : Get Method Name (and Signature) */
+ jvmtiError (JNICALL *GetMethodName) (jvmtiEnv* env,
+ jmethodID method,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr);
+
+ /* 65 : Get Method Declaring Class */
+ jvmtiError (JNICALL *GetMethodDeclaringClass) (jvmtiEnv* env,
+ jmethodID method,
+ jclass* declaring_class_ptr);
+
+ /* 66 : Get Method Modifiers */
+ jvmtiError (JNICALL *GetMethodModifiers) (jvmtiEnv* env,
+ jmethodID method,
+ jint* modifiers_ptr);
+
+ /* 67 : RESERVED */
+ void *reserved67;
+
+ /* 68 : Get Max Locals */
+ jvmtiError (JNICALL *GetMaxLocals) (jvmtiEnv* env,
+ jmethodID method,
+ jint* max_ptr);
+
+ /* 69 : Get Arguments Size */
+ jvmtiError (JNICALL *GetArgumentsSize) (jvmtiEnv* env,
+ jmethodID method,
+ jint* size_ptr);
+
+ /* 70 : Get Line Number Table */
+ jvmtiError (JNICALL *GetLineNumberTable) (jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLineNumberEntry** table_ptr);
+
+ /* 71 : Get Method Location */
+ jvmtiError (JNICALL *GetMethodLocation) (jvmtiEnv* env,
+ jmethodID method,
+ jlocation* start_location_ptr,
+ jlocation* end_location_ptr);
+
+ /* 72 : Get Local Variable Table */
+ jvmtiError (JNICALL *GetLocalVariableTable) (jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLocalVariableEntry** table_ptr);
+
+ /* 73 : Set Native Method Prefix */
+ jvmtiError (JNICALL *SetNativeMethodPrefix) (jvmtiEnv* env,
+ const char* prefix);
+
+ /* 74 : Set Native Method Prefixes */
+ jvmtiError (JNICALL *SetNativeMethodPrefixes) (jvmtiEnv* env,
+ jint prefix_count,
+ char** prefixes);
+
+ /* 75 : Get Bytecodes */
+ jvmtiError (JNICALL *GetBytecodes) (jvmtiEnv* env,
+ jmethodID method,
+ jint* bytecode_count_ptr,
+ unsigned char** bytecodes_ptr);
+
+ /* 76 : Is Method Native */
+ jvmtiError (JNICALL *IsMethodNative) (jvmtiEnv* env,
+ jmethodID method,
+ jboolean* is_native_ptr);
+
+ /* 77 : Is Method Synthetic */
+ jvmtiError (JNICALL *IsMethodSynthetic) (jvmtiEnv* env,
+ jmethodID method,
+ jboolean* is_synthetic_ptr);
+
+ /* 78 : Get Loaded Classes */
+ jvmtiError (JNICALL *GetLoadedClasses) (jvmtiEnv* env,
+ jint* class_count_ptr,
+ jclass** classes_ptr);
+
+ /* 79 : Get Classloader Classes */
+ jvmtiError (JNICALL *GetClassLoaderClasses) (jvmtiEnv* env,
+ jobject initiating_loader,
+ jint* class_count_ptr,
+ jclass** classes_ptr);
+
+ /* 80 : Pop Frame */
+ jvmtiError (JNICALL *PopFrame) (jvmtiEnv* env,
+ jthread thread);
+
+ /* 81 : Force Early Return - Object */
+ jvmtiError (JNICALL *ForceEarlyReturnObject) (jvmtiEnv* env,
+ jthread thread,
+ jobject value);
+
+ /* 82 : Force Early Return - Int */
+ jvmtiError (JNICALL *ForceEarlyReturnInt) (jvmtiEnv* env,
+ jthread thread,
+ jint value);
+
+ /* 83 : Force Early Return - Long */
+ jvmtiError (JNICALL *ForceEarlyReturnLong) (jvmtiEnv* env,
+ jthread thread,
+ jlong value);
+
+ /* 84 : Force Early Return - Float */
+ jvmtiError (JNICALL *ForceEarlyReturnFloat) (jvmtiEnv* env,
+ jthread thread,
+ jfloat value);
+
+ /* 85 : Force Early Return - Double */
+ jvmtiError (JNICALL *ForceEarlyReturnDouble) (jvmtiEnv* env,
+ jthread thread,
+ jdouble value);
+
+ /* 86 : Force Early Return - Void */
+ jvmtiError (JNICALL *ForceEarlyReturnVoid) (jvmtiEnv* env,
+ jthread thread);
+
+ /* 87 : Redefine Classes */
+ jvmtiError (JNICALL *RedefineClasses) (jvmtiEnv* env,
+ jint class_count,
+ const jvmtiClassDefinition* class_definitions);
+
+ /* 88 : Get Version Number */
+ jvmtiError (JNICALL *GetVersionNumber) (jvmtiEnv* env,
+ jint* version_ptr);
+
+ /* 89 : Get Capabilities */
+ jvmtiError (JNICALL *GetCapabilities) (jvmtiEnv* env,
+ jvmtiCapabilities* capabilities_ptr);
+
+ /* 90 : Get Source Debug Extension */
+ jvmtiError (JNICALL *GetSourceDebugExtension) (jvmtiEnv* env,
+ jclass klass,
+ char** source_debug_extension_ptr);
+
+ /* 91 : Is Method Obsolete */
+ jvmtiError (JNICALL *IsMethodObsolete) (jvmtiEnv* env,
+ jmethodID method,
+ jboolean* is_obsolete_ptr);
+
+ /* 92 : Suspend Thread List */
+ jvmtiError (JNICALL *SuspendThreadList) (jvmtiEnv* env,
+ jint request_count,
+ const jthread* request_list,
+ jvmtiError* results);
+
+ /* 93 : Resume Thread List */
+ jvmtiError (JNICALL *ResumeThreadList) (jvmtiEnv* env,
+ jint request_count,
+ const jthread* request_list,
+ jvmtiError* results);
+
+ /* 94 : RESERVED */
+ void *reserved94;
+
+ /* 95 : RESERVED */
+ void *reserved95;
+
+ /* 96 : RESERVED */
+ void *reserved96;
+
+ /* 97 : RESERVED */
+ void *reserved97;
+
+ /* 98 : RESERVED */
+ void *reserved98;
+
+ /* 99 : RESERVED */
+ void *reserved99;
+
+ /* 100 : Get All Stack Traces */
+ jvmtiError (JNICALL *GetAllStackTraces) (jvmtiEnv* env,
+ jint max_frame_count,
+ jvmtiStackInfo** stack_info_ptr,
+ jint* thread_count_ptr);
+
+ /* 101 : Get Thread List Stack Traces */
+ jvmtiError (JNICALL *GetThreadListStackTraces) (jvmtiEnv* env,
+ jint thread_count,
+ const jthread* thread_list,
+ jint max_frame_count,
+ jvmtiStackInfo** stack_info_ptr);
+
+ /* 102 : Get Thread Local Storage */
+ jvmtiError (JNICALL *GetThreadLocalStorage) (jvmtiEnv* env,
+ jthread thread,
+ void** data_ptr);
+
+ /* 103 : Set Thread Local Storage */
+ jvmtiError (JNICALL *SetThreadLocalStorage) (jvmtiEnv* env,
+ jthread thread,
+ const void* data);
+
+ /* 104 : Get Stack Trace */
+ jvmtiError (JNICALL *GetStackTrace) (jvmtiEnv* env,
+ jthread thread,
+ jint start_depth,
+ jint max_frame_count,
+ jvmtiFrameInfo* frame_buffer,
+ jint* count_ptr);
+
+ /* 105 : RESERVED */
+ void *reserved105;
+
+ /* 106 : Get Tag */
+ jvmtiError (JNICALL *GetTag) (jvmtiEnv* env,
+ jobject object,
+ jlong* tag_ptr);
+
+ /* 107 : Set Tag */
+ jvmtiError (JNICALL *SetTag) (jvmtiEnv* env,
+ jobject object,
+ jlong tag);
+
+ /* 108 : Force Garbage Collection */
+ jvmtiError (JNICALL *ForceGarbageCollection) (jvmtiEnv* env);
+
+ /* 109 : Iterate Over Objects Reachable From Object */
+ jvmtiError (JNICALL *IterateOverObjectsReachableFromObject) (jvmtiEnv* env,
+ jobject object,
+ jvmtiObjectReferenceCallback object_reference_callback,
+ const void* user_data);
+
+ /* 110 : Iterate Over Reachable Objects */
+ jvmtiError (JNICALL *IterateOverReachableObjects) (jvmtiEnv* env,
+ jvmtiHeapRootCallback heap_root_callback,
+ jvmtiStackReferenceCallback stack_ref_callback,
+ jvmtiObjectReferenceCallback object_ref_callback,
+ const void* user_data);
+
+ /* 111 : Iterate Over Heap */
+ jvmtiError (JNICALL *IterateOverHeap) (jvmtiEnv* env,
+ jvmtiHeapObjectFilter object_filter,
+ jvmtiHeapObjectCallback heap_object_callback,
+ const void* user_data);
+
+ /* 112 : Iterate Over Instances Of Class */
+ jvmtiError (JNICALL *IterateOverInstancesOfClass) (jvmtiEnv* env,
+ jclass klass,
+ jvmtiHeapObjectFilter object_filter,
+ jvmtiHeapObjectCallback heap_object_callback,
+ const void* user_data);
+
+ /* 113 : RESERVED */
+ void *reserved113;
+
+ /* 114 : Get Objects With Tags */
+ jvmtiError (JNICALL *GetObjectsWithTags) (jvmtiEnv* env,
+ jint tag_count,
+ const jlong* tags,
+ jint* count_ptr,
+ jobject** object_result_ptr,
+ jlong** tag_result_ptr);
+
+ /* 115 : Follow References */
+ jvmtiError (JNICALL *FollowReferences) (jvmtiEnv* env,
+ jint heap_filter,
+ jclass klass,
+ jobject initial_object,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data);
+
+ /* 116 : Iterate Through Heap */
+ jvmtiError (JNICALL *IterateThroughHeap) (jvmtiEnv* env,
+ jint heap_filter,
+ jclass klass,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data);
+
+ /* 117 : RESERVED */
+ void *reserved117;
+
+ /* 118 : RESERVED */
+ void *reserved118;
+
+ /* 119 : RESERVED */
+ void *reserved119;
+
+ /* 120 : Set JNI Function Table */
+ jvmtiError (JNICALL *SetJNIFunctionTable) (jvmtiEnv* env,
+ const jniNativeInterface* function_table);
+
+ /* 121 : Get JNI Function Table */
+ jvmtiError (JNICALL *GetJNIFunctionTable) (jvmtiEnv* env,
+ jniNativeInterface** function_table);
+
+ /* 122 : Set Event Callbacks */
+ jvmtiError (JNICALL *SetEventCallbacks) (jvmtiEnv* env,
+ const jvmtiEventCallbacks* callbacks,
+ jint size_of_callbacks);
+
+ /* 123 : Generate Events */
+ jvmtiError (JNICALL *GenerateEvents) (jvmtiEnv* env,
+ jvmtiEvent event_type);
+
+ /* 124 : Get Extension Functions */
+ jvmtiError (JNICALL *GetExtensionFunctions) (jvmtiEnv* env,
+ jint* extension_count_ptr,
+ jvmtiExtensionFunctionInfo** extensions);
+
+ /* 125 : Get Extension Events */
+ jvmtiError (JNICALL *GetExtensionEvents) (jvmtiEnv* env,
+ jint* extension_count_ptr,
+ jvmtiExtensionEventInfo** extensions);
+
+ /* 126 : Set Extension Event Callback */
+ jvmtiError (JNICALL *SetExtensionEventCallback) (jvmtiEnv* env,
+ jint extension_event_index,
+ jvmtiExtensionEvent callback);
+
+ /* 127 : Dispose Environment */
+ jvmtiError (JNICALL *DisposeEnvironment) (jvmtiEnv* env);
+
+ /* 128 : Get Error Name */
+ jvmtiError (JNICALL *GetErrorName) (jvmtiEnv* env,
+ jvmtiError error,
+ char** name_ptr);
+
+ /* 129 : Get JLocation Format */
+ jvmtiError (JNICALL *GetJLocationFormat) (jvmtiEnv* env,
+ jvmtiJlocationFormat* format_ptr);
+
+ /* 130 : Get System Properties */
+ jvmtiError (JNICALL *GetSystemProperties) (jvmtiEnv* env,
+ jint* count_ptr,
+ char*** property_ptr);
+
+ /* 131 : Get System Property */
+ jvmtiError (JNICALL *GetSystemProperty) (jvmtiEnv* env,
+ const char* property,
+ char** value_ptr);
+
+ /* 132 : Set System Property */
+ jvmtiError (JNICALL *SetSystemProperty) (jvmtiEnv* env,
+ const char* property,
+ const char* value);
+
+ /* 133 : Get Phase */
+ jvmtiError (JNICALL *GetPhase) (jvmtiEnv* env,
+ jvmtiPhase* phase_ptr);
+
+ /* 134 : Get Current Thread CPU Timer Information */
+ jvmtiError (JNICALL *GetCurrentThreadCpuTimerInfo) (jvmtiEnv* env,
+ jvmtiTimerInfo* info_ptr);
+
+ /* 135 : Get Current Thread CPU Time */
+ jvmtiError (JNICALL *GetCurrentThreadCpuTime) (jvmtiEnv* env,
+ jlong* nanos_ptr);
+
+ /* 136 : Get Thread CPU Timer Information */
+ jvmtiError (JNICALL *GetThreadCpuTimerInfo) (jvmtiEnv* env,
+ jvmtiTimerInfo* info_ptr);
+
+ /* 137 : Get Thread CPU Time */
+ jvmtiError (JNICALL *GetThreadCpuTime) (jvmtiEnv* env,
+ jthread thread,
+ jlong* nanos_ptr);
+
+ /* 138 : Get Timer Information */
+ jvmtiError (JNICALL *GetTimerInfo) (jvmtiEnv* env,
+ jvmtiTimerInfo* info_ptr);
+
+ /* 139 : Get Time */
+ jvmtiError (JNICALL *GetTime) (jvmtiEnv* env,
+ jlong* nanos_ptr);
+
+ /* 140 : Get Potential Capabilities */
+ jvmtiError (JNICALL *GetPotentialCapabilities) (jvmtiEnv* env,
+ jvmtiCapabilities* capabilities_ptr);
+
+ /* 141 : RESERVED */
+ void *reserved141;
+
+ /* 142 : Add Capabilities */
+ jvmtiError (JNICALL *AddCapabilities) (jvmtiEnv* env,
+ const jvmtiCapabilities* capabilities_ptr);
+
+ /* 143 : Relinquish Capabilities */
+ jvmtiError (JNICALL *RelinquishCapabilities) (jvmtiEnv* env,
+ const jvmtiCapabilities* capabilities_ptr);
+
+ /* 144 : Get Available Processors */
+ jvmtiError (JNICALL *GetAvailableProcessors) (jvmtiEnv* env,
+ jint* processor_count_ptr);
+
+ /* 145 : Get Class Version Numbers */
+ jvmtiError (JNICALL *GetClassVersionNumbers) (jvmtiEnv* env,
+ jclass klass,
+ jint* minor_version_ptr,
+ jint* major_version_ptr);
+
+ /* 146 : Get Constant Pool */
+ jvmtiError (JNICALL *GetConstantPool) (jvmtiEnv* env,
+ jclass klass,
+ jint* constant_pool_count_ptr,
+ jint* constant_pool_byte_count_ptr,
+ unsigned char** constant_pool_bytes_ptr);
+
+ /* 147 : Get Environment Local Storage */
+ jvmtiError (JNICALL *GetEnvironmentLocalStorage) (jvmtiEnv* env,
+ void** data_ptr);
+
+ /* 148 : Set Environment Local Storage */
+ jvmtiError (JNICALL *SetEnvironmentLocalStorage) (jvmtiEnv* env,
+ const void* data);
+
+ /* 149 : Add To Bootstrap Class Loader Search */
+ jvmtiError (JNICALL *AddToBootstrapClassLoaderSearch) (jvmtiEnv* env,
+ const char* segment);
+
+ /* 150 : Set Verbose Flag */
+ jvmtiError (JNICALL *SetVerboseFlag) (jvmtiEnv* env,
+ jvmtiVerboseFlag flag,
+ jboolean value);
+
+ /* 151 : Add To System Class Loader Search */
+ jvmtiError (JNICALL *AddToSystemClassLoaderSearch) (jvmtiEnv* env,
+ const char* segment);
+
+ /* 152 : Retransform Classes */
+ jvmtiError (JNICALL *RetransformClasses) (jvmtiEnv* env,
+ jint class_count,
+ const jclass* classes);
+
+ /* 153 : Get Owned Monitor Stack Depth Info */
+ jvmtiError (JNICALL *GetOwnedMonitorStackDepthInfo) (jvmtiEnv* env,
+ jthread thread,
+ jint* monitor_info_count_ptr,
+ jvmtiMonitorStackDepthInfo** monitor_info_ptr);
+
+ /* 154 : Get Object Size */
+ jvmtiError (JNICALL *GetObjectSize) (jvmtiEnv* env,
+ jobject object,
+ jlong* size_ptr);
+
+} jvmtiInterface_1;
+
+struct _jvmtiEnv {
+ const struct jvmtiInterface_1_ *functions;
+#ifdef __cplusplus
+
+
+ jvmtiError Allocate(jlong size,
+ unsigned char** mem_ptr) {
+ return functions->Allocate(this, size, mem_ptr);
+ }
+
+ jvmtiError Deallocate(unsigned char* mem) {
+ return functions->Deallocate(this, mem);
+ }
+
+ jvmtiError GetThreadState(jthread thread,
+ jint* thread_state_ptr) {
+ return functions->GetThreadState(this, thread, thread_state_ptr);
+ }
+
+ jvmtiError GetCurrentThread(jthread* thread_ptr) {
+ return functions->GetCurrentThread(this, thread_ptr);
+ }
+
+ jvmtiError GetAllThreads(jint* threads_count_ptr,
+ jthread** threads_ptr) {
+ return functions->GetAllThreads(this, threads_count_ptr, threads_ptr);
+ }
+
+ jvmtiError SuspendThread(jthread thread) {
+ return functions->SuspendThread(this, thread);
+ }
+
+ jvmtiError SuspendThreadList(jint request_count,
+ const jthread* request_list,
+ jvmtiError* results) {
+ return functions->SuspendThreadList(this, request_count, request_list, results);
+ }
+
+ jvmtiError ResumeThread(jthread thread) {
+ return functions->ResumeThread(this, thread);
+ }
+
+ jvmtiError ResumeThreadList(jint request_count,
+ const jthread* request_list,
+ jvmtiError* results) {
+ return functions->ResumeThreadList(this, request_count, request_list, results);
+ }
+
+ jvmtiError StopThread(jthread thread,
+ jobject exception) {
+ return functions->StopThread(this, thread, exception);
+ }
+
+ jvmtiError InterruptThread(jthread thread) {
+ return functions->InterruptThread(this, thread);
+ }
+
+ jvmtiError GetThreadInfo(jthread thread,
+ jvmtiThreadInfo* info_ptr) {
+ return functions->GetThreadInfo(this, thread, info_ptr);
+ }
+
+ jvmtiError GetOwnedMonitorInfo(jthread thread,
+ jint* owned_monitor_count_ptr,
+ jobject** owned_monitors_ptr) {
+ return functions->GetOwnedMonitorInfo(this, thread, owned_monitor_count_ptr, owned_monitors_ptr);
+ }
+
+ jvmtiError GetOwnedMonitorStackDepthInfo(jthread thread,
+ jint* monitor_info_count_ptr,
+ jvmtiMonitorStackDepthInfo** monitor_info_ptr) {
+ return functions->GetOwnedMonitorStackDepthInfo(this, thread, monitor_info_count_ptr, monitor_info_ptr);
+ }
+
+ jvmtiError GetCurrentContendedMonitor(jthread thread,
+ jobject* monitor_ptr) {
+ return functions->GetCurrentContendedMonitor(this, thread, monitor_ptr);
+ }
+
+ jvmtiError RunAgentThread(jthread thread,
+ jvmtiStartFunction proc,
+ const void* arg,
+ jint priority) {
+ return functions->RunAgentThread(this, thread, proc, arg, priority);
+ }
+
+ jvmtiError SetThreadLocalStorage(jthread thread,
+ const void* data) {
+ return functions->SetThreadLocalStorage(this, thread, data);
+ }
+
+ jvmtiError GetThreadLocalStorage(jthread thread,
+ void** data_ptr) {
+ return functions->GetThreadLocalStorage(this, thread, data_ptr);
+ }
+
+ jvmtiError GetTopThreadGroups(jint* group_count_ptr,
+ jthreadGroup** groups_ptr) {
+ return functions->GetTopThreadGroups(this, group_count_ptr, groups_ptr);
+ }
+
+ jvmtiError GetThreadGroupInfo(jthreadGroup group,
+ jvmtiThreadGroupInfo* info_ptr) {
+ return functions->GetThreadGroupInfo(this, group, info_ptr);
+ }
+
+ jvmtiError GetThreadGroupChildren(jthreadGroup group,
+ jint* thread_count_ptr,
+ jthread** threads_ptr,
+ jint* group_count_ptr,
+ jthreadGroup** groups_ptr) {
+ return functions->GetThreadGroupChildren(this, group, thread_count_ptr, threads_ptr, group_count_ptr, groups_ptr);
+ }
+
+ jvmtiError GetStackTrace(jthread thread,
+ jint start_depth,
+ jint max_frame_count,
+ jvmtiFrameInfo* frame_buffer,
+ jint* count_ptr) {
+ return functions->GetStackTrace(this, thread, start_depth, max_frame_count, frame_buffer, count_ptr);
+ }
+
+ jvmtiError GetAllStackTraces(jint max_frame_count,
+ jvmtiStackInfo** stack_info_ptr,
+ jint* thread_count_ptr) {
+ return functions->GetAllStackTraces(this, max_frame_count, stack_info_ptr, thread_count_ptr);
+ }
+
+ jvmtiError GetThreadListStackTraces(jint thread_count,
+ const jthread* thread_list,
+ jint max_frame_count,
+ jvmtiStackInfo** stack_info_ptr) {
+ return functions->GetThreadListStackTraces(this, thread_count, thread_list, max_frame_count, stack_info_ptr);
+ }
+
+ jvmtiError GetFrameCount(jthread thread,
+ jint* count_ptr) {
+ return functions->GetFrameCount(this, thread, count_ptr);
+ }
+
+ jvmtiError PopFrame(jthread thread) {
+ return functions->PopFrame(this, thread);
+ }
+
+ jvmtiError GetFrameLocation(jthread thread,
+ jint depth,
+ jmethodID* method_ptr,
+ jlocation* location_ptr) {
+ return functions->GetFrameLocation(this, thread, depth, method_ptr, location_ptr);
+ }
+
+ jvmtiError NotifyFramePop(jthread thread,
+ jint depth) {
+ return functions->NotifyFramePop(this, thread, depth);
+ }
+
+ jvmtiError ForceEarlyReturnObject(jthread thread,
+ jobject value) {
+ return functions->ForceEarlyReturnObject(this, thread, value);
+ }
+
+ jvmtiError ForceEarlyReturnInt(jthread thread,
+ jint value) {
+ return functions->ForceEarlyReturnInt(this, thread, value);
+ }
+
+ jvmtiError ForceEarlyReturnLong(jthread thread,
+ jlong value) {
+ return functions->ForceEarlyReturnLong(this, thread, value);
+ }
+
+ jvmtiError ForceEarlyReturnFloat(jthread thread,
+ jfloat value) {
+ return functions->ForceEarlyReturnFloat(this, thread, value);
+ }
+
+ jvmtiError ForceEarlyReturnDouble(jthread thread,
+ jdouble value) {
+ return functions->ForceEarlyReturnDouble(this, thread, value);
+ }
+
+ jvmtiError ForceEarlyReturnVoid(jthread thread) {
+ return functions->ForceEarlyReturnVoid(this, thread);
+ }
+
+ jvmtiError FollowReferences(jint heap_filter,
+ jclass klass,
+ jobject initial_object,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data) {
+ return functions->FollowReferences(this, heap_filter, klass, initial_object, callbacks, user_data);
+ }
+
+ jvmtiError IterateThroughHeap(jint heap_filter,
+ jclass klass,
+ const jvmtiHeapCallbacks* callbacks,
+ const void* user_data) {
+ return functions->IterateThroughHeap(this, heap_filter, klass, callbacks, user_data);
+ }
+
+ jvmtiError GetTag(jobject object,
+ jlong* tag_ptr) {
+ return functions->GetTag(this, object, tag_ptr);
+ }
+
+ jvmtiError SetTag(jobject object,
+ jlong tag) {
+ return functions->SetTag(this, object, tag);
+ }
+
+ jvmtiError GetObjectsWithTags(jint tag_count,
+ const jlong* tags,
+ jint* count_ptr,
+ jobject** object_result_ptr,
+ jlong** tag_result_ptr) {
+ return functions->GetObjectsWithTags(this, tag_count, tags, count_ptr, object_result_ptr, tag_result_ptr);
+ }
+
+ jvmtiError ForceGarbageCollection() {
+ return functions->ForceGarbageCollection(this);
+ }
+
+ jvmtiError IterateOverObjectsReachableFromObject(jobject object,
+ jvmtiObjectReferenceCallback object_reference_callback,
+ const void* user_data) {
+ return functions->IterateOverObjectsReachableFromObject(this, object, object_reference_callback, user_data);
+ }
+
+ jvmtiError IterateOverReachableObjects(jvmtiHeapRootCallback heap_root_callback,
+ jvmtiStackReferenceCallback stack_ref_callback,
+ jvmtiObjectReferenceCallback object_ref_callback,
+ const void* user_data) {
+ return functions->IterateOverReachableObjects(this, heap_root_callback, stack_ref_callback, object_ref_callback, user_data);
+ }
+
+ jvmtiError IterateOverHeap(jvmtiHeapObjectFilter object_filter,
+ jvmtiHeapObjectCallback heap_object_callback,
+ const void* user_data) {
+ return functions->IterateOverHeap(this, object_filter, heap_object_callback, user_data);
+ }
+
+ jvmtiError IterateOverInstancesOfClass(jclass klass,
+ jvmtiHeapObjectFilter object_filter,
+ jvmtiHeapObjectCallback heap_object_callback,
+ const void* user_data) {
+ return functions->IterateOverInstancesOfClass(this, klass, object_filter, heap_object_callback, user_data);
+ }
+
+ jvmtiError GetLocalObject(jthread thread,
+ jint depth,
+ jint slot,
+ jobject* value_ptr) {
+ return functions->GetLocalObject(this, thread, depth, slot, value_ptr);
+ }
+
+ jvmtiError GetLocalInt(jthread thread,
+ jint depth,
+ jint slot,
+ jint* value_ptr) {
+ return functions->GetLocalInt(this, thread, depth, slot, value_ptr);
+ }
+
+ jvmtiError GetLocalLong(jthread thread,
+ jint depth,
+ jint slot,
+ jlong* value_ptr) {
+ return functions->GetLocalLong(this, thread, depth, slot, value_ptr);
+ }
+
+ jvmtiError GetLocalFloat(jthread thread,
+ jint depth,
+ jint slot,
+ jfloat* value_ptr) {
+ return functions->GetLocalFloat(this, thread, depth, slot, value_ptr);
+ }
+
+ jvmtiError GetLocalDouble(jthread thread,
+ jint depth,
+ jint slot,
+ jdouble* value_ptr) {
+ return functions->GetLocalDouble(this, thread, depth, slot, value_ptr);
+ }
+
+ jvmtiError SetLocalObject(jthread thread,
+ jint depth,
+ jint slot,
+ jobject value) {
+ return functions->SetLocalObject(this, thread, depth, slot, value);
+ }
+
+ jvmtiError SetLocalInt(jthread thread,
+ jint depth,
+ jint slot,
+ jint value) {
+ return functions->SetLocalInt(this, thread, depth, slot, value);
+ }
+
+ jvmtiError SetLocalLong(jthread thread,
+ jint depth,
+ jint slot,
+ jlong value) {
+ return functions->SetLocalLong(this, thread, depth, slot, value);
+ }
+
+ jvmtiError SetLocalFloat(jthread thread,
+ jint depth,
+ jint slot,
+ jfloat value) {
+ return functions->SetLocalFloat(this, thread, depth, slot, value);
+ }
+
+ jvmtiError SetLocalDouble(jthread thread,
+ jint depth,
+ jint slot,
+ jdouble value) {
+ return functions->SetLocalDouble(this, thread, depth, slot, value);
+ }
+
+ jvmtiError SetBreakpoint(jmethodID method,
+ jlocation location) {
+ return functions->SetBreakpoint(this, method, location);
+ }
+
+ jvmtiError ClearBreakpoint(jmethodID method,
+ jlocation location) {
+ return functions->ClearBreakpoint(this, method, location);
+ }
+
+ jvmtiError SetFieldAccessWatch(jclass klass,
+ jfieldID field) {
+ return functions->SetFieldAccessWatch(this, klass, field);
+ }
+
+ jvmtiError ClearFieldAccessWatch(jclass klass,
+ jfieldID field) {
+ return functions->ClearFieldAccessWatch(this, klass, field);
+ }
+
+ jvmtiError SetFieldModificationWatch(jclass klass,
+ jfieldID field) {
+ return functions->SetFieldModificationWatch(this, klass, field);
+ }
+
+ jvmtiError ClearFieldModificationWatch(jclass klass,
+ jfieldID field) {
+ return functions->ClearFieldModificationWatch(this, klass, field);
+ }
+
+ jvmtiError GetLoadedClasses(jint* class_count_ptr,
+ jclass** classes_ptr) {
+ return functions->GetLoadedClasses(this, class_count_ptr, classes_ptr);
+ }
+
+ jvmtiError GetClassLoaderClasses(jobject initiating_loader,
+ jint* class_count_ptr,
+ jclass** classes_ptr) {
+ return functions->GetClassLoaderClasses(this, initiating_loader, class_count_ptr, classes_ptr);
+ }
+
+ jvmtiError GetClassSignature(jclass klass,
+ char** signature_ptr,
+ char** generic_ptr) {
+ return functions->GetClassSignature(this, klass, signature_ptr, generic_ptr);
+ }
+
+ jvmtiError GetClassStatus(jclass klass,
+ jint* status_ptr) {
+ return functions->GetClassStatus(this, klass, status_ptr);
+ }
+
+ jvmtiError GetSourceFileName(jclass klass,
+ char** source_name_ptr) {
+ return functions->GetSourceFileName(this, klass, source_name_ptr);
+ }
+
+ jvmtiError GetClassModifiers(jclass klass,
+ jint* modifiers_ptr) {
+ return functions->GetClassModifiers(this, klass, modifiers_ptr);
+ }
+
+ jvmtiError GetClassMethods(jclass klass,
+ jint* method_count_ptr,
+ jmethodID** methods_ptr) {
+ return functions->GetClassMethods(this, klass, method_count_ptr, methods_ptr);
+ }
+
+ jvmtiError GetClassFields(jclass klass,
+ jint* field_count_ptr,
+ jfieldID** fields_ptr) {
+ return functions->GetClassFields(this, klass, field_count_ptr, fields_ptr);
+ }
+
+ jvmtiError GetImplementedInterfaces(jclass klass,
+ jint* interface_count_ptr,
+ jclass** interfaces_ptr) {
+ return functions->GetImplementedInterfaces(this, klass, interface_count_ptr, interfaces_ptr);
+ }
+
+ jvmtiError GetClassVersionNumbers(jclass klass,
+ jint* minor_version_ptr,
+ jint* major_version_ptr) {
+ return functions->GetClassVersionNumbers(this, klass, minor_version_ptr, major_version_ptr);
+ }
+
+ jvmtiError GetConstantPool(jclass klass,
+ jint* constant_pool_count_ptr,
+ jint* constant_pool_byte_count_ptr,
+ unsigned char** constant_pool_bytes_ptr) {
+ return functions->GetConstantPool(this, klass, constant_pool_count_ptr, constant_pool_byte_count_ptr, constant_pool_bytes_ptr);
+ }
+
+ jvmtiError IsInterface(jclass klass,
+ jboolean* is_interface_ptr) {
+ return functions->IsInterface(this, klass, is_interface_ptr);
+ }
+
+ jvmtiError IsArrayClass(jclass klass,
+ jboolean* is_array_class_ptr) {
+ return functions->IsArrayClass(this, klass, is_array_class_ptr);
+ }
+
+ jvmtiError IsModifiableClass(jclass klass,
+ jboolean* is_modifiable_class_ptr) {
+ return functions->IsModifiableClass(this, klass, is_modifiable_class_ptr);
+ }
+
+ jvmtiError GetClassLoader(jclass klass,
+ jobject* classloader_ptr) {
+ return functions->GetClassLoader(this, klass, classloader_ptr);
+ }
+
+ jvmtiError GetSourceDebugExtension(jclass klass,
+ char** source_debug_extension_ptr) {
+ return functions->GetSourceDebugExtension(this, klass, source_debug_extension_ptr);
+ }
+
+ jvmtiError RetransformClasses(jint class_count,
+ const jclass* classes) {
+ return functions->RetransformClasses(this, class_count, classes);
+ }
+
+ jvmtiError RedefineClasses(jint class_count,
+ const jvmtiClassDefinition* class_definitions) {
+ return functions->RedefineClasses(this, class_count, class_definitions);
+ }
+
+ jvmtiError GetObjectSize(jobject object,
+ jlong* size_ptr) {
+ return functions->GetObjectSize(this, object, size_ptr);
+ }
+
+ jvmtiError GetObjectHashCode(jobject object,
+ jint* hash_code_ptr) {
+ return functions->GetObjectHashCode(this, object, hash_code_ptr);
+ }
+
+ jvmtiError GetObjectMonitorUsage(jobject object,
+ jvmtiMonitorUsage* info_ptr) {
+ return functions->GetObjectMonitorUsage(this, object, info_ptr);
+ }
+
+ jvmtiError GetFieldName(jclass klass,
+ jfieldID field,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr) {
+ return functions->GetFieldName(this, klass, field, name_ptr, signature_ptr, generic_ptr);
+ }
+
+ jvmtiError GetFieldDeclaringClass(jclass klass,
+ jfieldID field,
+ jclass* declaring_class_ptr) {
+ return functions->GetFieldDeclaringClass(this, klass, field, declaring_class_ptr);
+ }
+
+ jvmtiError GetFieldModifiers(jclass klass,
+ jfieldID field,
+ jint* modifiers_ptr) {
+ return functions->GetFieldModifiers(this, klass, field, modifiers_ptr);
+ }
+
+ jvmtiError IsFieldSynthetic(jclass klass,
+ jfieldID field,
+ jboolean* is_synthetic_ptr) {
+ return functions->IsFieldSynthetic(this, klass, field, is_synthetic_ptr);
+ }
+
+ jvmtiError GetMethodName(jmethodID method,
+ char** name_ptr,
+ char** signature_ptr,
+ char** generic_ptr) {
+ return functions->GetMethodName(this, method, name_ptr, signature_ptr, generic_ptr);
+ }
+
+ jvmtiError GetMethodDeclaringClass(jmethodID method,
+ jclass* declaring_class_ptr) {
+ return functions->GetMethodDeclaringClass(this, method, declaring_class_ptr);
+ }
+
+ jvmtiError GetMethodModifiers(jmethodID method,
+ jint* modifiers_ptr) {
+ return functions->GetMethodModifiers(this, method, modifiers_ptr);
+ }
+
+ jvmtiError GetMaxLocals(jmethodID method,
+ jint* max_ptr) {
+ return functions->GetMaxLocals(this, method, max_ptr);
+ }
+
+ jvmtiError GetArgumentsSize(jmethodID method,
+ jint* size_ptr) {
+ return functions->GetArgumentsSize(this, method, size_ptr);
+ }
+
+ jvmtiError GetLineNumberTable(jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLineNumberEntry** table_ptr) {
+ return functions->GetLineNumberTable(this, method, entry_count_ptr, table_ptr);
+ }
+
+ jvmtiError GetMethodLocation(jmethodID method,
+ jlocation* start_location_ptr,
+ jlocation* end_location_ptr) {
+ return functions->GetMethodLocation(this, method, start_location_ptr, end_location_ptr);
+ }
+
+ jvmtiError GetLocalVariableTable(jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLocalVariableEntry** table_ptr) {
+ return functions->GetLocalVariableTable(this, method, entry_count_ptr, table_ptr);
+ }
+
+ jvmtiError GetBytecodes(jmethodID method,
+ jint* bytecode_count_ptr,
+ unsigned char** bytecodes_ptr) {
+ return functions->GetBytecodes(this, method, bytecode_count_ptr, bytecodes_ptr);
+ }
+
+ jvmtiError IsMethodNative(jmethodID method,
+ jboolean* is_native_ptr) {
+ return functions->IsMethodNative(this, method, is_native_ptr);
+ }
+
+ jvmtiError IsMethodSynthetic(jmethodID method,
+ jboolean* is_synthetic_ptr) {
+ return functions->IsMethodSynthetic(this, method, is_synthetic_ptr);
+ }
+
+ jvmtiError IsMethodObsolete(jmethodID method,
+ jboolean* is_obsolete_ptr) {
+ return functions->IsMethodObsolete(this, method, is_obsolete_ptr);
+ }
+
+ jvmtiError SetNativeMethodPrefix(const char* prefix) {
+ return functions->SetNativeMethodPrefix(this, prefix);
+ }
+
+ jvmtiError SetNativeMethodPrefixes(jint prefix_count,
+ char** prefixes) {
+ return functions->SetNativeMethodPrefixes(this, prefix_count, prefixes);
+ }
+
+ jvmtiError CreateRawMonitor(const char* name,
+ jrawMonitorID* monitor_ptr) {
+ return functions->CreateRawMonitor(this, name, monitor_ptr);
+ }
+
+ jvmtiError DestroyRawMonitor(jrawMonitorID monitor) {
+ return functions->DestroyRawMonitor(this, monitor);
+ }
+
+ jvmtiError RawMonitorEnter(jrawMonitorID monitor) {
+ return functions->RawMonitorEnter(this, monitor);
+ }
+
+ jvmtiError RawMonitorExit(jrawMonitorID monitor) {
+ return functions->RawMonitorExit(this, monitor);
+ }
+
+ jvmtiError RawMonitorWait(jrawMonitorID monitor,
+ jlong millis) {
+ return functions->RawMonitorWait(this, monitor, millis);
+ }
+
+ jvmtiError RawMonitorNotify(jrawMonitorID monitor) {
+ return functions->RawMonitorNotify(this, monitor);
+ }
+
+ jvmtiError RawMonitorNotifyAll(jrawMonitorID monitor) {
+ return functions->RawMonitorNotifyAll(this, monitor);
+ }
+
+ jvmtiError SetJNIFunctionTable(const jniNativeInterface* function_table) {
+ return functions->SetJNIFunctionTable(this, function_table);
+ }
+
+ jvmtiError GetJNIFunctionTable(jniNativeInterface** function_table) {
+ return functions->GetJNIFunctionTable(this, function_table);
+ }
+
+ jvmtiError SetEventCallbacks(const jvmtiEventCallbacks* callbacks,
+ jint size_of_callbacks) {
+ return functions->SetEventCallbacks(this, callbacks, size_of_callbacks);
+ }
+
+ jvmtiError SetEventNotificationMode(jvmtiEventMode mode,
+ jvmtiEvent event_type,
+ jthread event_thread,
+ ...) {
+ return functions->SetEventNotificationMode(this, mode, event_type, event_thread);
+ }
+
+ jvmtiError GenerateEvents(jvmtiEvent event_type) {
+ return functions->GenerateEvents(this, event_type);
+ }
+
+ jvmtiError GetExtensionFunctions(jint* extension_count_ptr,
+ jvmtiExtensionFunctionInfo** extensions) {
+ return functions->GetExtensionFunctions(this, extension_count_ptr, extensions);
+ }
+
+ jvmtiError GetExtensionEvents(jint* extension_count_ptr,
+ jvmtiExtensionEventInfo** extensions) {
+ return functions->GetExtensionEvents(this, extension_count_ptr, extensions);
+ }
+
+ jvmtiError SetExtensionEventCallback(jint extension_event_index,
+ jvmtiExtensionEvent callback) {
+ return functions->SetExtensionEventCallback(this, extension_event_index, callback);
+ }
+
+ jvmtiError GetPotentialCapabilities(jvmtiCapabilities* capabilities_ptr) {
+ return functions->GetPotentialCapabilities(this, capabilities_ptr);
+ }
+
+ jvmtiError AddCapabilities(const jvmtiCapabilities* capabilities_ptr) {
+ return functions->AddCapabilities(this, capabilities_ptr);
+ }
+
+ jvmtiError RelinquishCapabilities(const jvmtiCapabilities* capabilities_ptr) {
+ return functions->RelinquishCapabilities(this, capabilities_ptr);
+ }
+
+ jvmtiError GetCapabilities(jvmtiCapabilities* capabilities_ptr) {
+ return functions->GetCapabilities(this, capabilities_ptr);
+ }
+
+ jvmtiError GetCurrentThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) {
+ return functions->GetCurrentThreadCpuTimerInfo(this, info_ptr);
+ }
+
+ jvmtiError GetCurrentThreadCpuTime(jlong* nanos_ptr) {
+ return functions->GetCurrentThreadCpuTime(this, nanos_ptr);
+ }
+
+ jvmtiError GetThreadCpuTimerInfo(jvmtiTimerInfo* info_ptr) {
+ return functions->GetThreadCpuTimerInfo(this, info_ptr);
+ }
+
+ jvmtiError GetThreadCpuTime(jthread thread,
+ jlong* nanos_ptr) {
+ return functions->GetThreadCpuTime(this, thread, nanos_ptr);
+ }
+
+ jvmtiError GetTimerInfo(jvmtiTimerInfo* info_ptr) {
+ return functions->GetTimerInfo(this, info_ptr);
+ }
+
+ jvmtiError GetTime(jlong* nanos_ptr) {
+ return functions->GetTime(this, nanos_ptr);
+ }
+
+ jvmtiError GetAvailableProcessors(jint* processor_count_ptr) {
+ return functions->GetAvailableProcessors(this, processor_count_ptr);
+ }
+
+ jvmtiError AddToBootstrapClassLoaderSearch(const char* segment) {
+ return functions->AddToBootstrapClassLoaderSearch(this, segment);
+ }
+
+ jvmtiError AddToSystemClassLoaderSearch(const char* segment) {
+ return functions->AddToSystemClassLoaderSearch(this, segment);
+ }
+
+ jvmtiError GetSystemProperties(jint* count_ptr,
+ char*** property_ptr) {
+ return functions->GetSystemProperties(this, count_ptr, property_ptr);
+ }
+
+ jvmtiError GetSystemProperty(const char* property,
+ char** value_ptr) {
+ return functions->GetSystemProperty(this, property, value_ptr);
+ }
+
+ jvmtiError SetSystemProperty(const char* property,
+ const char* value) {
+ return functions->SetSystemProperty(this, property, value);
+ }
+
+ jvmtiError GetPhase(jvmtiPhase* phase_ptr) {
+ return functions->GetPhase(this, phase_ptr);
+ }
+
+ jvmtiError DisposeEnvironment() {
+ return functions->DisposeEnvironment(this);
+ }
+
+ jvmtiError SetEnvironmentLocalStorage(const void* data) {
+ return functions->SetEnvironmentLocalStorage(this, data);
+ }
+
+ jvmtiError GetEnvironmentLocalStorage(void** data_ptr) {
+ return functions->GetEnvironmentLocalStorage(this, data_ptr);
+ }
+
+ jvmtiError GetVersionNumber(jint* version_ptr) {
+ return functions->GetVersionNumber(this, version_ptr);
+ }
+
+ jvmtiError GetErrorName(jvmtiError error,
+ char** name_ptr) {
+ return functions->GetErrorName(this, error, name_ptr);
+ }
+
+ jvmtiError SetVerboseFlag(jvmtiVerboseFlag flag,
+ jboolean value) {
+ return functions->SetVerboseFlag(this, flag, value);
+ }
+
+ jvmtiError GetJLocationFormat(jvmtiJlocationFormat* format_ptr) {
+ return functions->GetJLocationFormat(this, format_ptr);
+ }
+
+#endif /* __cplusplus */
+};
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif /* __cplusplus */
+
+#endif /* !_JAVA_JVMTI_H_ */
+
diff --git a/cpp/wiiusej/include/win32/jawt_md.h b/cpp/wiiusej/include/win32/jawt_md.h
new file mode 100644
index 0000000..82ba034
--- /dev/null
+++ b/cpp/wiiusej/include/win32/jawt_md.h
@@ -0,0 +1,41 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+#ifndef _JAVASOFT_JAWT_MD_H_
+#define _JAVASOFT_JAWT_MD_H_
+
+#include
+#include "jawt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*
+ * Win32-specific declarations for AWT native interface.
+ * See notes in jawt.h for an example of use.
+ */
+typedef struct jawt_Win32DrawingSurfaceInfo {
+ /* Native window, DDB, or DIB handle */
+ union {
+ HWND hwnd;
+ HBITMAP hbitmap;
+ void* pbits;
+ };
+ /*
+ * This HDC should always be used instead of the HDC returned from
+ * BeginPaint() or any calls to GetDC().
+ */
+ HDC hdc;
+ HPALETTE hpalette;
+} JAWT_Win32DrawingSurfaceInfo;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !_JAVASOFT_JAWT_MD_H_ */
diff --git a/cpp/wiiusej/include/win32/jni_md.h b/cpp/wiiusej/include/win32/jni_md.h
new file mode 100644
index 0000000..9ac4718
--- /dev/null
+++ b/cpp/wiiusej/include/win32/jni_md.h
@@ -0,0 +1,19 @@
+/*
+ * %W% %E%
+ *
+ * Copyright (c) 2006, Oracle and/or its affiliates. All rights reserved.
+ * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
+ */
+
+#ifndef _JAVASOFT_JNI_MD_H_
+#define _JAVASOFT_JNI_MD_H_
+
+#define JNIEXPORT __declspec(dllexport)
+#define JNIIMPORT __declspec(dllimport)
+#define JNICALL __stdcall
+
+typedef long jint;
+typedef __int64 jlong;
+typedef signed char jbyte;
+
+#endif /* !_JAVASOFT_JNI_MD_H_ */
diff --git a/cpp/wiiusej/msvc/WiiUseJ.suo b/cpp/wiiusej/msvc/WiiUseJ.suo
index 550b6e1..4b26671 100644
Binary files a/cpp/wiiusej/msvc/WiiUseJ.suo and b/cpp/wiiusej/msvc/WiiUseJ.suo differ
diff --git a/cpp/wiiusej/msvc/WiiUseJ.vcxproj b/cpp/wiiusej/msvc/WiiUseJ.vcxproj
index c53d198..0837c54 100644
--- a/cpp/wiiusej/msvc/WiiUseJ.vcxproj
+++ b/cpp/wiiusej/msvc/WiiUseJ.vcxproj
@@ -44,7 +44,7 @@
false
- C:\Program Files (x86)\Java\jdk1.6.0_23\include;C:\Program Files (x86)\Java\jdk1.6.0_23\include\win32;$(IncludePath)
+ ..\include;..\include\win32;$(IncludePath)
.\Release\
$(ExtensionsToDeleteOnClean)
..\lib;$(LibraryPath)
diff --git a/java/native/WiiUseJ.dll b/java/native/WiiUseJ.dll
index abf3690..94eb17c 100644
Binary files a/java/native/WiiUseJ.dll and b/java/native/WiiUseJ.dll differ
diff --git a/java/src/org/apache/log4j/Appender.java b/java/src/org/apache/log4j/Appender.java
new file mode 100644
index 0000000..42ca4b8
--- /dev/null
+++ b/java/src/org/apache/log4j/Appender.java
@@ -0,0 +1,142 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ Implement this interface for your own strategies for outputting log
+ statements.
+
+ @author Ceki Gülcü
+*/
+public interface Appender {
+
+ /**
+ Add a filter to the end of the filter list.
+
+ @since 0.9.0
+ */
+ void addFilter(Filter newFilter);
+
+ /**
+ Returns the head Filter. The Filters are organized in a linked list
+ and so all Filters on this Appender are available through the result.
+
+ @return the head Filter or null, if no Filters are present
+ @since 1.1
+ */
+ public
+ Filter getFilter();
+
+ /**
+ Clear the list of filters by removing all the filters in it.
+
+ @since 0.9.0
+ */
+ public
+ void clearFilters();
+
+ /**
+ Release any resources allocated within the appender such as file
+ handles, network connections, etc.
+
+ It is a programming error to append to a closed appender.
+
+ @since 0.8.4
+ */
+ public
+ void close();
+
+ /**
+ Log in Appender specific way. When appropriate,
+ Loggers will call the doAppend method of appender
+ implementations in order to log. */
+ public
+ void doAppend(LoggingEvent event);
+
+
+ /**
+ Get the name of this appender.
+ @return name, may be null.*/
+ public
+ String getName();
+
+
+ /**
+ Set the {@link ErrorHandler} for this appender.
+
+ @since 0.9.0
+ */
+ public
+ void setErrorHandler(ErrorHandler errorHandler);
+
+ /**
+ Returns the {@link ErrorHandler} for this appender.
+
+ @since 1.1
+ */
+ public
+ ErrorHandler getErrorHandler();
+
+ /**
+ Set the {@link Layout} for this appender.
+
+ @since 0.8.1
+ */
+ public
+ void setLayout(Layout layout);
+
+ /**
+ Returns this appenders layout.
+
+ @since 1.1
+ */
+ public
+ Layout getLayout();
+
+
+ /**
+ Set the name of this appender. The name is used by other
+ components to identify this appender.
+
+ @since 0.8.1
+ */
+ public
+ void setName(String name);
+
+ /**
+ Configurators call this method to determine if the appender
+ requires a layout. If this method returns true,
+ meaning that layout is required, then the configurator will
+ configure an layout using the configuration information at its
+ disposal. If this method returns false, meaning that
+ a layout is not required, then layout configuration will be
+ skipped even if there is available layout configuration
+ information at the disposal of the configurator..
+
+
In the rather exceptional case, where the appender
+ implementation admits a layout but can also work without it, then
+ the appender should return true.
+
+ @since 0.8.4 */
+ public
+ boolean requiresLayout();
+}
diff --git a/java/src/org/apache/log4j/AppenderSkeleton.java b/java/src/org/apache/log4j/AppenderSkeleton.java
new file mode 100644
index 0000000..d4d2a5a
--- /dev/null
+++ b/java/src/org/apache/log4j/AppenderSkeleton.java
@@ -0,0 +1,304 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.helpers.OnlyOnceErrorHandler;
+import org.apache.log4j.helpers.LogLog;
+
+
+/**
+ * Abstract superclass of the other appenders in the package.
+ *
+ * This class provides the code for common functionality, such as
+ * support for threshold filtering and support for general filters.
+ *
+ * @since 0.8.1
+ * @author Ceki Gülcü
+ * */
+public abstract class AppenderSkeleton implements Appender, OptionHandler {
+
+ /** The layout variable does not need to be set if the appender
+ implementation has its own layout. */
+ protected Layout layout;
+
+ /** Appenders are named. */
+ protected String name;
+
+ /**
+ There is no level threshold filtering by default. */
+ protected Priority threshold;
+
+ /**
+ It is assumed and enforced that errorHandler is never null.
+ */
+ protected ErrorHandler errorHandler = new OnlyOnceErrorHandler();
+
+ /** The first filter in the filter chain. Set to null
+ initially. */
+ protected Filter headFilter;
+ /** The last filter in the filter chain. */
+ protected Filter tailFilter;
+
+ /**
+ Is this appender closed?
+ */
+ protected boolean closed = false;
+
+ /**
+ * Create new instance.
+ */
+ public AppenderSkeleton() {
+ super();
+ }
+
+ /**
+ * Create new instance.
+ * Provided for compatibility with log4j 1.3.
+ *
+ * @param isActive true if appender is ready for use upon construction.
+ * Not used in log4j 1.2.x.
+ * @since 1.2.15
+ */
+ protected AppenderSkeleton(final boolean isActive) {
+ super();
+ }
+
+
+
+ /**
+ Derived appenders should override this method if option structure
+ requires it. */
+ public
+ void activateOptions() {
+ }
+
+
+ /**
+ Add a filter to end of the filter list.
+
+ @since 0.9.0
+ */
+ public
+ void addFilter(Filter newFilter) {
+ if(headFilter == null) {
+ headFilter = tailFilter = newFilter;
+ } else {
+ tailFilter.setNext(newFilter);
+ tailFilter = newFilter;
+ }
+ }
+
+ /**
+ Subclasses of AppenderSkeleton should implement this
+ method to perform actual logging. See also {@link #doAppend
+ AppenderSkeleton.doAppend} method.
+
+ @since 0.9.0
+ */
+ abstract
+ protected
+ void append(LoggingEvent event);
+
+
+ /**
+ Clear the filters chain.
+
+ @since 0.9.0 */
+ public
+ void clearFilters() {
+ headFilter = tailFilter = null;
+ }
+
+ /**
+ Finalize this appender by calling the derived class'
+ close method.
+
+ @since 0.8.4 */
+ public
+ void finalize() {
+ // An appender might be closed then garbage collected. There is no
+ // point in closing twice.
+ if(this.closed)
+ return;
+
+ LogLog.debug("Finalizing appender named ["+name+"].");
+ close();
+ }
+
+
+ /**
+ Return the currently set {@link ErrorHandler} for this
+ Appender.
+
+ @since 0.9.0 */
+ public
+ ErrorHandler getErrorHandler() {
+ return this.errorHandler;
+ }
+
+
+ /**
+ Returns the head Filter.
+
+ @since 1.1
+ */
+ public
+ Filter getFilter() {
+ return headFilter;
+ }
+
+ /**
+ Return the first filter in the filter chain for this
+ Appender. The return value may be null if no is
+ filter is set.
+
+ */
+ public
+ final
+ Filter getFirstFilter() {
+ return headFilter;
+ }
+
+ /**
+ Returns the layout of this appender. The value may be null.
+ */
+ public
+ Layout getLayout() {
+ return layout;
+ }
+
+
+ /**
+ Returns the name of this appender.
+ @return name, may be null.
+ */
+ public
+ final
+ String getName() {
+ return this.name;
+ }
+
+ /**
+ Returns this appenders threshold level. See the {@link
+ #setThreshold} method for the meaning of this option.
+
+ @since 1.1 */
+ public
+ Priority getThreshold() {
+ return threshold;
+ }
+
+
+ /**
+ Check whether the message level is below the appender's
+ threshold. If there is no threshold set, then the return value is
+ always true.
+
+ */
+ public
+ boolean isAsSevereAsThreshold(Priority priority) {
+ return ((threshold == null) || priority.isGreaterOrEqual(threshold));
+ }
+
+
+ /**
+ * This method performs threshold checks and invokes filters before
+ * delegating actual logging to the subclasses specific {@link
+ * AppenderSkeleton#append} method.
+ * */
+ public
+ synchronized
+ void doAppend(LoggingEvent event) {
+ if(closed) {
+ LogLog.error("Attempted to append to closed appender named ["+name+"].");
+ return;
+ }
+
+ if(!isAsSevereAsThreshold(event.getLevel())) {
+ return;
+ }
+
+ Filter f = this.headFilter;
+
+ FILTER_LOOP:
+ while(f != null) {
+ switch(f.decide(event)) {
+ case Filter.DENY: return;
+ case Filter.ACCEPT: break FILTER_LOOP;
+ case Filter.NEUTRAL: f = f.getNext();
+ }
+ }
+
+ this.append(event);
+ }
+
+ /**
+ Set the {@link ErrorHandler} for this Appender.
+ @since 0.9.0
+ */
+ public
+ synchronized
+ void setErrorHandler(ErrorHandler eh) {
+ if(eh == null) {
+ // We do not throw exception here since the cause is probably a
+ // bad config file.
+ LogLog.warn("You have tried to set a null error-handler.");
+ } else {
+ this.errorHandler = eh;
+ }
+ }
+
+ /**
+ Set the layout for this appender. Note that some appenders have
+ their own (fixed) layouts or do not use one. For example, the
+ {@link org.apache.log4j.net.SocketAppender} ignores the layout set
+ here.
+ */
+ public
+ void setLayout(Layout layout) {
+ this.layout = layout;
+ }
+
+
+ /**
+ Set the name of this Appender.
+ */
+ public
+ void setName(String name) {
+ this.name = name;
+ }
+
+
+ /**
+ Set the threshold level. All log events with lower level
+ than the threshold level are ignored by the appender.
+
+
In configuration files this option is specified by setting the
+ value of the Threshold option to a level
+ string, such as "DEBUG", "INFO" and so on.
+
+ @since 0.8.3 */
+ public
+ void setThreshold(Priority threshold) {
+ this.threshold = threshold;
+ }
+}
diff --git a/java/src/org/apache/log4j/AsyncAppender.java b/java/src/org/apache/log4j/AsyncAppender.java
new file mode 100644
index 0000000..214ffa7
--- /dev/null
+++ b/java/src/org/apache/log4j/AsyncAppender.java
@@ -0,0 +1,596 @@
+/*
+ * 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.
+ */
+
+// Contibutors: Aaron Greenhouse
+// Thomas Tuft Muller
+package org.apache.log4j;
+
+import java.text.MessageFormat;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.log4j.helpers.AppenderAttachableImpl;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * The AsyncAppender lets users log events asynchronously.
+ *
+ *
+ * The AsyncAppender will collect the events sent to it and then dispatch them
+ * to all the appenders that are attached to it. You can attach multiple
+ * appenders to an AsyncAppender.
+ *
+ *
+ *
+ * The AsyncAppender uses a separate thread to serve the events in its buffer.
+ *
+ *
+ * Important note: The AsyncAppender can only be script
+ * configured using the {@link org.apache.log4j.xml.DOMConfigurator}.
+ *
+ *
+ * @author Ceki Gülcü
+ * @author Curt Arnold
+ * @since 0.9.1
+ */
+public class AsyncAppender extends AppenderSkeleton
+ implements AppenderAttachable {
+ /**
+ * The default buffer size is set to 128 events.
+ */
+ public static final int DEFAULT_BUFFER_SIZE = 128;
+
+ /**
+ * Event buffer, also used as monitor to protect itself and
+ * discardMap from simulatenous modifications.
+ */
+ private final List buffer = new ArrayList();
+
+ /**
+ * Map of DiscardSummary objects keyed by logger name.
+ */
+ private final Map discardMap = new HashMap();
+
+ /**
+ * Buffer size.
+ */
+ private int bufferSize = DEFAULT_BUFFER_SIZE;
+
+ /** Nested appenders. */
+ AppenderAttachableImpl aai;
+
+ /**
+ * Nested appenders.
+ */
+ private final AppenderAttachableImpl appenders;
+
+ /**
+ * Dispatcher.
+ */
+ private final Thread dispatcher;
+
+ /**
+ * Should location info be included in dispatched messages.
+ */
+ private boolean locationInfo = false;
+
+ /**
+ * Does appender block when buffer is full.
+ */
+ private boolean blocking = true;
+
+ /**
+ * Create new instance.
+ */
+ public AsyncAppender() {
+ appenders = new AppenderAttachableImpl();
+
+ //
+ // only set for compatibility
+ aai = appenders;
+
+ dispatcher =
+ new Thread(new Dispatcher(this, buffer, discardMap, appenders));
+
+ // It is the user's responsibility to close appenders before
+ // exiting.
+ dispatcher.setDaemon(true);
+
+ // set the dispatcher priority to lowest possible value
+ // dispatcher.setPriority(Thread.MIN_PRIORITY);
+ dispatcher.setName("AsyncAppender-Dispatcher-" + dispatcher.getName());
+ dispatcher.start();
+ }
+
+ /**
+ * Add appender.
+ *
+ * @param newAppender appender to add, may not be null.
+ */
+ public void addAppender(final Appender newAppender) {
+ synchronized (appenders) {
+ appenders.addAppender(newAppender);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void append(final LoggingEvent event) {
+ //
+ // if dispatcher thread has died then
+ // append subsequent events synchronously
+ // See bug 23021
+ if ((dispatcher == null) || !dispatcher.isAlive() || (bufferSize <= 0)) {
+ synchronized (appenders) {
+ appenders.appendLoopOnAppenders(event);
+ }
+
+ return;
+ }
+
+ // Set the NDC and thread name for the calling thread as these
+ // LoggingEvent fields were not set at event creation time.
+ event.getNDC();
+ event.getThreadName();
+ // Get a copy of this thread's MDC.
+ event.getMDCCopy();
+ if (locationInfo) {
+ event.getLocationInformation();
+ }
+ event.getRenderedMessage();
+ event.getThrowableStrRep();
+
+ synchronized (buffer) {
+ while (true) {
+ int previousSize = buffer.size();
+
+ if (previousSize < bufferSize) {
+ buffer.add(event);
+
+ //
+ // if buffer had been empty
+ // signal all threads waiting on buffer
+ // to check their conditions.
+ //
+ if (previousSize == 0) {
+ buffer.notifyAll();
+ }
+
+ break;
+ }
+
+ //
+ // Following code is only reachable if buffer is full
+ //
+ //
+ // if blocking and thread is not already interrupted
+ // and not the dispatcher then
+ // wait for a buffer notification
+ boolean discard = true;
+ if (blocking
+ && !Thread.interrupted()
+ && Thread.currentThread() != dispatcher) {
+ try {
+ buffer.wait();
+ discard = false;
+ } catch (InterruptedException e) {
+ //
+ // reset interrupt status so
+ // calling code can see interrupt on
+ // their next wait or sleep.
+ Thread.currentThread().interrupt();
+ }
+ }
+
+ //
+ // if blocking is false or thread has been interrupted
+ // add event to discard map.
+ //
+ if (discard) {
+ String loggerName = event.getLoggerName();
+ DiscardSummary summary = (DiscardSummary) discardMap.get(loggerName);
+
+ if (summary == null) {
+ summary = new DiscardSummary(event);
+ discardMap.put(loggerName, summary);
+ } else {
+ summary.add(event);
+ }
+
+ break;
+ }
+ }
+ }
+ }
+
+ /**
+ * Close this AsyncAppender by interrupting the dispatcher
+ * thread which will process all pending events before exiting.
+ */
+ public void close() {
+ /**
+ * Set closed flag and notify all threads to check their conditions.
+ * Should result in dispatcher terminating.
+ */
+ synchronized (buffer) {
+ closed = true;
+ buffer.notifyAll();
+ }
+
+ try {
+ dispatcher.join();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ org.apache.log4j.helpers.LogLog.error(
+ "Got an InterruptedException while waiting for the "
+ + "dispatcher to finish.", e);
+ }
+
+ //
+ // close all attached appenders.
+ //
+ synchronized (appenders) {
+ Enumeration iter = appenders.getAllAppenders();
+
+ if (iter != null) {
+ while (iter.hasMoreElements()) {
+ Object next = iter.nextElement();
+
+ if (next instanceof Appender) {
+ ((Appender) next).close();
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ * Get iterator over attached appenders.
+ * @return iterator or null if no attached appenders.
+ */
+ public Enumeration getAllAppenders() {
+ synchronized (appenders) {
+ return appenders.getAllAppenders();
+ }
+ }
+
+ /**
+ * Get appender by name.
+ *
+ * @param name name, may not be null.
+ * @return matching appender or null.
+ */
+ public Appender getAppender(final String name) {
+ synchronized (appenders) {
+ return appenders.getAppender(name);
+ }
+ }
+
+ /**
+ * Gets whether the location of the logging request call
+ * should be captured.
+ *
+ * @return the current value of the LocationInfo option.
+ */
+ public boolean getLocationInfo() {
+ return locationInfo;
+ }
+
+ /**
+ * Determines if specified appender is attached.
+ * @param appender appender.
+ * @return true if attached.
+ */
+ public boolean isAttached(final Appender appender) {
+ synchronized (appenders) {
+ return appenders.isAttached(appender);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ /**
+ * Removes and closes all attached appenders.
+ */
+ public void removeAllAppenders() {
+ synchronized (appenders) {
+ appenders.removeAllAppenders();
+ }
+ }
+
+ /**
+ * Removes an appender.
+ * @param appender appender to remove.
+ */
+ public void removeAppender(final Appender appender) {
+ synchronized (appenders) {
+ appenders.removeAppender(appender);
+ }
+ }
+
+ /**
+ * Remove appender by name.
+ * @param name name.
+ */
+ public void removeAppender(final String name) {
+ synchronized (appenders) {
+ appenders.removeAppender(name);
+ }
+ }
+
+ /**
+ * The LocationInfo option takes a boolean value. By default, it is
+ * set to false which means there will be no effort to extract the location
+ * information related to the event. As a result, the event that will be
+ * ultimately logged will likely to contain the wrong location information
+ * (if present in the log format).
+ *
+ *
+ * Location information extraction is comparatively very slow and should be
+ * avoided unless performance is not a concern.
+ *
+ * @param flag true if location information should be extracted.
+ */
+ public void setLocationInfo(final boolean flag) {
+ locationInfo = flag;
+ }
+
+ /**
+ * Sets the number of messages allowed in the event buffer
+ * before the calling thread is blocked (if blocking is true)
+ * or until messages are summarized and discarded. Changing
+ * the size will not affect messages already in the buffer.
+ *
+ * @param size buffer size, must be positive.
+ */
+ public void setBufferSize(final int size) {
+ //
+ // log4j 1.2 would throw exception if size was negative
+ // and deadlock if size was zero.
+ //
+ if (size < 0) {
+ throw new java.lang.NegativeArraySizeException("size");
+ }
+
+ synchronized (buffer) {
+ //
+ // don't let size be zero.
+ //
+ bufferSize = (size < 1) ? 1 : size;
+ buffer.notifyAll();
+ }
+ }
+
+ /**
+ * Gets the current buffer size.
+ * @return the current value of the BufferSize option.
+ */
+ public int getBufferSize() {
+ return bufferSize;
+ }
+
+ /**
+ * Sets whether appender should wait if there is no
+ * space available in the event buffer or immediately return.
+ *
+ * @since 1.2.14
+ * @param value true if appender should wait until available space in buffer.
+ */
+ public void setBlocking(final boolean value) {
+ synchronized (buffer) {
+ blocking = value;
+ buffer.notifyAll();
+ }
+ }
+
+ /**
+ * Gets whether appender should block calling thread when buffer is full.
+ * If false, messages will be counted by logger and a summary
+ * message appended after the contents of the buffer have been appended.
+ *
+ * @since 1.2.14
+ * @return true if calling thread will be blocked when buffer is full.
+ */
+ public boolean getBlocking() {
+ return blocking;
+ }
+
+ /**
+ * Summary of discarded logging events for a logger.
+ */
+ private static final class DiscardSummary {
+ /**
+ * First event of the highest severity.
+ */
+ private LoggingEvent maxEvent;
+
+ /**
+ * Total count of messages discarded.
+ */
+ private int count;
+
+ /**
+ * Create new instance.
+ *
+ * @param event event, may not be null.
+ */
+ public DiscardSummary(final LoggingEvent event) {
+ maxEvent = event;
+ count = 1;
+ }
+
+ /**
+ * Add discarded event to summary.
+ *
+ * @param event event, may not be null.
+ */
+ public void add(final LoggingEvent event) {
+ if (event.getLevel().toInt() > maxEvent.getLevel().toInt()) {
+ maxEvent = event;
+ }
+
+ count++;
+ }
+
+ /**
+ * Create event with summary information.
+ *
+ * @return new event.
+ */
+ public LoggingEvent createEvent() {
+ String msg =
+ MessageFormat.format(
+ "Discarded {0} messages due to full event buffer including: {1}",
+ new Object[] { new Integer(count), maxEvent.getMessage() });
+
+ return new LoggingEvent(
+ "org.apache.log4j.AsyncAppender.DONT_REPORT_LOCATION",
+ Logger.getLogger(maxEvent.getLoggerName()),
+ maxEvent.getLevel(),
+ msg,
+ null);
+ }
+ }
+
+ /**
+ * Event dispatcher.
+ */
+ private static class Dispatcher implements Runnable {
+ /**
+ * Parent AsyncAppender.
+ */
+ private final AsyncAppender parent;
+
+ /**
+ * Event buffer.
+ */
+ private final List buffer;
+
+ /**
+ * Map of DiscardSummary keyed by logger name.
+ */
+ private final Map discardMap;
+
+ /**
+ * Wrapped appenders.
+ */
+ private final AppenderAttachableImpl appenders;
+
+ /**
+ * Create new instance of dispatcher.
+ *
+ * @param parent parent AsyncAppender, may not be null.
+ * @param buffer event buffer, may not be null.
+ * @param discardMap discard map, may not be null.
+ * @param appenders appenders, may not be null.
+ */
+ public Dispatcher(
+ final AsyncAppender parent, final List buffer, final Map discardMap,
+ final AppenderAttachableImpl appenders) {
+
+ this.parent = parent;
+ this.buffer = buffer;
+ this.appenders = appenders;
+ this.discardMap = discardMap;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void run() {
+ boolean isActive = true;
+
+ //
+ // if interrupted (unlikely), end thread
+ //
+ try {
+ //
+ // loop until the AsyncAppender is closed.
+ //
+ while (isActive) {
+ LoggingEvent[] events = null;
+
+ //
+ // extract pending events while synchronized
+ // on buffer
+ //
+ synchronized (buffer) {
+ int bufferSize = buffer.size();
+ isActive = !parent.closed;
+
+ while ((bufferSize == 0) && isActive) {
+ buffer.wait();
+ bufferSize = buffer.size();
+ isActive = !parent.closed;
+ }
+
+ if (bufferSize > 0) {
+ events = new LoggingEvent[bufferSize + discardMap.size()];
+ buffer.toArray(events);
+
+ //
+ // add events due to buffer overflow
+ //
+ int index = bufferSize;
+
+ for (
+ Iterator iter = discardMap.values().iterator();
+ iter.hasNext();) {
+ events[index++] = ((DiscardSummary) iter.next()).createEvent();
+ }
+
+ //
+ // clear buffer and discard map
+ //
+ buffer.clear();
+ discardMap.clear();
+
+ //
+ // allow blocked appends to continue
+ buffer.notifyAll();
+ }
+ }
+
+ //
+ // process events after lock on buffer is released.
+ //
+ if (events != null) {
+ for (int i = 0; i < events.length; i++) {
+ synchronized (appenders) {
+ appenders.appendLoopOnAppenders(events[i]);
+ }
+ }
+ }
+ }
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/BasicConfigurator.java b/java/src/org/apache/log4j/BasicConfigurator.java
new file mode 100644
index 0000000..2d859cf
--- /dev/null
+++ b/java/src/org/apache/log4j/BasicConfigurator.java
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+// Contibutors: "Luke Blanshard"
+// "Mark DONSZELMANN"
+// "Muly Oved"
+
+package org.apache.log4j;
+
+
+/**
+ Use this class to quickly configure the package.
+
+ For file based configuration see {@link
+ PropertyConfigurator}. For XML based configuration see {@link
+ org.apache.log4j.xml.DOMConfigurator DOMConfigurator}.
+
+ @since 0.8.1
+ @author Ceki Gülcü */
+public class BasicConfigurator {
+
+ protected BasicConfigurator() {
+ }
+
+ /**
+ Add a {@link ConsoleAppender} that uses {@link PatternLayout}
+ using the {@link PatternLayout#TTCC_CONVERSION_PATTERN} and
+ prints to System.out to the root category. */
+ static
+ public
+ void configure() {
+ Logger root = Logger.getRootLogger();
+ root.addAppender(new ConsoleAppender(
+ new PatternLayout(PatternLayout.TTCC_CONVERSION_PATTERN)));
+ }
+
+ /**
+ Add appender to the root category.
+ @param appender The appender to add to the root category.
+ */
+ static
+ public
+ void configure(Appender appender) {
+ Logger root = Logger.getRootLogger();
+ root.addAppender(appender);
+ }
+
+ /**
+ Reset the default hierarchy to its defaut. It is equivalent to
+ calling
+ Category.getDefaultHierarchy().resetConfiguration().
+
+ See {@link Hierarchy#resetConfiguration()} for more details. */
+ public
+ static
+ void resetConfiguration() {
+ LogManager.resetConfiguration();
+ }
+}
diff --git a/java/src/org/apache/log4j/Category.java b/java/src/org/apache/log4j/Category.java
new file mode 100644
index 0000000..e0cb561
--- /dev/null
+++ b/java/src/org/apache/log4j/Category.java
@@ -0,0 +1,1062 @@
+/*
+ * 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.
+ */
+
+// Contibutors: Alex Blewitt
+// Markus Oestreicher
+// Frank Hoering
+// Nelson Minar
+// Jim Cakalic
+// Avy Sharell
+// Ciaran Treanor
+// Jeff Turner
+// Michael Horwitz
+// Calvin Chan
+// Aaron Greenhouse
+// Beat Meier
+// Colin Sampaleanu
+
+package org.apache.log4j;
+
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.HierarchyEventListener;
+import org.apache.log4j.helpers.NullEnumeration;
+import org.apache.log4j.helpers.AppenderAttachableImpl;
+
+import java.util.Enumeration;
+import java.util.MissingResourceException;
+import java.util.ResourceBundle;
+import java.util.Vector;
+
+
+/**
+ * This class has been deprecated and
+ * replaced by the {@link Logger} subclass. It
+ * will be kept around to preserve backward compatibility until mid
+ * 2003.
+ *
+ * Logger is a subclass of Category, i.e. it extends
+ * Category. In other words, a logger is a category. Thus,
+ * all operations that can be performed on a category can be
+ * performed on a logger. Internally, whenever log4j is asked to
+ * produce a Category object, it will instead produce a Logger
+ * object. Log4j 1.2 will never produce Category objects but
+ * only Logger instances. In order to preserve backward
+ * compatibility, methods that previously accepted category objects
+ * still continue to accept category objects.
+ *
+ *
For example, the following are all legal and will work as
+ * expected.
+ *
+
+ // Deprecated form:
+ Category cat = Category.getInstance("foo.bar")
+
+ // Preferred form for retrieving loggers:
+ Logger logger = Logger.getLogger("foo.bar")
+
+
+ * The first form is deprecated and should be avoided.
+ *
+ *
There is absolutely no need for new client code to use or
+ * refer to the Category class. Whenever possible,
+ * please avoid referring to it or using it.
+ *
+ *
See the short manual for an
+ * introduction on this class.
+ *
+ * See the document entitled preparing
+ * for log4j 1.3 for a more detailed discussion.
+ *
+ * @author Ceki Gülcü
+ * @author Anders Kristensen
+ */
+public class Category implements AppenderAttachable {
+
+ /**
+ The hierarchy where categories are attached to by default.
+ */
+ //static
+ //public
+ //final Hierarchy defaultHierarchy = new Hierarchy(new
+ // RootCategory(Level.DEBUG));
+
+ /**
+ The name of this category.
+ */
+ protected String name;
+
+ /**
+ The assigned level of this category. The
+ level variable need not be assigned a value in
+ which case it is inherited form the hierarchy. */
+ volatile protected Level level;
+
+ /**
+ The parent of this category. All categories have at least one
+ ancestor which is the root category. */
+ volatile protected Category parent;
+
+ /**
+ The fully qualified name of the Category class. See also the
+ getFQCN method. */
+ private static final String FQCN = Category.class.getName();
+
+ protected ResourceBundle resourceBundle;
+
+ // Categories need to know what Hierarchy they are in
+ protected LoggerRepository repository;
+
+
+ AppenderAttachableImpl aai;
+
+ /** Additivity is set to true by default, that is children inherit
+ the appenders of their ancestors by default. If this variable is
+ set to false then the appenders found in the
+ ancestors of this category are not used. However, the children
+ of this category will inherit its appenders, unless the children
+ have their additivity flag set to false too. See
+ the user manual for more details. */
+ protected boolean additive = true;
+
+ /**
+ This constructor created a new Category instance and
+ sets its name.
+
+
It is intended to be used by sub-classes only. You should not
+ create categories directly.
+
+ @param name The name of the category.
+ */
+ protected
+ Category(String name) {
+ this.name = name;
+ }
+
+ /**
+ Add newAppender to the list of appenders of this
+ Category instance.
+
+
If newAppender is already in the list of
+ appenders, then it won't be added again.
+ */
+ synchronized
+ public
+ void addAppender(Appender newAppender) {
+ if(aai == null) {
+ aai = new AppenderAttachableImpl();
+ }
+ aai.addAppender(newAppender);
+ repository.fireAddAppenderEvent(this, newAppender);
+ }
+
+ /**
+ If assertion parameter is false, then
+ logs msg as an {@link #error(Object) error} statement.
+
+
The assert method has been renamed to
+ assertLog because assert is a language
+ reserved word in JDK 1.4.
+
+ @param assertion
+ @param msg The message to print if assertion is
+ false.
+
+ @since 1.2 */
+ public
+ void assertLog(boolean assertion, String msg) {
+ if(!assertion)
+ this.error(msg);
+ }
+
+
+ /**
+ Call the appenders in the hierrachy starting at
+ this. If no appenders could be found, emit a
+ warning.
+
+
This method calls all the appenders inherited from the
+ hierarchy circumventing any evaluation of whether to log or not
+ to log the particular log request.
+
+ @param event the event to log. */
+ public
+ void callAppenders(LoggingEvent event) {
+ int writes = 0;
+
+ for(Category c = this; c != null; c=c.parent) {
+ // Protected against simultaneous call to addAppender, removeAppender,...
+ synchronized(c) {
+ if(c.aai != null) {
+ writes += c.aai.appendLoopOnAppenders(event);
+ }
+ if(!c.additive) {
+ break;
+ }
+ }
+ }
+
+ if(writes == 0) {
+ repository.emitNoAppenderWarning(this);
+ }
+ }
+
+ /**
+ Close all attached appenders implementing the AppenderAttachable
+ interface.
+ @since 1.0
+ */
+ synchronized
+ void closeNestedAppenders() {
+ Enumeration enumeration = this.getAllAppenders();
+ if(enumeration != null) {
+ while(enumeration.hasMoreElements()) {
+ Appender a = (Appender) enumeration.nextElement();
+ if(a instanceof AppenderAttachable) {
+ a.close();
+ }
+ }
+ }
+ }
+
+ /**
+ Log a message object with the {@link Level#DEBUG DEBUG} level.
+
+
This method first checks if this category is DEBUG
+ enabled by comparing the level of this category with the {@link
+ Level#DEBUG DEBUG} level. If this category is
+ DEBUG enabled, then it converts the message object
+ (passed as parameter) to a string by invoking the appropriate
+ {@link org.apache.log4j.or.ObjectRenderer}. It then proceeds to call all the
+ registered appenders in this category and also higher in the
+ hierarchy depending on the value of the additivity flag.
+
+
WARNING Note that passing a {@link Throwable} to this
+ method will print the name of the Throwable but no
+ stack trace. To print a stack trace use the {@link #debug(Object,
+ Throwable)} form instead.
+
+ @param message the message object to log. */
+ public
+ void debug(Object message) {
+ if(repository.isDisabled(Level.DEBUG_INT))
+ return;
+ if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) {
+ forcedLog(FQCN, Level.DEBUG, message, null);
+ }
+ }
+
+
+ /**
+ Log a message object with the DEBUG level including
+ the stack trace of the {@link Throwable} t passed as
+ parameter.
+
+
See {@link #debug(Object)} form for more detailed information.
+
+ @param message the message object to log.
+ @param t the exception to log, including its stack trace. */
+ public
+ void debug(Object message, Throwable t) {
+ if(repository.isDisabled(Level.DEBUG_INT))
+ return;
+ if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, Level.DEBUG, message, t);
+ }
+
+ /**
+ Log a message object with the {@link Level#ERROR ERROR} Level.
+
+
This method first checks if this category is ERROR
+ enabled by comparing the level of this category with {@link
+ Level#ERROR ERROR} Level. If this category is ERROR
+ enabled, then it converts the message object passed as parameter
+ to a string by invoking the appropriate {@link
+ org.apache.log4j.or.ObjectRenderer}. It proceeds to call all the
+ registered appenders in this category and also higher in the
+ hierarchy depending on the value of the additivity flag.
+
+
WARNING Note that passing a {@link Throwable} to this
+ method will print the name of the Throwable but no
+ stack trace. To print a stack trace use the {@link #error(Object,
+ Throwable)} form instead.
+
+ @param message the message object to log */
+ public
+ void error(Object message) {
+ if(repository.isDisabled(Level.ERROR_INT))
+ return;
+ if(Level.ERROR.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, Level.ERROR, message, null);
+ }
+
+ /**
+ Log a message object with the ERROR level including
+ the stack trace of the {@link Throwable} t passed as
+ parameter.
+
+
See {@link #error(Object)} form for more detailed information.
+
+ @param message the message object to log.
+ @param t the exception to log, including its stack trace. */
+ public
+ void error(Object message, Throwable t) {
+ if(repository.isDisabled(Level.ERROR_INT))
+ return;
+ if(Level.ERROR.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, Level.ERROR, message, t);
+
+ }
+
+
+ /**
+ If the named category exists (in the default hierarchy) then it
+ returns a reference to the category, otherwise it returns
+ null.
+
+ @deprecated Please use {@link LogManager#exists} instead.
+
+ @since 0.8.5 */
+ public
+ static
+ Logger exists(String name) {
+ return LogManager.exists(name);
+ }
+
+ /**
+ Log a message object with the {@link Level#FATAL FATAL} Level.
+
+
This method first checks if this category is FATAL
+ enabled by comparing the level of this category with {@link
+ Level#FATAL FATAL} Level. If the category is FATAL
+ enabled, then it converts the message object passed as parameter
+ to a string by invoking the appropriate
+ {@link org.apache.log4j.or.ObjectRenderer}. It
+ proceeds to call all the registered appenders in this category and
+ also higher in the hierarchy depending on the value of the
+ additivity flag.
+
+
WARNING Note that passing a {@link Throwable} to this
+ method will print the name of the Throwable but no stack trace. To
+ print a stack trace use the {@link #fatal(Object, Throwable)} form
+ instead.
+
+ @param message the message object to log */
+ public
+ void fatal(Object message) {
+ if(repository.isDisabled(Level.FATAL_INT))
+ return;
+ if(Level.FATAL.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, Level.FATAL, message, null);
+ }
+
+ /**
+ Log a message object with the FATAL level including
+ the stack trace of the {@link Throwable} t passed as
+ parameter.
+
+
See {@link #fatal(Object)} for more detailed information.
+
+ @param message the message object to log.
+ @param t the exception to log, including its stack trace. */
+ public
+ void fatal(Object message, Throwable t) {
+ if(repository.isDisabled(Level.FATAL_INT))
+ return;
+ if(Level.FATAL.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, Level.FATAL, message, t);
+ }
+
+
+ /**
+ This method creates a new logging event and logs the event
+ without further checks. */
+ protected
+ void forcedLog(String fqcn, Priority level, Object message, Throwable t) {
+ callAppenders(new LoggingEvent(fqcn, this, level, message, t));
+ }
+
+
+ /**
+ Get the additivity flag for this Category instance.
+ */
+ public
+ boolean getAdditivity() {
+ return additive;
+ }
+
+ /**
+ Get the appenders contained in this category as an {@link
+ Enumeration}. If no appenders can be found, then a {@link NullEnumeration}
+ is returned.
+
+ @return Enumeration An enumeration of the appenders in this category. */
+ synchronized
+ public
+ Enumeration getAllAppenders() {
+ if(aai == null)
+ return NullEnumeration.getInstance();
+ else
+ return aai.getAllAppenders();
+ }
+
+ /**
+ Look for the appender named as name.
+
+
Return the appender with that name if in the list. Return
+ null otherwise. */
+ synchronized
+ public
+ Appender getAppender(String name) {
+ if(aai == null || name == null)
+ return null;
+
+ return aai.getAppender(name);
+ }
+
+ /**
+ Starting from this category, search the category hierarchy for a
+ non-null level and return it. Otherwise, return the level of the
+ root category.
+
+
The Category class is designed so that this method executes as
+ quickly as possible.
+ */
+ public
+ Level getEffectiveLevel() {
+ for(Category c = this; c != null; c=c.parent) {
+ if(c.level != null)
+ return c.level;
+ }
+ return null; // If reached will cause an NullPointerException.
+ }
+
+ /**
+ *
+ * @deprecated Please use the the {@link #getEffectiveLevel} method
+ * instead.
+ * */
+ public
+ Priority getChainedPriority() {
+ for(Category c = this; c != null; c=c.parent) {
+ if(c.level != null)
+ return c.level;
+ }
+ return null; // If reached will cause an NullPointerException.
+ }
+
+
+ /**
+ Returns all the currently defined categories in the default
+ hierarchy as an {@link java.util.Enumeration Enumeration}.
+
+
The root category is not included in the returned
+ {@link Enumeration}.
+
+ @deprecated Please use {@link LogManager#getCurrentLoggers()} instead.
+ */
+ public
+ static
+ Enumeration getCurrentCategories() {
+ return LogManager.getCurrentLoggers();
+ }
+
+
+ /**
+ Return the default Hierarchy instance.
+
+ @deprecated Please use {@link LogManager#getLoggerRepository()} instead.
+
+ @since 1.0
+ */
+ public
+ static
+ LoggerRepository getDefaultHierarchy() {
+ return LogManager.getLoggerRepository();
+ }
+
+ /**
+ Return the the {@link Hierarchy} where this Category
+ instance is attached.
+
+ @deprecated Please use {@link #getLoggerRepository} instead.
+
+ @since 1.1 */
+ public
+ LoggerRepository getHierarchy() {
+ return repository;
+ }
+
+ /**
+ Return the the {@link LoggerRepository} where this
+ Category is attached.
+
+ @since 1.2 */
+ public
+ LoggerRepository getLoggerRepository() {
+ return repository;
+ }
+
+
+ /**
+ * @deprecated Make sure to use {@link Logger#getLogger(String)} instead.
+ */
+ public
+ static
+ Category getInstance(String name) {
+ return LogManager.getLogger(name);
+ }
+
+ /**
+ * @deprecated Please make sure to use {@link Logger#getLogger(Class)} instead.
+ */
+ public
+ static
+ Category getInstance(Class clazz) {
+ return LogManager.getLogger(clazz);
+ }
+
+
+ /**
+ Return the category name. */
+ public
+ final
+ String getName() {
+ return name;
+ }
+
+
+ /**
+ Returns the parent of this category. Note that the parent of a
+ given category may change during the lifetime of the category.
+
+
The root category will return null.
+
+ @since 1.2
+ */
+ final
+ public
+ Category getParent() {
+ return this.parent;
+ }
+
+
+ /**
+ Returns the assigned {@link Level}, if any, for this Category.
+
+ @return Level - the assigned Level, can be null.
+ */
+ final
+ public
+ Level getLevel() {
+ return this.level;
+ }
+
+ /**
+ @deprecated Please use {@link #getLevel} instead.
+ */
+ final
+ public
+ Level getPriority() {
+ return this.level;
+ }
+
+
+ /**
+ * @deprecated Please use {@link Logger#getRootLogger()} instead.
+ */
+ final
+ public
+ static
+ Category getRoot() {
+ return LogManager.getRootLogger();
+ }
+
+ /**
+ Return the inherited {@link ResourceBundle} for this
+ category.
+
+
This method walks the hierarchy to find the appropriate
+ resource bundle. It will return the resource bundle attached to
+ the closest ancestor of this category, much like the way
+ priorities are searched. In case there is no bundle in the
+ hierarchy then null is returned.
+
+ @since 0.9.0 */
+ public
+ ResourceBundle getResourceBundle() {
+ for(Category c = this; c != null; c=c.parent) {
+ if(c.resourceBundle != null)
+ return c.resourceBundle;
+ }
+ // It might be the case that there is no resource bundle
+ return null;
+ }
+
+ /**
+ Returns the string resource coresponding to key in
+ this category's inherited resource bundle. See also {@link
+ #getResourceBundle}.
+
+
If the resource cannot be found, then an {@link #error error}
+ message will be logged complaining about the missing resource.
+ */
+ protected
+ String getResourceBundleString(String key) {
+ ResourceBundle rb = getResourceBundle();
+ // This is one of the rare cases where we can use logging in order
+ // to report errors from within log4j.
+ if(rb == null) {
+ //if(!hierarchy.emittedNoResourceBundleWarning) {
+ //error("No resource bundle has been set for category "+name);
+ //hierarchy.emittedNoResourceBundleWarning = true;
+ //}
+ return null;
+ }
+ else {
+ try {
+ return rb.getString(key);
+ }
+ catch(MissingResourceException mre) {
+ error("No resource is associated with key \""+key+"\".");
+ return null;
+ }
+ }
+ }
+
+ /**
+ Log a message object with the {@link Level#INFO INFO} Level.
+
+
This method first checks if this category is INFO
+ enabled by comparing the level of this category with {@link
+ Level#INFO INFO} Level. If the category is INFO
+ enabled, then it converts the message object passed as parameter
+ to a string by invoking the appropriate
+ {@link org.apache.log4j.or.ObjectRenderer}. It
+ proceeds to call all the registered appenders in this category and
+ also higher in the hierarchy depending on the value of the
+ additivity flag.
+
+
WARNING Note that passing a {@link Throwable} to this
+ method will print the name of the Throwable but no stack trace. To
+ print a stack trace use the {@link #info(Object, Throwable)} form
+ instead.
+
+ @param message the message object to log */
+ public
+ void info(Object message) {
+ if(repository.isDisabled(Level.INFO_INT))
+ return;
+ if(Level.INFO.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, Level.INFO, message, null);
+ }
+
+ /**
+ Log a message object with the INFO level including
+ the stack trace of the {@link Throwable} t passed as
+ parameter.
+
+
See {@link #info(Object)} for more detailed information.
+
+ @param message the message object to log.
+ @param t the exception to log, including its stack trace. */
+ public
+ void info(Object message, Throwable t) {
+ if(repository.isDisabled(Level.INFO_INT))
+ return;
+ if(Level.INFO.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, Level.INFO, message, t);
+ }
+
+ /**
+ Is the appender passed as parameter attached to this category?
+ */
+ public
+ boolean isAttached(Appender appender) {
+ if(appender == null || aai == null)
+ return false;
+ else {
+ return aai.isAttached(appender);
+ }
+ }
+
+ /**
+ * Check whether this category is enabled for the DEBUG
+ * Level.
+ *
+ *
This function is intended to lessen the computational cost of
+ * disabled log debug statements.
+ *
+ *
For some cat Category object, when you write,
+ *
+ * cat.debug("This is entry number: " + i );
+ *
+ *
+ * You incur the cost constructing the message, concatenatiion in
+ * this case, regardless of whether the message is logged or not.
+ *
+ *
If you are worried about speed, then you should write
+ *
+ * if(cat.isDebugEnabled()) {
+ * cat.debug("This is entry number: " + i );
+ * }
+ *
+ *
+ * This way you will not incur the cost of parameter
+ * construction if debugging is disabled for cat. On
+ * the other hand, if the cat is debug enabled, you
+ * will incur the cost of evaluating whether the category is debug
+ * enabled twice. Once in isDebugEnabled and once in
+ * the debug. This is an insignificant overhead
+ * since evaluating a category takes about 1%% of the time it
+ * takes to actually log.
+ *
+ * @return boolean - true if this category is debug
+ * enabled, false otherwise.
+ * */
+ public
+ boolean isDebugEnabled() {
+ if(repository.isDisabled( Level.DEBUG_INT))
+ return false;
+ return Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel());
+ }
+
+ /**
+ Check whether this category is enabled for a given {@link
+ Level} passed as parameter.
+
+ See also {@link #isDebugEnabled}.
+
+ @return boolean True if this category is enabled for level.
+ */
+ public
+ boolean isEnabledFor(Priority level) {
+ if(repository.isDisabled(level.level))
+ return false;
+ return level.isGreaterOrEqual(this.getEffectiveLevel());
+ }
+
+ /**
+ Check whether this category is enabled for the info Level.
+ See also {@link #isDebugEnabled}.
+
+ @return boolean - true if this category is enabled
+ for level info, false otherwise.
+ */
+ public
+ boolean isInfoEnabled() {
+ if(repository.isDisabled(Level.INFO_INT))
+ return false;
+ return Level.INFO.isGreaterOrEqual(this.getEffectiveLevel());
+ }
+
+
+ /**
+ Log a localized message. The user supplied parameter
+ key is replaced by its localized version from the
+ resource bundle.
+
+ @see #setResourceBundle
+
+ @since 0.8.4 */
+ public
+ void l7dlog(Priority priority, String key, Throwable t) {
+ if(repository.isDisabled(priority.level)) {
+ return;
+ }
+ if(priority.isGreaterOrEqual(this.getEffectiveLevel())) {
+ String msg = getResourceBundleString(key);
+ // if message corresponding to 'key' could not be found in the
+ // resource bundle, then default to 'key'.
+ if(msg == null) {
+ msg = key;
+ }
+ forcedLog(FQCN, priority, msg, t);
+ }
+ }
+ /**
+ Log a localized and parameterized message. First, the user
+ supplied key is searched in the resource
+ bundle. Next, the resulting pattern is formatted using
+ {@link java.text.MessageFormat#format(String,Object[])} method with the
+ user supplied object array params.
+
+ @since 0.8.4
+ */
+ public
+ void l7dlog(Priority priority, String key, Object[] params, Throwable t) {
+ if(repository.isDisabled(priority.level)) {
+ return;
+ }
+ if(priority.isGreaterOrEqual(this.getEffectiveLevel())) {
+ String pattern = getResourceBundleString(key);
+ String msg;
+ if(pattern == null)
+ msg = key;
+ else
+ msg = java.text.MessageFormat.format(pattern, params);
+ forcedLog(FQCN, priority, msg, t);
+ }
+ }
+
+ /**
+ This generic form is intended to be used by wrappers.
+ */
+ public
+ void log(Priority priority, Object message, Throwable t) {
+ if(repository.isDisabled(priority.level)) {
+ return;
+ }
+ if(priority.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, priority, message, t);
+ }
+
+ /**
+ This generic form is intended to be used by wrappers.
+ */
+ public
+ void log(Priority priority, Object message) {
+ if(repository.isDisabled(priority.level)) {
+ return;
+ }
+ if(priority.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, priority, message, null);
+ }
+
+ /**
+
+ This is the most generic printing method. It is intended to be
+ invoked by wrapper classes.
+
+ @param callerFQCN The wrapper class' fully qualified class name.
+ @param level The level of the logging request.
+ @param message The message of the logging request.
+ @param t The throwable of the logging request, may be null. */
+ public
+ void log(String callerFQCN, Priority level, Object message, Throwable t) {
+ if(repository.isDisabled(level.level)) {
+ return;
+ }
+ if(level.isGreaterOrEqual(this.getEffectiveLevel())) {
+ forcedLog(callerFQCN, level, message, t);
+ }
+ }
+
+ /**
+ * LoggerRepository forgot the fireRemoveAppenderEvent method,
+ * if using the stock Hierarchy implementation, then call its fireRemove.
+ * Custom repositories can implement HierarchyEventListener if they
+ * want remove notifications.
+ * @param appender appender, may be null.
+ */
+ private void fireRemoveAppenderEvent(final Appender appender) {
+ if (appender != null) {
+ if (repository instanceof Hierarchy) {
+ ((Hierarchy) repository).fireRemoveAppenderEvent(this, appender);
+ } else if (repository instanceof HierarchyEventListener) {
+ ((HierarchyEventListener) repository).removeAppenderEvent(this, appender);
+ }
+ }
+ }
+
+ /**
+ Remove all previously added appenders from this Category
+ instance.
+
+
This is useful when re-reading configuration information.
+ */
+ synchronized
+ public
+ void removeAllAppenders() {
+ if(aai != null) {
+ Vector appenders = new Vector();
+ for (Enumeration iter = aai.getAllAppenders(); iter != null && iter.hasMoreElements();) {
+ appenders.add(iter.nextElement());
+ }
+ aai.removeAllAppenders();
+ for(Enumeration iter = appenders.elements(); iter.hasMoreElements();) {
+ fireRemoveAppenderEvent((Appender) iter.nextElement());
+ }
+ aai = null;
+ }
+ }
+
+
+ /**
+ Remove the appender passed as parameter form the list of appenders.
+
+ @since 0.8.2
+ */
+ synchronized
+ public
+ void removeAppender(Appender appender) {
+ if(appender == null || aai == null)
+ return;
+ boolean wasAttached = aai.isAttached(appender);
+ aai.removeAppender(appender);
+ if (wasAttached) {
+ fireRemoveAppenderEvent(appender);
+ }
+ }
+
+ /**
+ Remove the appender with the name passed as parameter form the
+ list of appenders.
+
+ @since 0.8.2 */
+ synchronized
+ public
+ void removeAppender(String name) {
+ if(name == null || aai == null) return;
+ Appender appender = aai.getAppender(name);
+ aai.removeAppender(name);
+ if (appender != null) {
+ fireRemoveAppenderEvent(appender);
+ }
+ }
+
+ /**
+ Set the additivity flag for this Category instance.
+ @since 0.8.1
+ */
+ public
+ void setAdditivity(boolean additive) {
+ this.additive = additive;
+ }
+
+ /**
+ Only the Hiearchy class can set the hiearchy of a
+ category. Default package access is MANDATORY here. */
+ final
+ void setHierarchy(LoggerRepository repository) {
+ this.repository = repository;
+ }
+
+ /**
+ Set the level of this Category. If you are passing any of
+ Level.DEBUG, Level.INFO,
+ Level.WARN, Level.ERROR,
+ Level.FATAL as a parameter, you need to case them as
+ Level.
+
+
As in
logger.setLevel((Level) Level.DEBUG);
+
+
+ Null values are admitted. */
+ public
+ void setLevel(Level level) {
+ this.level = level;
+ }
+
+
+ /**
+ Set the level of this Category.
+
+
Null values are admitted.
+
+ @deprecated Please use {@link #setLevel} instead.
+ */
+ public
+ void setPriority(Priority priority) {
+ this.level = (Level) priority;
+ }
+
+
+ /**
+ Set the resource bundle to be used with localized logging
+ methods {@link #l7dlog(Priority,String,Throwable)} and {@link
+ #l7dlog(Priority,String,Object[],Throwable)}.
+
+ @since 0.8.4
+ */
+ public
+ void setResourceBundle(ResourceBundle bundle) {
+ resourceBundle = bundle;
+ }
+
+ /**
+ Calling this method will safely close and remove all
+ appenders in all the categories including root contained in the
+ default hierachy.
+
+
Some appenders such as {@link org.apache.log4j.net.SocketAppender}
+ and {@link AsyncAppender} need to be closed before the
+ application exists. Otherwise, pending logging events might be
+ lost.
+
+
The shutdown method is careful to close nested
+ appenders before closing regular appenders. This is allows
+ configurations where a regular appender is attached to a category
+ and again to a nested appender.
+
+ @deprecated Please use {@link LogManager#shutdown()} instead.
+
+ @since 1.0
+ */
+ public
+ static
+ void shutdown() {
+ LogManager.shutdown();
+ }
+
+
+ /**
+ Log a message object with the {@link Level#WARN WARN} Level.
+
+
This method first checks if this category is WARN
+ enabled by comparing the level of this category with {@link
+ Level#WARN WARN} Level. If the category is WARN
+ enabled, then it converts the message object passed as parameter
+ to a string by invoking the appropriate
+ {@link org.apache.log4j.or.ObjectRenderer}. It
+ proceeds to call all the registered appenders in this category and
+ also higher in the hieararchy depending on the value of the
+ additivity flag.
+
+
WARNING Note that passing a {@link Throwable} to this
+ method will print the name of the Throwable but no stack trace. To
+ print a stack trace use the {@link #warn(Object, Throwable)} form
+ instead.
+
+ @param message the message object to log. */
+ public
+ void warn(Object message) {
+ if(repository.isDisabled( Level.WARN_INT))
+ return;
+
+ if(Level.WARN.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, Level.WARN, message, null);
+ }
+
+ /**
+ Log a message with the WARN level including the
+ stack trace of the {@link Throwable} t passed as
+ parameter.
+
+
See {@link #warn(Object)} for more detailed information.
+
+ @param message the message object to log.
+ @param t the exception to log, including its stack trace. */
+ public
+ void warn(Object message, Throwable t) {
+ if(repository.isDisabled(Level.WARN_INT))
+ return;
+ if(Level.WARN.isGreaterOrEqual(this.getEffectiveLevel()))
+ forcedLog(FQCN, Level.WARN, message, t);
+ }
+}
diff --git a/java/src/org/apache/log4j/CategoryKey.java b/java/src/org/apache/log4j/CategoryKey.java
new file mode 100644
index 0000000..fe0fff7
--- /dev/null
+++ b/java/src/org/apache/log4j/CategoryKey.java
@@ -0,0 +1,52 @@
+/*
+ * 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.log4j;
+
+/**
+ CategoryKey is a wrapper for String that apparently accellerated
+ hash table lookup in early JVM's.
+ @author Ceki Gülcü
+*/
+class CategoryKey {
+
+ String name;
+ int hashCache;
+
+ CategoryKey(String name) {
+ this.name = name;
+ hashCache = name.hashCode();
+ }
+
+ final
+ public
+ int hashCode() {
+ return hashCache;
+ }
+
+ final
+ public
+ boolean equals(Object rArg) {
+ if(this == rArg)
+ return true;
+
+ if(rArg != null && CategoryKey.class == rArg.getClass())
+ return name.equals(((CategoryKey)rArg ).name);
+ else
+ return false;
+ }
+}
diff --git a/java/src/org/apache/log4j/ConsoleAppender.java b/java/src/org/apache/log4j/ConsoleAppender.java
new file mode 100644
index 0000000..b44fd5f
--- /dev/null
+++ b/java/src/org/apache/log4j/ConsoleAppender.java
@@ -0,0 +1,220 @@
+/*
+ * 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.log4j;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import org.apache.log4j.helpers.LogLog;
+
+/**
+ * ConsoleAppender appends log events to System.out or
+ * System.err using a layout specified by the user. The
+ * default target is System.out.
+ *
+ * @author Ceki Gülcü
+ * @author Curt Arnold
+ * @since 1.1 */
+public class ConsoleAppender extends WriterAppender {
+
+ public static final String SYSTEM_OUT = "System.out";
+ public static final String SYSTEM_ERR = "System.err";
+
+ protected String target = SYSTEM_OUT;
+
+ /**
+ * Determines if the appender honors reassignments of System.out
+ * or System.err made after configuration.
+ */
+ private boolean follow = false;
+
+ /**
+ * Constructs an unconfigured appender.
+ */
+ public ConsoleAppender() {
+ }
+
+ /**
+ * Creates a configured appender.
+ *
+ * @param layout layout, may not be null.
+ */
+ public ConsoleAppender(Layout layout) {
+ this(layout, SYSTEM_OUT);
+ }
+
+ /**
+ * Creates a configured appender.
+ * @param layout layout, may not be null.
+ * @param target target, either "System.err" or "System.out".
+ */
+ public ConsoleAppender(Layout layout, String target) {
+ setLayout(layout);
+ setTarget(target);
+ activateOptions();
+ }
+
+ /**
+ * Sets the value of the Target option. Recognized values
+ * are "System.out" and "System.err". Any other value will be
+ * ignored.
+ * */
+ public
+ void setTarget(String value) {
+ String v = value.trim();
+
+ if (SYSTEM_OUT.equalsIgnoreCase(v)) {
+ target = SYSTEM_OUT;
+ } else if (SYSTEM_ERR.equalsIgnoreCase(v)) {
+ target = SYSTEM_ERR;
+ } else {
+ targetWarn(value);
+ }
+ }
+
+ /**
+ * Returns the current value of the Target property. The
+ * default value of the option is "System.out".
+ *
+ * See also {@link #setTarget}.
+ * */
+ public
+ String getTarget() {
+ return target;
+ }
+
+ /**
+ * Sets whether the appender honors reassignments of System.out
+ * or System.err made after configuration.
+ * @param newValue if true, appender will use value of System.out or
+ * System.err in force at the time when logging events are appended.
+ * @since 1.2.13
+ */
+ public final void setFollow(final boolean newValue) {
+ follow = newValue;
+ }
+
+ /**
+ * Gets whether the appender honors reassignments of System.out
+ * or System.err made after configuration.
+ * @return true if appender will use value of System.out or
+ * System.err in force at the time when logging events are appended.
+ * @since 1.2.13
+ */
+ public final boolean getFollow() {
+ return follow;
+ }
+
+ void targetWarn(String val) {
+ LogLog.warn("["+val+"] should be System.out or System.err.");
+ LogLog.warn("Using previously set target, System.out by default.");
+ }
+
+ /**
+ * Prepares the appender for use.
+ */
+ public void activateOptions() {
+ if (follow) {
+ if (target.equals(SYSTEM_ERR)) {
+ setWriter(createWriter(new SystemErrStream()));
+ } else {
+ setWriter(createWriter(new SystemOutStream()));
+ }
+ } else {
+ if (target.equals(SYSTEM_ERR)) {
+ setWriter(createWriter(System.err));
+ } else {
+ setWriter(createWriter(System.out));
+ }
+ }
+
+ super.activateOptions();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected
+ final
+ void closeWriter() {
+ if (follow) {
+ super.closeWriter();
+ }
+ }
+
+
+ /**
+ * An implementation of OutputStream that redirects to the
+ * current System.err.
+ *
+ */
+ private static class SystemErrStream extends OutputStream {
+ public SystemErrStream() {
+ }
+
+ public void close() {
+ }
+
+ public void flush() {
+ System.err.flush();
+ }
+
+ public void write(final byte[] b) throws IOException {
+ System.err.write(b);
+ }
+
+ public void write(final byte[] b, final int off, final int len)
+ throws IOException {
+ System.err.write(b, off, len);
+ }
+
+ public void write(final int b) throws IOException {
+ System.err.write(b);
+ }
+ }
+
+ /**
+ * An implementation of OutputStream that redirects to the
+ * current System.out.
+ *
+ */
+ private static class SystemOutStream extends OutputStream {
+ public SystemOutStream() {
+ }
+
+ public void close() {
+ }
+
+ public void flush() {
+ System.out.flush();
+ }
+
+ public void write(final byte[] b) throws IOException {
+ System.out.write(b);
+ }
+
+ public void write(final byte[] b, final int off, final int len)
+ throws IOException {
+ System.out.write(b, off, len);
+ }
+
+ public void write(final int b) throws IOException {
+ System.out.write(b);
+ }
+ }
+
+}
diff --git a/java/src/org/apache/log4j/DailyRollingFileAppender.java b/java/src/org/apache/log4j/DailyRollingFileAppender.java
new file mode 100644
index 0000000..79a3b5c
--- /dev/null
+++ b/java/src/org/apache/log4j/DailyRollingFileAppender.java
@@ -0,0 +1,454 @@
+/*
+ * 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.log4j;
+
+import java.io.IOException;
+import java.io.File;
+import java.io.InterruptedIOException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.util.Locale;
+
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ DailyRollingFileAppender extends {@link FileAppender} so that the
+ underlying file is rolled over at a user chosen frequency.
+
+ DailyRollingFileAppender has been observed to exhibit
+ synchronization issues and data loss. The log4j extras
+ companion includes alternatives which should be considered
+ for new deployments and which are discussed in the documentation
+ for org.apache.log4j.rolling.RollingFileAppender.
+
+
The rolling schedule is specified by the DatePattern
+ option. This pattern should follow the {@link SimpleDateFormat}
+ conventions. In particular, you must escape literal text
+ within a pair of single quotes. A formatted version of the date
+ pattern is used as the suffix for the rolled file name.
+
+
For example, if the File option is set to
+ /foo/bar.log and the DatePattern set to
+ '.'yyyy-MM-dd, on 2001-02-16 at midnight, the logging
+ file /foo/bar.log will be copied to
+ /foo/bar.log.2001-02-16 and logging for 2001-02-17
+ will continue in /foo/bar.log until it rolls over
+ the next day.
+
+
Is is possible to specify monthly, weekly, half-daily, daily,
+ hourly, or minutely rollover schedules.
+
+
+
+ | DatePattern |
+ Rollover schedule |
+ Example |
+
+
+ '.'yyyy-MM
+ | Rollover at the beginning of each month |
+
+ At midnight of May 31st, 2002 /foo/bar.log will be
+ copied to /foo/bar.log.2002-05. Logging for the month
+ of June will be output to /foo/bar.log until it is
+ also rolled over the next month.
+
+ |
+ '.'yyyy-ww
+
+ | Rollover at the first day of each week. The first day of the
+ week depends on the locale. |
+
+ Assuming the first day of the week is Sunday, on Saturday
+ midnight, June 9th 2002, the file /foo/bar.log will be
+ copied to /foo/bar.log.2002-23. Logging for the 24th week
+ of 2002 will be output to /foo/bar.log until it is
+ rolled over the next week.
+
+ |
+ '.'yyyy-MM-dd
+
+ | Rollover at midnight each day. |
+
+ At midnight, on March 8th, 2002, /foo/bar.log will
+ be copied to /foo/bar.log.2002-03-08. Logging for the
+ 9th day of March will be output to /foo/bar.log until
+ it is rolled over the next day.
+
+ |
+ '.'yyyy-MM-dd-a
+
+ | Rollover at midnight and midday of each day. |
+
+ At noon, on March 9th, 2002, /foo/bar.log will be
+ copied to /foo/bar.log.2002-03-09-AM. Logging for the
+ afternoon of the 9th will be output to /foo/bar.log
+ until it is rolled over at midnight.
+
+ |
+ '.'yyyy-MM-dd-HH
+
+ | Rollover at the top of every hour. |
+
+ At approximately 11:00.000 o'clock on March 9th, 2002,
+ /foo/bar.log will be copied to
+ /foo/bar.log.2002-03-09-10. Logging for the 11th hour
+ of the 9th of March will be output to /foo/bar.log
+ until it is rolled over at the beginning of the next hour.
+
+
+ |
+ '.'yyyy-MM-dd-HH-mm
+
+ | Rollover at the beginning of every minute. |
+
+ At approximately 11:23,000, on March 9th, 2001,
+ /foo/bar.log will be copied to
+ /foo/bar.log.2001-03-09-10-22. Logging for the minute
+ of 11:23 (9th of March) will be output to
+ /foo/bar.log until it is rolled over the next minute.
+
+ |
+
+ Do not use the colon ":" character in anywhere in the
+ DatePattern option. The text before the colon is interpeted
+ as the protocol specificaion of a URL which is probably not what
+ you want.
+
+
+ @author Eirik Lygre
+ @author Ceki Gülcü*/
+public class DailyRollingFileAppender extends FileAppender {
+
+
+ // The code assumes that the following constants are in a increasing
+ // sequence.
+ static final int TOP_OF_TROUBLE=-1;
+ static final int TOP_OF_MINUTE = 0;
+ static final int TOP_OF_HOUR = 1;
+ static final int HALF_DAY = 2;
+ static final int TOP_OF_DAY = 3;
+ static final int TOP_OF_WEEK = 4;
+ static final int TOP_OF_MONTH = 5;
+
+
+ /**
+ The date pattern. By default, the pattern is set to
+ "'.'yyyy-MM-dd" meaning daily rollover.
+ */
+ private String datePattern = "'.'yyyy-MM-dd";
+
+ /**
+ The log file will be renamed to the value of the
+ scheduledFilename variable when the next interval is entered. For
+ example, if the rollover period is one hour, the log file will be
+ renamed to the value of "scheduledFilename" at the beginning of
+ the next hour.
+
+ The precise time when a rollover occurs depends on logging
+ activity.
+ */
+ private String scheduledFilename;
+
+ /**
+ The next time we estimate a rollover should occur. */
+ private long nextCheck = System.currentTimeMillis () - 1;
+
+ Date now = new Date();
+
+ SimpleDateFormat sdf;
+
+ RollingCalendar rc = new RollingCalendar();
+
+ int checkPeriod = TOP_OF_TROUBLE;
+
+ // The gmtTimeZone is used only in computeCheckPeriod() method.
+ static final TimeZone gmtTimeZone = TimeZone.getTimeZone("GMT");
+
+
+ /**
+ The default constructor does nothing. */
+ public DailyRollingFileAppender() {
+ }
+
+ /**
+ Instantiate a DailyRollingFileAppender and open the
+ file designated by filename. The opened filename will
+ become the ouput destination for this appender.
+
+ */
+ public DailyRollingFileAppender (Layout layout, String filename,
+ String datePattern) throws IOException {
+ super(layout, filename, true);
+ this.datePattern = datePattern;
+ activateOptions();
+ }
+
+ /**
+ The DatePattern takes a string in the same format as
+ expected by {@link SimpleDateFormat}. This options determines the
+ rollover schedule.
+ */
+ public void setDatePattern(String pattern) {
+ datePattern = pattern;
+ }
+
+ /** Returns the value of the DatePattern option. */
+ public String getDatePattern() {
+ return datePattern;
+ }
+
+ public void activateOptions() {
+ super.activateOptions();
+ if(datePattern != null && fileName != null) {
+ now.setTime(System.currentTimeMillis());
+ sdf = new SimpleDateFormat(datePattern);
+ int type = computeCheckPeriod();
+ printPeriodicity(type);
+ rc.setType(type);
+ File file = new File(fileName);
+ scheduledFilename = fileName+sdf.format(new Date(file.lastModified()));
+
+ } else {
+ LogLog.error("Either File or DatePattern options are not set for appender ["
+ +name+"].");
+ }
+ }
+
+ void printPeriodicity(int type) {
+ switch(type) {
+ case TOP_OF_MINUTE:
+ LogLog.debug("Appender ["+name+"] to be rolled every minute.");
+ break;
+ case TOP_OF_HOUR:
+ LogLog.debug("Appender ["+name
+ +"] to be rolled on top of every hour.");
+ break;
+ case HALF_DAY:
+ LogLog.debug("Appender ["+name
+ +"] to be rolled at midday and midnight.");
+ break;
+ case TOP_OF_DAY:
+ LogLog.debug("Appender ["+name
+ +"] to be rolled at midnight.");
+ break;
+ case TOP_OF_WEEK:
+ LogLog.debug("Appender ["+name
+ +"] to be rolled at start of week.");
+ break;
+ case TOP_OF_MONTH:
+ LogLog.debug("Appender ["+name
+ +"] to be rolled at start of every month.");
+ break;
+ default:
+ LogLog.warn("Unknown periodicity for appender ["+name+"].");
+ }
+ }
+
+
+ // This method computes the roll over period by looping over the
+ // periods, starting with the shortest, and stopping when the r0 is
+ // different from from r1, where r0 is the epoch formatted according
+ // the datePattern (supplied by the user) and r1 is the
+ // epoch+nextMillis(i) formatted according to datePattern. All date
+ // formatting is done in GMT and not local format because the test
+ // logic is based on comparisons relative to 1970-01-01 00:00:00
+ // GMT (the epoch).
+
+ int computeCheckPeriod() {
+ RollingCalendar rollingCalendar = new RollingCalendar(gmtTimeZone, Locale.getDefault());
+ // set sate to 1970-01-01 00:00:00 GMT
+ Date epoch = new Date(0);
+ if(datePattern != null) {
+ for(int i = TOP_OF_MINUTE; i <= TOP_OF_MONTH; i++) {
+ SimpleDateFormat simpleDateFormat = new SimpleDateFormat(datePattern);
+ simpleDateFormat.setTimeZone(gmtTimeZone); // do all date formatting in GMT
+ String r0 = simpleDateFormat.format(epoch);
+ rollingCalendar.setType(i);
+ Date next = new Date(rollingCalendar.getNextCheckMillis(epoch));
+ String r1 = simpleDateFormat.format(next);
+ //System.out.println("Type = "+i+", r0 = "+r0+", r1 = "+r1);
+ if(r0 != null && r1 != null && !r0.equals(r1)) {
+ return i;
+ }
+ }
+ }
+ return TOP_OF_TROUBLE; // Deliberately head for trouble...
+ }
+
+ /**
+ Rollover the current file to a new file.
+ */
+ void rollOver() throws IOException {
+
+ /* Compute filename, but only if datePattern is specified */
+ if (datePattern == null) {
+ errorHandler.error("Missing DatePattern option in rollOver().");
+ return;
+ }
+
+ String datedFilename = fileName+sdf.format(now);
+ // It is too early to roll over because we are still within the
+ // bounds of the current interval. Rollover will occur once the
+ // next interval is reached.
+ if (scheduledFilename.equals(datedFilename)) {
+ return;
+ }
+
+ // close current file, and rename it to datedFilename
+ this.closeFile();
+
+ File target = new File(scheduledFilename);
+ if (target.exists()) {
+ target.delete();
+ }
+
+ File file = new File(fileName);
+ boolean result = file.renameTo(target);
+ if(result) {
+ LogLog.debug(fileName +" -> "+ scheduledFilename);
+ } else {
+ LogLog.error("Failed to rename ["+fileName+"] to ["+scheduledFilename+"].");
+ }
+
+ try {
+ // This will also close the file. This is OK since multiple
+ // close operations are safe.
+ this.setFile(fileName, true, this.bufferedIO, this.bufferSize);
+ }
+ catch(IOException e) {
+ errorHandler.error("setFile("+fileName+", true) call failed.");
+ }
+ scheduledFilename = datedFilename;
+ }
+
+ /**
+ * This method differentiates DailyRollingFileAppender from its
+ * super class.
+ *
+ *
Before actually logging, this method will check whether it is
+ * time to do a rollover. If it is, it will schedule the next
+ * rollover time and then rollover.
+ * */
+ protected void subAppend(LoggingEvent event) {
+ long n = System.currentTimeMillis();
+ if (n >= nextCheck) {
+ now.setTime(n);
+ nextCheck = rc.getNextCheckMillis(now);
+ try {
+ rollOver();
+ }
+ catch(IOException ioe) {
+ if (ioe instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("rollOver() failed.", ioe);
+ }
+ }
+ super.subAppend(event);
+ }
+}
+
+/**
+ * RollingCalendar is a helper class to DailyRollingFileAppender.
+ * Given a periodicity type and the current time, it computes the
+ * start of the next interval.
+ * */
+class RollingCalendar extends GregorianCalendar {
+ private static final long serialVersionUID = -3560331770601814177L;
+
+ int type = DailyRollingFileAppender.TOP_OF_TROUBLE;
+
+ RollingCalendar() {
+ super();
+ }
+
+ RollingCalendar(TimeZone tz, Locale locale) {
+ super(tz, locale);
+ }
+
+ void setType(int type) {
+ this.type = type;
+ }
+
+ public long getNextCheckMillis(Date now) {
+ return getNextCheckDate(now).getTime();
+ }
+
+ public Date getNextCheckDate(Date now) {
+ this.setTime(now);
+
+ switch(type) {
+ case DailyRollingFileAppender.TOP_OF_MINUTE:
+ this.set(Calendar.SECOND, 0);
+ this.set(Calendar.MILLISECOND, 0);
+ this.add(Calendar.MINUTE, 1);
+ break;
+ case DailyRollingFileAppender.TOP_OF_HOUR:
+ this.set(Calendar.MINUTE, 0);
+ this.set(Calendar.SECOND, 0);
+ this.set(Calendar.MILLISECOND, 0);
+ this.add(Calendar.HOUR_OF_DAY, 1);
+ break;
+ case DailyRollingFileAppender.HALF_DAY:
+ this.set(Calendar.MINUTE, 0);
+ this.set(Calendar.SECOND, 0);
+ this.set(Calendar.MILLISECOND, 0);
+ int hour = get(Calendar.HOUR_OF_DAY);
+ if(hour < 12) {
+ this.set(Calendar.HOUR_OF_DAY, 12);
+ } else {
+ this.set(Calendar.HOUR_OF_DAY, 0);
+ this.add(Calendar.DAY_OF_MONTH, 1);
+ }
+ break;
+ case DailyRollingFileAppender.TOP_OF_DAY:
+ this.set(Calendar.HOUR_OF_DAY, 0);
+ this.set(Calendar.MINUTE, 0);
+ this.set(Calendar.SECOND, 0);
+ this.set(Calendar.MILLISECOND, 0);
+ this.add(Calendar.DATE, 1);
+ break;
+ case DailyRollingFileAppender.TOP_OF_WEEK:
+ this.set(Calendar.DAY_OF_WEEK, getFirstDayOfWeek());
+ this.set(Calendar.HOUR_OF_DAY, 0);
+ this.set(Calendar.MINUTE, 0);
+ this.set(Calendar.SECOND, 0);
+ this.set(Calendar.MILLISECOND, 0);
+ this.add(Calendar.WEEK_OF_YEAR, 1);
+ break;
+ case DailyRollingFileAppender.TOP_OF_MONTH:
+ this.set(Calendar.DATE, 1);
+ this.set(Calendar.HOUR_OF_DAY, 0);
+ this.set(Calendar.MINUTE, 0);
+ this.set(Calendar.SECOND, 0);
+ this.set(Calendar.MILLISECOND, 0);
+ this.add(Calendar.MONTH, 1);
+ break;
+ default:
+ throw new IllegalStateException("Unknown periodicity type.");
+ }
+ return getTime();
+ }
+}
diff --git a/java/src/org/apache/log4j/DefaultCategoryFactory.java b/java/src/org/apache/log4j/DefaultCategoryFactory.java
new file mode 100644
index 0000000..c7bb0c4
--- /dev/null
+++ b/java/src/org/apache/log4j/DefaultCategoryFactory.java
@@ -0,0 +1,31 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggerFactory;
+
+class DefaultCategoryFactory implements LoggerFactory {
+
+ DefaultCategoryFactory() {
+ }
+
+ public
+ Logger makeNewLoggerInstance(String name) {
+ return new Logger(name);
+ }
+}
diff --git a/java/src/org/apache/log4j/DefaultThrowableRenderer.java b/java/src/org/apache/log4j/DefaultThrowableRenderer.java
new file mode 100644
index 0000000..29bfe06
--- /dev/null
+++ b/java/src/org/apache/log4j/DefaultThrowableRenderer.java
@@ -0,0 +1,83 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.ThrowableRenderer;
+
+import java.io.StringWriter;
+import java.io.PrintWriter;
+import java.io.LineNumberReader;
+import java.io.StringReader;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.util.ArrayList;
+
+/**
+ * Default implementation of ThrowableRenderer using
+ * Throwable.printStackTrace.
+ *
+ * @since 1.2.16
+ */
+public final class DefaultThrowableRenderer implements ThrowableRenderer {
+ /**
+ * Construct new instance.
+ */
+ public DefaultThrowableRenderer() {
+
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] doRender(final Throwable throwable) {
+ return render(throwable);
+ }
+
+ /**
+ * Render throwable using Throwable.printStackTrace.
+ * @param throwable throwable, may not be null.
+ * @return string representation.
+ */
+ public static String[] render(final Throwable throwable) {
+ StringWriter sw = new StringWriter();
+ PrintWriter pw = new PrintWriter(sw);
+ try {
+ throwable.printStackTrace(pw);
+ } catch(RuntimeException ex) {
+ }
+ pw.flush();
+ LineNumberReader reader = new LineNumberReader(
+ new StringReader(sw.toString()));
+ ArrayList lines = new ArrayList();
+ try {
+ String line = reader.readLine();
+ while(line != null) {
+ lines.add(line);
+ line = reader.readLine();
+ }
+ } catch(IOException ex) {
+ if (ex instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ lines.add(ex.toString());
+ }
+ String[] tempRep = new String[lines.size()];
+ lines.toArray(tempRep);
+ return tempRep;
+ }
+}
diff --git a/java/src/org/apache/log4j/Dispatcher.java b/java/src/org/apache/log4j/Dispatcher.java
new file mode 100644
index 0000000..e879ff0
--- /dev/null
+++ b/java/src/org/apache/log4j/Dispatcher.java
@@ -0,0 +1,125 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.helpers.AppenderAttachableImpl;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Obsolete AsyncAppender dispatcher provided for compatibility only.
+ *
+ * @deprecated Since 1.3.
+ */
+class Dispatcher extends Thread {
+ /**
+ * @deprecated
+ */
+ private org.apache.log4j.helpers.BoundedFIFO bf;
+ private AppenderAttachableImpl aai;
+ private boolean interrupted = false;
+ AsyncAppender container;
+
+ /**
+ *
+ * @param bf
+ * @param container
+ * @deprecated
+ */
+ Dispatcher(org.apache.log4j.helpers.BoundedFIFO bf, AsyncAppender container) {
+ this.bf = bf;
+ this.container = container;
+ this.aai = container.aai;
+
+ // It is the user's responsibility to close appenders before
+ // exiting.
+ this.setDaemon(true);
+
+ // set the dispatcher priority to lowest possible value
+ this.setPriority(Thread.MIN_PRIORITY);
+ this.setName("Dispatcher-" + getName());
+
+ // set the dispatcher priority to MIN_PRIORITY plus or minus 2
+ // depending on the direction of MIN to MAX_PRIORITY.
+ //+ (Thread.MAX_PRIORITY > Thread.MIN_PRIORITY ? 1 : -1)*2);
+ }
+
+ void close() {
+ synchronized (bf) {
+ interrupted = true;
+
+ // We have a waiting dispacther if and only if bf.length is
+ // zero. In that case, we need to give it a death kiss.
+ if (bf.length() == 0) {
+ bf.notify();
+ }
+ }
+ }
+
+ /**
+ * The dispatching strategy is to wait until there are events in the buffer
+ * to process. After having processed an event, we release the monitor
+ * (variable bf) so that new events can be placed in the buffer, instead of
+ * keeping the monitor and processing the remaining events in the buffer.
+ *
+ *
+ * Other approaches might yield better results.
+ *
+ */
+ public void run() {
+ //Category cat = Category.getInstance(Dispatcher.class.getName());
+ LoggingEvent event;
+
+ while (true) {
+ synchronized (bf) {
+ if (bf.length() == 0) {
+ // Exit loop if interrupted but only if the the buffer is empty.
+ if (interrupted) {
+ //cat.info("Exiting.");
+ break;
+ }
+
+ try {
+ //LogLog.debug("Waiting for new event to dispatch.");
+ bf.wait();
+ } catch (InterruptedException e) {
+ break;
+ }
+ }
+
+ event = bf.get();
+
+ if (bf.wasFull()) {
+ //LogLog.debug("Notifying AsyncAppender about freed space.");
+ bf.notify();
+ }
+ }
+
+ // synchronized
+ synchronized (container.aai) {
+ if ((aai != null) && (event != null)) {
+ aai.appendLoopOnAppenders(event);
+ }
+ }
+ }
+
+ // while
+ // close and remove all appenders
+ aai.removeAllAppenders();
+ }
+}
diff --git a/java/src/org/apache/log4j/EnhancedPatternLayout.java b/java/src/org/apache/log4j/EnhancedPatternLayout.java
new file mode 100644
index 0000000..926053a
--- /dev/null
+++ b/java/src/org/apache/log4j/EnhancedPatternLayout.java
@@ -0,0 +1,559 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.helpers.PatternConverter;
+import org.apache.log4j.pattern.BridgePatternConverter;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+// Contributors: Nelson Minar
+// Anders Kristensen
+
+/**
+ * This class is an enhanced version of org.apache.log4j.PatternLayout
+ * which was originally developed as part of the abandoned log4j 1.3
+ * effort and has been available in the extras companion.
+ * This pattern layout should be used in preference to
+ * org.apache.log4j.PatternLayout except when compatibility
+ * where PatternLayout has been extended either through subclassing
+ * or alternative pattern parsers.
+ *
+ *
+ * A flexible layout configurable with pattern string. The goal of this class
+ * is to {@link #format format} a {@link LoggingEvent} and return the results
+ * in a {@link StringBuffer}. The format of the result depends on the
+ * conversion pattern.
+ *
+ *
+ *
The conversion pattern is closely related to the conversion
+ * pattern of the printf function in C. A conversion pattern is
+ * composed of literal text and format control expressions called
+ * conversion specifiers.
+ *
+ *
Note that you are free to insert any literal text within the
+ * conversion pattern.
+ *
+
+ Each conversion specifier starts with a percent sign (%) and is
+ followed by optional format modifiers and a conversion
+ character. The conversion character specifies the type of
+ data, e.g. category, priority, date, thread name. The format
+ modifiers control such things as field width, padding, left and
+ right justification. The following is a simple example.
+
+
Let the conversion pattern be "%-5p [%t]: %m%n" and assume
+ that the log4j environment was set to use a EnhancedPatternLayout. Then the
+ statements
+
+ Category root = Category.getRoot();
+ root.debug("Message 1");
+ root.warn("Message 2");
+
+ would yield the output
+
+ DEBUG [main]: Message 1
+ WARN [main]: Message 2
+
+
+ Note that there is no explicit separator between text and
+ conversion specifiers. The pattern parser knows when it has reached
+ the end of a conversion specifier when it reads a conversion
+ character. In the example above the conversion specifier
+ %-5p means the priority of the logging event should be left
+ justified to a width of five characters.
+
+ The recognized conversion characters are
+
+
+
+ | Conversion Character |
+ Effect |
+
+
+ | c |
+
+ Used to output the category of the logging event. The
+ category conversion specifier can be optionally followed by
+ NameAbbreviator pattern.
+
+ For example, for the category name "alpha.beta.gamma" the pattern
+ %c{2} will output the last two elements ("beta.gamma"),
+ %c{-2} will remove two elements leaving "gamma",
+ %c{1.} will output "a.b.gamma".
+
+ |
+
+
+
+ | C |
+
+ Used to output the fully qualified class name of the caller
+ issuing the logging request. This conversion specifier
+ can be optionally followed by precision specifier, that
+ is a decimal constant in brackets.
+
+ | Used to output the category of the logging event. The
+ category conversion specifier can be optionally followed by
+ NameAbbreviator pattern.
+
+ For example, for the category name "alpha.beta.gamma" the pattern
+ %c{2} will output the last two elements ("beta.gamma"),
+ %c{-2} will remove two elements leaving "gamma",
+ %c{1.} will output "a.b.gamma".
+
+ WARNING Generating the caller class information is
+ slow. Thus, its use should be avoided unless execution speed is
+ not an issue.
+
+ |
+
+
+ | d | Used to output the date of
+ the logging event. The date conversion specifier may be
+ followed by a set of braces containing a
+ date and time pattern strings {@link java.text.SimpleDateFormat},
+ ABSOLUTE, DATE or ISO8601
+ and a set of braces containing a time zone id per
+ {@link java.util.TimeZone#getTimeZone(String)}.
+ For example, %d{HH:mm:ss,SSS},
+ %d{dd MMM yyyy HH:mm:ss,SSS},
+ %d{DATE} or %d{HH:mm:ss}{GMT+0}. If no date format specifier is given then
+ ISO8601 format is assumed.
+ |
+
+
+
+ | F |
+
+ Used to output the file name where the logging request was
+ issued.
+
+ WARNING Generating caller location information is
+ extremely slow and should be avoided unless execution speed
+ is not an issue.
+
+ |
+
+
+ | l |
+
+ Used to output location information of the caller which generated
+ the logging event.
+
+ The location information depends on the JVM implementation but
+ usually consists of the fully qualified name of the calling
+ method followed by the callers source the file name and line
+ number between parentheses.
+
+ The location information can be very useful. However, its
+ generation is extremely slow and should be avoided
+ unless execution speed is not an issue.
+
+ |
+
+
+
+ | L |
+
+ Used to output the line number from where the logging request
+ was issued.
+
+ WARNING Generating caller location information is
+ extremely slow and should be avoided unless execution speed
+ is not an issue.
+
+ |
+
+
+
+ | m |
+ Used to output the application supplied message associated with
+ the logging event. |
+
+
+
+ | M |
+
+ Used to output the method name where the logging request was
+ issued.
+
+ WARNING Generating caller location information is
+ extremely slow and should be avoided unless execution speed
+ is not an issue.
+
+ |
+
+
+ | n |
+
+ Outputs the platform dependent line separator character or
+ characters.
+
+ This conversion character offers practically the same
+ performance as using non-portable line separator strings such as
+ "\n", or "\r\n". Thus, it is the preferred way of specifying a
+ line separator.
+
+
+ |
+
+
+ | p |
+ Used to output the priority of the logging event. |
+
+
+
+
+ | r |
+
+ Used to output the number of milliseconds elapsed since the construction
+ of the layout until the creation of the logging event. |
+
+
+
+
+ | t |
+
+ Used to output the name of the thread that generated the
+ logging event. |
+
+
+
+
+
+ | x |
+
+ Used to output the NDC (nested diagnostic context) associated
+ with the thread that generated the logging event.
+ |
+
+
+
+
+ | X |
+
+
+
+ Used to output the MDC (mapped diagnostic context) associated
+ with the thread that generated the logging event. The X
+ conversion character can be followed by the key for the
+ map placed between braces, as in %X{clientNumber} where
+ clientNumber is the key. The value in the MDC
+ corresponding to the key will be output. If no additional sub-option
+ is specified, then the entire contents of the MDC key value pair set
+ is output using a format {{key1,val1},{key2,val2}}
+
+ See {@link MDC} class for more details.
+
+
+ |
+
+
+
+ | properties |
+
+
+ Used to output the Properties associated
+ with the logging event. The properties
+ conversion word can be followed by the key for the
+ map placed between braces, as in %properties{application} where
+ application is the key. The value in the Properties bundle
+ corresponding to the key will be output. If no additional sub-option
+ is specified, then the entire contents of the Properties key value pair set
+ is output using a format {{key1,val1},{key2,val2}}
+ |
+
+
+
+ | throwable |
+
+
+ Used to output the Throwable trace that has been bound to the LoggingEvent, by
+ default this will output the full trace as one would normally
+ find by a call to Throwable.printStackTrace().
+ %throwable{short} or %throwable{1} will output the first line of
+ stack trace. throwable{none} or throwable{0} will suppress
+ the stack trace. %throwable{n} will output n lines of stack trace
+ if a positive integer or omit the last -n lines if a negative integer.
+ If no %throwable pattern is specified, the appender will take
+ responsibility to output the stack trace as it sees fit.
+ |
+
+
+
+
+ | % |
+
+ The sequence %% outputs a single percent sign.
+ |
+
+
+
+
+ By default the relevant information is output as is. However,
+ with the aid of format modifiers it is possible to change the
+ minimum field width, the maximum field width and justification.
+
+
The optional format modifier is placed between the percent sign
+ and the conversion character.
+
+
The first optional format modifier is the left justification
+ flag which is just the minus (-) character. Then comes the
+ optional minimum field width modifier. This is a decimal
+ constant that represents the minimum number of characters to
+ output. If the data item requires fewer characters, it is padded on
+ either the left or the right until the minimum width is
+ reached. The default is to pad on the left (right justify) but you
+ can specify right padding with the left justification flag. The
+ padding character is space. If the data item is larger than the
+ minimum field width, the field is expanded to accommodate the
+ data. The value is never truncated.
+
+
This behavior can be changed using the maximum field
+ width modifier which is designated by a period followed by a
+ decimal constant. If the data item is longer than the maximum
+ field, then the extra characters are removed from the
+ beginning of the data item and not from the end. For
+ example, it the maximum field width is eight and the data item is
+ ten characters long, then the first two characters of the data item
+ are dropped. This behavior deviates from the printf function in C
+ where truncation is done from the end.
+
+
Below are various format modifier examples for the category
+ conversion specifier.
+
+
+
+ | Format modifier
+ | left justify
+ | minimum width
+ | maximum width
+ | comment
+
+ |
+ | %20c |
+ false |
+ 20 |
+ none |
+
+ Left pad with spaces if the category name is less than 20
+ characters long.
+
+ |
| %-20c | true | 20 | none | Right pad with
+ spaces if the category name is less than 20 characters long.
+
+ |
+ | %.30c |
+ NA |
+ none |
+ 30 |
+
+ Truncate from the beginning if the category name is longer than 30
+ characters.
+
+ |
+ | %20.30c |
+ false |
+ 20 |
+ 30 |
+
+ Left pad with spaces if the category name is shorter than 20
+ characters. However, if category name is longer than 30 characters,
+ then truncate from the beginning.
+
+ |
+ | %-20.30c |
+ true |
+ 20 |
+ 30 |
+
+ Right pad with spaces if the category name is shorter than 20
+ characters. However, if category name is longer than 30 characters,
+ then truncate from the beginning.
+
+ |
+
+ Below are some examples of conversion patterns.
+
+
+
+ - %r [%t] %-5p %c %x - %m%n
+
- This is essentially the TTCC layout.
+
+
- %-6r [%15.15t] %-5p %30.30c %x - %m%n
+
+
- Similar to the TTCC layout except that the relative time is
+ right padded if less than 6 digits, thread name is right padded if
+ less than 15 characters and truncated if longer and the category
+ name is left padded if shorter than 30 characters and truncated if
+ longer.
+
+
+
+ The above text is largely inspired from Peter A. Darnell and
+ Philip E. Margolis' highly recommended book "C -- a Software
+ Engineering Approach", ISBN 0-387-97389-3.
+
+ @author James P. Cakalic
+ @author Ceki Gülcü
+
+
+ @since 1.2.16 */
+public class EnhancedPatternLayout extends Layout {
+ /** Default pattern string for log output. Currently set to the
+ string "%m%n" which just prints the application supplied
+ message. */
+ public static final String DEFAULT_CONVERSION_PATTERN = "%m%n";
+
+ /** A conversion pattern equivalent to the TTCCCLayout.
+ Current value is %r [%t] %p %c %x - %m%n. */
+ public static final String TTCC_CONVERSION_PATTERN =
+ "%r [%t] %p %c %x - %m%n";
+
+ /**
+ * Initial size of internal buffer, no longer used.
+ * @deprecated since 1.3
+ */
+ protected final int BUF_SIZE = 256;
+
+ /**
+ * Maximum capacity of internal buffer, no longer used.
+ * @deprecated since 1.3
+ */
+ protected final int MAX_CAPACITY = 1024;
+
+ /**
+ * Customized pattern conversion rules are stored under this key in the
+ * {@link org.apache.log4j.spi.LoggerRepository LoggerRepository} object store.
+ */
+ public static final String PATTERN_RULE_REGISTRY = "PATTERN_RULE_REGISTRY";
+
+
+ /**
+ * Initial converter for pattern.
+ */
+ private PatternConverter head;
+
+ /**
+ * Conversion pattern.
+ */
+ private String conversionPattern;
+
+ /**
+ * True if any element in pattern formats information from exceptions.
+ */
+ private boolean handlesExceptions;
+
+ /**
+ Constructs a EnhancedPatternLayout using the DEFAULT_LAYOUT_PATTERN.
+
+ The default pattern just produces the application supplied message.
+ */
+ public EnhancedPatternLayout() {
+ this(DEFAULT_CONVERSION_PATTERN);
+ }
+
+ /**
+ * Constructs a EnhancedPatternLayout using the supplied conversion pattern.
+ * @param pattern conversion pattern.
+ */
+ public EnhancedPatternLayout(final String pattern) {
+ this.conversionPattern = pattern;
+ head = createPatternParser(
+ (pattern == null) ? DEFAULT_CONVERSION_PATTERN : pattern).parse();
+ if (head instanceof BridgePatternConverter) {
+ handlesExceptions = !((BridgePatternConverter) head).ignoresThrowable();
+ } else {
+ handlesExceptions = false;
+ }
+ }
+
+ /**
+ * Set the ConversionPattern option. This is the string which
+ * controls formatting and consists of a mix of literal content and
+ * conversion specifiers.
+ *
+ * @param conversionPattern conversion pattern.
+ */
+ public void setConversionPattern(final String conversionPattern) {
+ this.conversionPattern =
+ OptionConverter.convertSpecialChars(conversionPattern);
+ head = createPatternParser(this.conversionPattern).parse();
+ if (head instanceof BridgePatternConverter) {
+ handlesExceptions = !((BridgePatternConverter) head).ignoresThrowable();
+ } else {
+ handlesExceptions = false;
+ }
+ }
+
+ /**
+ * Returns the value of the ConversionPattern option.
+ * @return conversion pattern.
+ */
+ public String getConversionPattern() {
+ return conversionPattern;
+ }
+
+
+ /**
+ Returns PatternParser used to parse the conversion string. Subclasses
+ may override this to return a subclass of PatternParser which recognize
+ custom conversion characters.
+
+ @since 0.9.0
+ */
+ protected org.apache.log4j.helpers.PatternParser createPatternParser(String pattern) {
+ return new org.apache.log4j.pattern.BridgePatternParser(pattern);
+ }
+
+
+ /**
+ Activates the conversion pattern. Do not forget to call this method after
+ you change the parameters of the EnhancedPatternLayout instance.
+ */
+ public void activateOptions() {
+ // nothing to do.
+ }
+
+
+ /**
+ * Formats a logging event to a writer.
+ * @param event logging event to be formatted.
+ */
+ public String format(final LoggingEvent event) {
+ StringBuffer buf = new StringBuffer();
+ for(PatternConverter c = head;
+ c != null;
+ c = c.next) {
+ c.format(buf, event);
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Will return false if any of the conversion specifiers in the pattern
+ * handles {@link Exception Exceptions}.
+ * @return true if the pattern formats any information from exceptions.
+ */
+ public boolean ignoresThrowable() {
+ return !handlesExceptions;
+ }
+}
diff --git a/java/src/org/apache/log4j/EnhancedThrowableRenderer.java b/java/src/org/apache/log4j/EnhancedThrowableRenderer.java
new file mode 100644
index 0000000..c5b8d7b
--- /dev/null
+++ b/java/src/org/apache/log4j/EnhancedThrowableRenderer.java
@@ -0,0 +1,168 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.ThrowableRenderer;
+
+import java.io.File;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.security.CodeSource;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Enhanced implementation of ThrowableRenderer. Uses Throwable.getStackTrace
+ * if running on JDK 1.4 or later and delegates to DefaultThrowableRenderer.render
+ * on earlier virtual machines.
+ *
+ * @since 1.2.16
+ */
+public final class EnhancedThrowableRenderer implements ThrowableRenderer {
+ /**
+ * Throwable.getStackTrace() method.
+ */
+ private Method getStackTraceMethod;
+ /**
+ * StackTraceElement.getClassName() method.
+ */
+ private Method getClassNameMethod;
+
+
+ /**
+ * Construct new instance.
+ */
+ public EnhancedThrowableRenderer() {
+ try {
+ Class[] noArgs = null;
+ getStackTraceMethod = Throwable.class.getMethod("getStackTrace", noArgs);
+ Class ste = Class.forName("java.lang.StackTraceElement");
+ getClassNameMethod = ste.getMethod("getClassName", noArgs);
+ } catch(Exception ex) {
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String[] doRender(final Throwable throwable) {
+ if (getStackTraceMethod != null) {
+ try {
+ Object[] noArgs = null;
+ Object[] elements = (Object[]) getStackTraceMethod.invoke(throwable, noArgs);
+ String[] lines = new String[elements.length + 1];
+ lines[0] = throwable.toString();
+ Map classMap = new HashMap();
+ for(int i = 0; i < elements.length; i++) {
+ lines[i+1] = formatElement(elements[i], classMap);
+ }
+ return lines;
+ } catch(Exception ex) {
+ }
+ }
+ return DefaultThrowableRenderer.render(throwable);
+ }
+
+ /**
+ * Format one element from stack trace.
+ * @param element element, may not be null.
+ * @param classMap map of class name to location.
+ * @return string representation of element.
+ */
+ private String formatElement(final Object element, final Map classMap) {
+ StringBuffer buf = new StringBuffer("\tat ");
+ buf.append(element);
+ try {
+ String className = getClassNameMethod.invoke(element, (Object[]) null).toString();
+ Object classDetails = classMap.get(className);
+ if (classDetails != null) {
+ buf.append(classDetails);
+ } else {
+ Class cls = findClass(className);
+ int detailStart = buf.length();
+ buf.append('[');
+ try {
+ CodeSource source = cls.getProtectionDomain().getCodeSource();
+ if (source != null) {
+ URL locationURL = source.getLocation();
+ if (locationURL != null) {
+ //
+ // if a file: URL
+ //
+ if ("file".equals(locationURL.getProtocol())) {
+ String path = locationURL.getPath();
+ if (path != null) {
+ //
+ // find the last file separator character
+ //
+ int lastSlash = path.lastIndexOf('/');
+ int lastBack = path.lastIndexOf(File.separatorChar);
+ if (lastBack > lastSlash) {
+ lastSlash = lastBack;
+ }
+ //
+ // if no separator or ends with separator (a directory)
+ // then output the URL, otherwise just the file name.
+ //
+ if (lastSlash <= 0 || lastSlash == path.length() - 1) {
+ buf.append(locationURL);
+ } else {
+ buf.append(path.substring(lastSlash + 1));
+ }
+ }
+ } else {
+ buf.append(locationURL);
+ }
+ }
+ }
+ } catch(SecurityException ex) {
+ }
+ buf.append(':');
+ Package pkg = cls.getPackage();
+ if (pkg != null) {
+ String implVersion = pkg.getImplementationVersion();
+ if (implVersion != null) {
+ buf.append(implVersion);
+ }
+ }
+ buf.append(']');
+ classMap.put(className, buf.substring(detailStart));
+ }
+ } catch(Exception ex) {
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Find class given class name.
+ * @param className class name, may not be null.
+ * @return class, will not be null.
+ * @throws ClassNotFoundException thrown if class can not be found.
+ */
+ private Class findClass(final String className) throws ClassNotFoundException {
+ try {
+ return Thread.currentThread().getContextClassLoader().loadClass(className);
+ } catch (ClassNotFoundException e) {
+ try {
+ return Class.forName(className);
+ } catch (ClassNotFoundException e1) {
+ return getClass().getClassLoader().loadClass(className);
+ }
+ }
+ }
+
+}
diff --git a/java/src/org/apache/log4j/FileAppender.java b/java/src/org/apache/log4j/FileAppender.java
new file mode 100644
index 0000000..0728695
--- /dev/null
+++ b/java/src/org/apache/log4j/FileAppender.java
@@ -0,0 +1,348 @@
+/*
+ * 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.log4j;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.Writer;
+
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorCode;
+
+// Contibutors: Jens Uwe Pipka
+// Ben Sandee
+
+/**
+ * FileAppender appends log events to a file.
+ *
+ * Support for java.io.Writer and console appending
+ * has been deprecated and then removed. See the replacement
+ * solutions: {@link WriterAppender} and {@link ConsoleAppender}.
+ *
+ * @author Ceki Gülcü
+ * */
+public class FileAppender extends WriterAppender {
+
+ /** Controls file truncatation. The default value for this variable
+ * is true, meaning that by default a
+ * FileAppender will append to an existing file and not
+ * truncate it.
+ *
+ *
This option is meaningful only if the FileAppender opens the
+ * file.
+ */
+ protected boolean fileAppend = true;
+
+ /**
+ The name of the log file. */
+ protected String fileName = null;
+
+ /**
+ Do we do bufferedIO? */
+ protected boolean bufferedIO = false;
+
+ /**
+ * Determines the size of IO buffer be. Default is 8K.
+ */
+ protected int bufferSize = 8*1024;
+
+
+ /**
+ The default constructor does not do anything.
+ */
+ public
+ FileAppender() {
+ }
+
+ /**
+ Instantiate a FileAppender and open the file
+ designated by filename. The opened filename will
+ become the output destination for this appender.
+
+
If the append parameter is true, the file will be
+ appended to. Otherwise, the file designated by
+ filename will be truncated before being opened.
+
+
If the bufferedIO parameter is true,
+ then buffered IO will be used to write to the output file.
+
+ */
+ public
+ FileAppender(Layout layout, String filename, boolean append, boolean bufferedIO,
+ int bufferSize) throws IOException {
+ this.layout = layout;
+ this.setFile(filename, append, bufferedIO, bufferSize);
+ }
+
+ /**
+ Instantiate a FileAppender and open the file designated by
+ filename. The opened filename will become the output
+ destination for this appender.
+
+
If the append parameter is true, the file will be
+ appended to. Otherwise, the file designated by
+ filename will be truncated before being opened.
+ */
+ public
+ FileAppender(Layout layout, String filename, boolean append)
+ throws IOException {
+ this.layout = layout;
+ this.setFile(filename, append, false, bufferSize);
+ }
+
+ /**
+ Instantiate a FileAppender and open the file designated by
+ filename. The opened filename will become the output
+ destination for this appender.
+
+
The file will be appended to. */
+ public
+ FileAppender(Layout layout, String filename) throws IOException {
+ this(layout, filename, true);
+ }
+
+ /**
+ The File property takes a string value which should be the
+ name of the file to append to.
+
+
Note that the special values
+ "System.out" or "System.err" are no longer honored.
+
+
Note: Actual opening of the file is made when {@link
+ #activateOptions} is called, not when the options are set. */
+ public void setFile(String file) {
+ // Trim spaces from both ends. The users probably does not want
+ // trailing spaces in file names.
+ String val = file.trim();
+ fileName = val;
+ }
+
+ /**
+ Returns the value of the Append option.
+ */
+ public
+ boolean getAppend() {
+ return fileAppend;
+ }
+
+
+ /** Returns the value of the File option. */
+ public
+ String getFile() {
+ return fileName;
+ }
+
+ /**
+ If the value of File is not null, then {@link
+ #setFile} is called with the values of File and
+ Append properties.
+
+ @since 0.8.1 */
+ public
+ void activateOptions() {
+ if(fileName != null) {
+ try {
+ setFile(fileName, fileAppend, bufferedIO, bufferSize);
+ }
+ catch(java.io.IOException e) {
+ errorHandler.error("setFile("+fileName+","+fileAppend+") call failed.",
+ e, ErrorCode.FILE_OPEN_FAILURE);
+ }
+ } else {
+ //LogLog.error("File option not set for appender ["+name+"].");
+ LogLog.warn("File option not set for appender ["+name+"].");
+ LogLog.warn("Are you using FileAppender instead of ConsoleAppender?");
+ }
+ }
+
+ /**
+ Closes the previously opened file.
+ */
+ protected
+ void closeFile() {
+ if(this.qw != null) {
+ try {
+ this.qw.close();
+ }
+ catch(java.io.IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // Exceptionally, it does not make sense to delegate to an
+ // ErrorHandler. Since a closed appender is basically dead.
+ LogLog.error("Could not close " + qw, e);
+ }
+ }
+ }
+
+ /**
+ Get the value of the BufferedIO option.
+
+
BufferedIO will significatnly increase performance on heavily
+ loaded systems.
+
+ */
+ public
+ boolean getBufferedIO() {
+ return this.bufferedIO;
+ }
+
+
+ /**
+ Get the size of the IO buffer.
+ */
+ public
+ int getBufferSize() {
+ return this.bufferSize;
+ }
+
+
+
+ /**
+ The Append option takes a boolean value. It is set to
+ true by default. If true, then File
+ will be opened in append mode by {@link #setFile setFile} (see
+ above). Otherwise, {@link #setFile setFile} will open
+ File in truncate mode.
+
+
Note: Actual opening of the file is made when {@link
+ #activateOptions} is called, not when the options are set.
+ */
+ public
+ void setAppend(boolean flag) {
+ fileAppend = flag;
+ }
+
+ /**
+ The BufferedIO option takes a boolean value. It is set to
+ false by default. If true, then File
+ will be opened and the resulting {@link java.io.Writer} wrapped
+ around a {@link BufferedWriter}.
+
+ BufferedIO will significatnly increase performance on heavily
+ loaded systems.
+
+ */
+ public
+ void setBufferedIO(boolean bufferedIO) {
+ this.bufferedIO = bufferedIO;
+ if(bufferedIO) {
+ immediateFlush = false;
+ }
+ }
+
+
+ /**
+ Set the size of the IO buffer.
+ */
+ public
+ void setBufferSize(int bufferSize) {
+ this.bufferSize = bufferSize;
+ }
+
+ /**
+
Sets and opens the file where the log output will
+ go. The specified file must be writable.
+
+
If there was already an opened file, then the previous file
+ is closed first.
+
+
Do not use this method directly. To configure a FileAppender
+ or one of its subclasses, set its properties one by one and then
+ call activateOptions.
+
+ @param fileName The path to the log file.
+ @param append If true will append to fileName. Otherwise will
+ truncate fileName. */
+ public
+ synchronized
+ void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
+ throws IOException {
+ LogLog.debug("setFile called: "+fileName+", "+append);
+
+ // It does not make sense to have immediate flush and bufferedIO.
+ if(bufferedIO) {
+ setImmediateFlush(false);
+ }
+
+ reset();
+ FileOutputStream ostream = null;
+ try {
+ //
+ // attempt to create file
+ //
+ ostream = new FileOutputStream(fileName, append);
+ } catch(FileNotFoundException ex) {
+ //
+ // if parent directory does not exist then
+ // attempt to create it and try to create file
+ // see bug 9150
+ //
+ String parentName = new File(fileName).getParent();
+ if (parentName != null) {
+ File parentDir = new File(parentName);
+ if(!parentDir.exists() && parentDir.mkdirs()) {
+ ostream = new FileOutputStream(fileName, append);
+ } else {
+ throw ex;
+ }
+ } else {
+ throw ex;
+ }
+ }
+ Writer fw = createWriter(ostream);
+ if(bufferedIO) {
+ fw = new BufferedWriter(fw, bufferSize);
+ }
+ this.setQWForFiles(fw);
+ this.fileName = fileName;
+ this.fileAppend = append;
+ this.bufferedIO = bufferedIO;
+ this.bufferSize = bufferSize;
+ writeHeader();
+ LogLog.debug("setFile ended");
+ }
+
+
+ /**
+ Sets the quiet writer being used.
+
+ This method is overriden by {@link RollingFileAppender}.
+ */
+ protected
+ void setQWForFiles(Writer writer) {
+ this.qw = new QuietWriter(writer, errorHandler);
+ }
+
+
+ /**
+ Close any previously opened file and call the parent's
+ reset. */
+ protected
+ void reset() {
+ closeFile();
+ this.fileName = null;
+ super.reset();
+ }
+}
+
diff --git a/java/src/org/apache/log4j/HTMLLayout.java b/java/src/org/apache/log4j/HTMLLayout.java
new file mode 100644
index 0000000..f7020cf
--- /dev/null
+++ b/java/src/org/apache/log4j/HTMLLayout.java
@@ -0,0 +1,267 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.helpers.Transform;
+
+/**
+ * This layout outputs events in a HTML table.
+ *
+ * Appenders using this layout should have their encoding
+ * set to UTF-8 or UTF-16, otherwise events containing
+ * non ASCII characters could result in corrupted
+ * log files.
+ *
+ * @author Ceki Gülcü
+ */
+public class HTMLLayout extends Layout {
+
+ protected final int BUF_SIZE = 256;
+ protected final int MAX_CAPACITY = 1024;
+
+ static String TRACE_PREFIX = "
";
+
+ // output buffer appended to when format() is invoked
+ private StringBuffer sbuf = new StringBuffer(BUF_SIZE);
+
+ /**
+ A string constant used in naming the option for setting the the
+ location information flag. Current value of this string
+ constant is LocationInfo.
+
+
Note that all option keys are case sensitive.
+
+ @deprecated Options are now handled using the JavaBeans paradigm.
+ This constant is not longer needed and will be removed in the
+ near term.
+
+ */
+ public static final String LOCATION_INFO_OPTION = "LocationInfo";
+
+ /**
+ A string constant used in naming the option for setting the the
+ HTML document title. Current value of this string
+ constant is Title.
+ */
+ public static final String TITLE_OPTION = "Title";
+
+ // Print no location info by default
+ boolean locationInfo = false;
+
+ String title = "Log4J Log Messages";
+
+ /**
+ The LocationInfo option takes a boolean value. By
+ default, it is set to false which means there will be no location
+ information output by this layout. If the the option is set to
+ true, then the file name and line number of the statement
+ at the origin of the log statement will be output.
+
+
If you are embedding this layout within an {@link
+ org.apache.log4j.net.SMTPAppender} then make sure to set the
+ LocationInfo option of that appender as well.
+ */
+ public
+ void setLocationInfo(boolean flag) {
+ locationInfo = flag;
+ }
+
+ /**
+ Returns the current value of the LocationInfo option.
+ */
+ public
+ boolean getLocationInfo() {
+ return locationInfo;
+ }
+
+ /**
+ The Title option takes a String value. This option sets the
+ document title of the generated HTML document.
+
+
Defaults to 'Log4J Log Messages'.
+ */
+ public
+ void setTitle(String title) {
+ this.title = title;
+ }
+
+ /**
+ Returns the current value of the Title option.
+ */
+ public
+ String getTitle() {
+ return title;
+ }
+
+ /**
+ Returns the content type output by this layout, i.e "text/html".
+ */
+ public
+ String getContentType() {
+ return "text/html";
+ }
+
+ /**
+ No options to activate.
+ */
+ public
+ void activateOptions() {
+ }
+
+ public
+ String format(LoggingEvent event) {
+
+ if(sbuf.capacity() > MAX_CAPACITY) {
+ sbuf = new StringBuffer(BUF_SIZE);
+ } else {
+ sbuf.setLength(0);
+ }
+
+ sbuf.append(Layout.LINE_SEP + "
" + Layout.LINE_SEP);
+
+ sbuf.append("| ");
+ sbuf.append(event.timeStamp - LoggingEvent.getStartTime());
+ sbuf.append(" | " + Layout.LINE_SEP);
+
+ String escapedThread = Transform.escapeTags(event.getThreadName());
+ sbuf.append("");
+ sbuf.append(escapedThread);
+ sbuf.append(" | " + Layout.LINE_SEP);
+
+ sbuf.append("");
+ if (event.getLevel().equals(Level.DEBUG)) {
+ sbuf.append("");
+ sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
+ sbuf.append("");
+ }
+ else if(event.getLevel().isGreaterOrEqual(Level.WARN)) {
+ sbuf.append("");
+ sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
+ sbuf.append("");
+ } else {
+ sbuf.append(Transform.escapeTags(String.valueOf(event.getLevel())));
+ }
+ sbuf.append(" | " + Layout.LINE_SEP);
+
+ String escapedLogger = Transform.escapeTags(event.getLoggerName());
+ sbuf.append("");
+ sbuf.append(escapedLogger);
+ sbuf.append(" | " + Layout.LINE_SEP);
+
+ if(locationInfo) {
+ LocationInfo locInfo = event.getLocationInformation();
+ sbuf.append("");
+ sbuf.append(Transform.escapeTags(locInfo.getFileName()));
+ sbuf.append(':');
+ sbuf.append(locInfo.getLineNumber());
+ sbuf.append(" | " + Layout.LINE_SEP);
+ }
+
+ sbuf.append("");
+ sbuf.append(Transform.escapeTags(event.getRenderedMessage()));
+ sbuf.append(" | " + Layout.LINE_SEP);
+ sbuf.append("
" + Layout.LINE_SEP);
+
+ if (event.getNDC() != null) {
+ sbuf.append("| ");
+ sbuf.append("NDC: " + Transform.escapeTags(event.getNDC()));
+ sbuf.append(" |
" + Layout.LINE_SEP);
+ }
+
+ String[] s = event.getThrowableStrRep();
+ if(s != null) {
+ sbuf.append("| ");
+ appendThrowableAsHTML(s, sbuf);
+ sbuf.append(" |
" + Layout.LINE_SEP);
+ }
+
+ return sbuf.toString();
+ }
+
+ void appendThrowableAsHTML(String[] s, StringBuffer sbuf) {
+ if(s != null) {
+ int len = s.length;
+ if(len == 0)
+ return;
+ sbuf.append(Transform.escapeTags(s[0]));
+ sbuf.append(Layout.LINE_SEP);
+ for(int i = 1; i < len; i++) {
+ sbuf.append(TRACE_PREFIX);
+ sbuf.append(Transform.escapeTags(s[i]));
+ sbuf.append(Layout.LINE_SEP);
+ }
+ }
+ }
+
+ /**
+ Returns appropriate HTML headers.
+ */
+ public
+ String getHeader() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("" + Layout.LINE_SEP);
+ sbuf.append("" + Layout.LINE_SEP);
+ sbuf.append("" + Layout.LINE_SEP);
+ sbuf.append("" + title + "" + Layout.LINE_SEP);
+ sbuf.append("" + Layout.LINE_SEP);
+ sbuf.append("" + Layout.LINE_SEP);
+ sbuf.append("" + Layout.LINE_SEP);
+ sbuf.append("
" + Layout.LINE_SEP);
+ sbuf.append("Log session start time " + new java.util.Date() + "
" + Layout.LINE_SEP);
+ sbuf.append("
" + Layout.LINE_SEP);
+ sbuf.append("" + Layout.LINE_SEP);
+ sbuf.append("" + Layout.LINE_SEP);
+ sbuf.append("| Time | " + Layout.LINE_SEP);
+ sbuf.append("Thread | " + Layout.LINE_SEP);
+ sbuf.append("Level | " + Layout.LINE_SEP);
+ sbuf.append("Category | " + Layout.LINE_SEP);
+ if(locationInfo) {
+ sbuf.append("File:Line | " + Layout.LINE_SEP);
+ }
+ sbuf.append("Message | " + Layout.LINE_SEP);
+ sbuf.append("
" + Layout.LINE_SEP);
+ return sbuf.toString();
+ }
+
+ /**
+ Returns the appropriate HTML footers.
+ */
+ public
+ String getFooter() {
+ StringBuffer sbuf = new StringBuffer();
+ sbuf.append("
" + Layout.LINE_SEP);
+ sbuf.append("
" + Layout.LINE_SEP);
+ sbuf.append("");
+ return sbuf.toString();
+ }
+
+ /**
+ The HTML layout handles the throwable contained in logging
+ events. Hence, this method return false. */
+ public
+ boolean ignoresThrowable() {
+ return false;
+ }
+}
diff --git a/java/src/org/apache/log4j/Hierarchy.java b/java/src/org/apache/log4j/Hierarchy.java
new file mode 100644
index 0000000..5b712e0
--- /dev/null
+++ b/java/src/org/apache/log4j/Hierarchy.java
@@ -0,0 +1,577 @@
+/*
+ * 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.
+ */
+
+// WARNING This class MUST not have references to the Category or
+// WARNING RootCategory classes in its static initiliazation neither
+// WARNING directly nor indirectly.
+
+// Contributors:
+// Luke Blanshard
+// Mario Schomburg - IBM Global Services/Germany
+// Anders Kristensen
+// Igor Poteryaev
+
+package org.apache.log4j;
+
+
+import java.util.Hashtable;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.HierarchyEventListener;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.or.RendererMap;
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
+
+/**
+ This class is specialized in retrieving loggers by name and also
+ maintaining the logger hierarchy.
+
+ The casual user does not have to deal with this class
+ directly.
+
+
The structure of the logger hierarchy is maintained by the
+ {@link #getLogger} method. The hierarchy is such that children link
+ to their parent but parents do not have any pointers to their
+ children. Moreover, loggers can be instantiated in any order, in
+ particular descendant before ancestor.
+
+
In case a descendant is created before a particular ancestor,
+ then it creates a provision node for the ancestor and adds itself
+ to the provision node. Other descendants of the same ancestor add
+ themselves to the previously created provision node.
+
+ @author Ceki Gülcü
+
+*/
+public class Hierarchy implements LoggerRepository, RendererSupport, ThrowableRendererSupport {
+
+ private LoggerFactory defaultFactory;
+ private Vector listeners;
+
+ Hashtable ht;
+ Logger root;
+ RendererMap rendererMap;
+
+ int thresholdInt;
+ Level threshold;
+
+ boolean emittedNoAppenderWarning = false;
+ boolean emittedNoResourceBundleWarning = false;
+
+ private ThrowableRenderer throwableRenderer = null;
+
+ /**
+ Create a new logger hierarchy.
+
+ @param root The root of the new hierarchy.
+
+ */
+ public
+ Hierarchy(Logger root) {
+ ht = new Hashtable();
+ listeners = new Vector(1);
+ this.root = root;
+ // Enable all level levels by default.
+ setThreshold(Level.ALL);
+ this.root.setHierarchy(this);
+ rendererMap = new RendererMap();
+ defaultFactory = new DefaultCategoryFactory();
+ }
+
+ /**
+ Add an object renderer for a specific class.
+ */
+ public
+ void addRenderer(Class classToRender, ObjectRenderer or) {
+ rendererMap.put(classToRender, or);
+ }
+
+ public
+ void addHierarchyEventListener(HierarchyEventListener listener) {
+ if(listeners.contains(listener)) {
+ LogLog.warn("Ignoring attempt to add an existent listener.");
+ } else {
+ listeners.addElement(listener);
+ }
+ }
+
+ /**
+ This call will clear all logger definitions from the internal
+ hashtable. Invoking this method will irrevocably mess up the
+ logger hierarchy.
+
+
You should really know what you are doing before
+ invoking this method.
+
+ @since 0.9.0 */
+ public
+ void clear() {
+ //System.out.println("\n\nAbout to clear internal hash table.");
+ ht.clear();
+ }
+
+ public
+ void emitNoAppenderWarning(Category cat) {
+ // No appenders in hierarchy, warn user only once.
+ if(!this.emittedNoAppenderWarning) {
+ LogLog.warn("No appenders could be found for logger (" +
+ cat.getName() + ").");
+ LogLog.warn("Please initialize the log4j system properly.");
+ LogLog.warn("See http://logging.apache.org/log4j/1.2/faq.html#noconfig for more info.");
+ this.emittedNoAppenderWarning = true;
+ }
+ }
+
+ /**
+ Check if the named logger exists in the hierarchy. If so return
+ its reference, otherwise returns null.
+
+ @param name The name of the logger to search for.
+
+ */
+ public
+ Logger exists(String name) {
+ Object o = ht.get(new CategoryKey(name));
+ if(o instanceof Logger) {
+ return (Logger) o;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ The string form of {@link #setThreshold(Level)}.
+ */
+ public
+ void setThreshold(String levelStr) {
+ Level l = (Level) Level.toLevel(levelStr, null);
+ if(l != null) {
+ setThreshold(l);
+ } else {
+ LogLog.warn("Could not convert ["+levelStr+"] to Level.");
+ }
+ }
+
+
+ /**
+ Enable logging for logging requests with level l or
+ higher. By default all levels are enabled.
+
+ @param l The minimum level for which logging requests are sent to
+ their appenders. */
+ public
+ void setThreshold(Level l) {
+ if(l != null) {
+ thresholdInt = l.level;
+ threshold = l;
+ }
+ }
+
+ public
+ void fireAddAppenderEvent(Category logger, Appender appender) {
+ if(listeners != null) {
+ int size = listeners.size();
+ HierarchyEventListener listener;
+ for(int i = 0; i < size; i++) {
+ listener = (HierarchyEventListener) listeners.elementAt(i);
+ listener.addAppenderEvent(logger, appender);
+ }
+ }
+ }
+
+ void fireRemoveAppenderEvent(Category logger, Appender appender) {
+ if(listeners != null) {
+ int size = listeners.size();
+ HierarchyEventListener listener;
+ for(int i = 0; i < size; i++) {
+ listener = (HierarchyEventListener) listeners.elementAt(i);
+ listener.removeAppenderEvent(logger, appender);
+ }
+ }
+ }
+
+ /**
+ Returns a {@link Level} representation of the enable
+ state.
+
+ @since 1.2 */
+ public
+ Level getThreshold() {
+ return threshold;
+ }
+
+ /**
+ Returns an integer representation of the this repository's
+ threshold.
+
+ @since 1.2 */
+ //public
+ //int getThresholdInt() {
+ // return thresholdInt;
+ //}
+
+
+ /**
+ Return a new logger instance named as the first parameter using
+ the default factory.
+
+
If a logger of that name already exists, then it will be
+ returned. Otherwise, a new logger will be instantiated and
+ then linked with its existing ancestors as well as children.
+
+ @param name The name of the logger to retrieve.
+
+ */
+ public
+ Logger getLogger(String name) {
+ return getLogger(name, defaultFactory);
+ }
+
+ /**
+ Return a new logger instance named as the first parameter using
+ factory.
+
+
If a logger of that name already exists, then it will be
+ returned. Otherwise, a new logger will be instantiated by the
+ factory parameter and linked with its existing
+ ancestors as well as children.
+
+ @param name The name of the logger to retrieve.
+ @param factory The factory that will make the new logger instance.
+
+ */
+ public
+ Logger getLogger(String name, LoggerFactory factory) {
+ //System.out.println("getInstance("+name+") called.");
+ CategoryKey key = new CategoryKey(name);
+ // Synchronize to prevent write conflicts. Read conflicts (in
+ // getChainedLevel method) are possible only if variable
+ // assignments are non-atomic.
+ Logger logger;
+
+ synchronized(ht) {
+ Object o = ht.get(key);
+ if(o == null) {
+ logger = factory.makeNewLoggerInstance(name);
+ logger.setHierarchy(this);
+ ht.put(key, logger);
+ updateParents(logger);
+ return logger;
+ } else if(o instanceof Logger) {
+ return (Logger) o;
+ } else if (o instanceof ProvisionNode) {
+ //System.out.println("("+name+") ht.get(this) returned ProvisionNode");
+ logger = factory.makeNewLoggerInstance(name);
+ logger.setHierarchy(this);
+ ht.put(key, logger);
+ updateChildren((ProvisionNode) o, logger);
+ updateParents(logger);
+ return logger;
+ }
+ else {
+ // It should be impossible to arrive here
+ return null; // but let's keep the compiler happy.
+ }
+ }
+ }
+
+ /**
+ Returns all the currently defined categories in this hierarchy as
+ an {@link java.util.Enumeration Enumeration}.
+
+
The root logger is not included in the returned
+ {@link Enumeration}. */
+ public
+ Enumeration getCurrentLoggers() {
+ // The accumlation in v is necessary because not all elements in
+ // ht are Logger objects as there might be some ProvisionNodes
+ // as well.
+ Vector v = new Vector(ht.size());
+
+ Enumeration elems = ht.elements();
+ while(elems.hasMoreElements()) {
+ Object o = elems.nextElement();
+ if(o instanceof Logger) {
+ v.addElement(o);
+ }
+ }
+ return v.elements();
+ }
+
+ /**
+ @deprecated Please use {@link #getCurrentLoggers} instead.
+ */
+ public
+ Enumeration getCurrentCategories() {
+ return getCurrentLoggers();
+ }
+
+
+ /**
+ Get the renderer map for this hierarchy.
+ */
+ public
+ RendererMap getRendererMap() {
+ return rendererMap;
+ }
+
+
+ /**
+ Get the root of this hierarchy.
+
+ @since 0.9.0
+ */
+ public
+ Logger getRootLogger() {
+ return root;
+ }
+
+ /**
+ This method will return true if this repository is
+ disabled for level object passed as parameter and
+ false otherwise. See also the {@link
+ #setThreshold(Level) threshold} emthod. */
+ public
+ boolean isDisabled(int level) {
+ return thresholdInt > level;
+ }
+
+ /**
+ @deprecated Deprecated with no replacement.
+ */
+ public
+ void overrideAsNeeded(String override) {
+ LogLog.warn("The Hiearchy.overrideAsNeeded method has been deprecated.");
+ }
+
+ /**
+ Reset all values contained in this hierarchy instance to their
+ default. This removes all appenders from all categories, sets
+ the level of all non-root categories to null,
+ sets their additivity flag to true and sets the level
+ of the root logger to {@link Level#DEBUG DEBUG}. Moreover,
+ message disabling is set its default "off" value.
+
+
Existing categories are not removed. They are just reset.
+
+
This method should be used sparingly and with care as it will
+ block all logging until it is completed.
+
+ @since 0.8.5 */
+ public
+ void resetConfiguration() {
+
+ getRootLogger().setLevel((Level) Level.DEBUG);
+ root.setResourceBundle(null);
+ setThreshold(Level.ALL);
+
+ // the synchronization is needed to prevent JDK 1.2.x hashtable
+ // surprises
+ synchronized(ht) {
+ shutdown(); // nested locks are OK
+
+ Enumeration cats = getCurrentLoggers();
+ while(cats.hasMoreElements()) {
+ Logger c = (Logger) cats.nextElement();
+ c.setLevel(null);
+ c.setAdditivity(true);
+ c.setResourceBundle(null);
+ }
+ }
+ rendererMap.clear();
+ throwableRenderer = null;
+ }
+
+ /**
+ Does nothing.
+
+ @deprecated Deprecated with no replacement.
+ */
+ public
+ void setDisableOverride(String override) {
+ LogLog.warn("The Hiearchy.setDisableOverride method has been deprecated.");
+ }
+
+
+
+ /**
+ Used by subclasses to add a renderer to the hierarchy passed as parameter.
+ */
+ public
+ void setRenderer(Class renderedClass, ObjectRenderer renderer) {
+ rendererMap.put(renderedClass, renderer);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setThrowableRenderer(final ThrowableRenderer renderer) {
+ throwableRenderer = renderer;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public ThrowableRenderer getThrowableRenderer() {
+ return throwableRenderer;
+ }
+
+
+ /**
+ Shutting down a hierarchy will safely close and remove
+ all appenders in all categories including the root logger.
+
+ Some appenders such as {@link org.apache.log4j.net.SocketAppender}
+ and {@link AsyncAppender} need to be closed before the
+ application exists. Otherwise, pending logging events might be
+ lost.
+
+
The shutdown method is careful to close nested
+ appenders before closing regular appenders. This is allows
+ configurations where a regular appender is attached to a logger
+ and again to a nested appender.
+
+
+ @since 1.0 */
+ public
+ void shutdown() {
+ Logger root = getRootLogger();
+
+ // begin by closing nested appenders
+ root.closeNestedAppenders();
+
+ synchronized(ht) {
+ Enumeration cats = this.getCurrentLoggers();
+ while(cats.hasMoreElements()) {
+ Logger c = (Logger) cats.nextElement();
+ c.closeNestedAppenders();
+ }
+
+ // then, remove all appenders
+ root.removeAllAppenders();
+ cats = this.getCurrentLoggers();
+ while(cats.hasMoreElements()) {
+ Logger c = (Logger) cats.nextElement();
+ c.removeAllAppenders();
+ }
+ }
+ }
+
+
+ /**
+ This method loops through all the *potential* parents of
+ 'cat'. There 3 possible cases:
+
+ 1) No entry for the potential parent of 'cat' exists
+
+ We create a ProvisionNode for this potential parent and insert
+ 'cat' in that provision node.
+
+ 2) There entry is of type Logger for the potential parent.
+
+ The entry is 'cat's nearest existing parent. We update cat's
+ parent field with this entry. We also break from the loop
+ because updating our parent's parent is our parent's
+ responsibility.
+
+ 3) There entry is of type ProvisionNode for this potential parent.
+
+ We add 'cat' to the list of children for this potential parent.
+ */
+ final
+ private
+ void updateParents(Logger cat) {
+ String name = cat.name;
+ int length = name.length();
+ boolean parentFound = false;
+
+ //System.out.println("UpdateParents called for " + name);
+
+ // if name = "w.x.y.z", loop thourgh "w.x.y", "w.x" and "w", but not "w.x.y.z"
+ for(int i = name.lastIndexOf('.', length-1); i >= 0;
+ i = name.lastIndexOf('.', i-1)) {
+ String substr = name.substring(0, i);
+
+ //System.out.println("Updating parent : " + substr);
+ CategoryKey key = new CategoryKey(substr); // simple constructor
+ Object o = ht.get(key);
+ // Create a provision node for a future parent.
+ if(o == null) {
+ //System.out.println("No parent "+substr+" found. Creating ProvisionNode.");
+ ProvisionNode pn = new ProvisionNode(cat);
+ ht.put(key, pn);
+ } else if(o instanceof Category) {
+ parentFound = true;
+ cat.parent = (Category) o;
+ //System.out.println("Linking " + cat.name + " -> " + ((Category) o).name);
+ break; // no need to update the ancestors of the closest ancestor
+ } else if(o instanceof ProvisionNode) {
+ ((ProvisionNode) o).addElement(cat);
+ } else {
+ Exception e = new IllegalStateException("unexpected object type " +
+ o.getClass() + " in ht.");
+ e.printStackTrace();
+ }
+ }
+ // If we could not find any existing parents, then link with root.
+ if(!parentFound)
+ cat.parent = root;
+ }
+
+ /**
+ We update the links for all the children that placed themselves
+ in the provision node 'pn'. The second argument 'cat' is a
+ reference for the newly created Logger, parent of all the
+ children in 'pn'
+
+ We loop on all the children 'c' in 'pn':
+
+ If the child 'c' has been already linked to a child of
+ 'cat' then there is no need to update 'c'.
+
+ Otherwise, we set cat's parent field to c's parent and set
+ c's parent field to cat.
+
+ */
+ final
+ private
+ void updateChildren(ProvisionNode pn, Logger logger) {
+ //System.out.println("updateChildren called for " + logger.name);
+ final int last = pn.size();
+
+ for(int i = 0; i < last; i++) {
+ Logger l = (Logger) pn.elementAt(i);
+ //System.out.println("Updating child " +p.name);
+
+ // Unless this child already points to a correct (lower) parent,
+ // make cat.parent point to l.parent and l.parent to cat.
+ if(!l.parent.name.startsWith(logger.name)) {
+ logger.parent = l.parent;
+ l.parent = logger;
+ }
+ }
+ }
+
+}
+
+
diff --git a/java/src/org/apache/log4j/Layout.java b/java/src/org/apache/log4j/Layout.java
new file mode 100644
index 0000000..63015aa
--- /dev/null
+++ b/java/src/org/apache/log4j/Layout.java
@@ -0,0 +1,89 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ Extend this abstract class to create your own log layout format.
+
+ @author Ceki Gülcü
+
+*/
+
+public abstract class Layout implements OptionHandler {
+
+ // Note that the line.separator property can be looked up even by
+ // applets.
+ public final static String LINE_SEP = System.getProperty("line.separator");
+ public final static int LINE_SEP_LEN = LINE_SEP.length();
+
+
+ /**
+ Implement this method to create your own layout format.
+ */
+ abstract
+ public
+ String format(LoggingEvent event);
+
+ /**
+ Returns the content type output by this layout. The base class
+ returns "text/plain".
+ */
+ public
+ String getContentType() {
+ return "text/plain";
+ }
+
+ /**
+ Returns the header for the layout format. The base class returns
+ null. */
+ public
+ String getHeader() {
+ return null;
+ }
+
+ /**
+ Returns the footer for the layout format. The base class returns
+ null. */
+ public
+ String getFooter() {
+ return null;
+ }
+
+
+
+ /**
+ If the layout handles the throwable object contained within
+ {@link LoggingEvent}, then the layout should return
+ false. Otherwise, if the layout ignores throwable
+ object, then the layout should return true.
+ If ignoresThrowable is true, the appender is responsible for
+ rendering the throwable.
+
+
The {@link SimpleLayout}, {@link TTCCLayout}, {@link
+ PatternLayout} all return true. The {@link
+ org.apache.log4j.xml.XMLLayout} returns false.
+
+ @since 0.8.4 */
+ abstract
+ public
+ boolean ignoresThrowable();
+
+}
diff --git a/java/src/org/apache/log4j/Level.java b/java/src/org/apache/log4j/Level.java
new file mode 100644
index 0000000..5028862
--- /dev/null
+++ b/java/src/org/apache/log4j/Level.java
@@ -0,0 +1,224 @@
+/*
+ * 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.
+ */
+
+// Contributors: Kitching Simon
+// Nicholas Wolff
+
+package org.apache.log4j;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.io.ObjectStreamException;
+import java.io.Serializable;
+
+/**
+ Defines the minimum set of levels recognized by the system, that is
+ OFF, FATAL, ERROR,
+ WARN, INFODEBUG and
+ ALL.
+
+ The Level class may be subclassed to define a larger
+ level set.
+
+ @author Ceki Gülcü
+
+ */
+public class Level extends Priority implements Serializable {
+
+ /**
+ * TRACE level integer value.
+ * @since 1.2.12
+ */
+ public static final int TRACE_INT = 5000;
+
+ /**
+ The OFF has the highest possible rank and is
+ intended to turn off logging. */
+ final static public Level OFF = new Level(OFF_INT, "OFF", 0);
+
+ /**
+ The FATAL level designates very severe error
+ events that will presumably lead the application to abort.
+ */
+ final static public Level FATAL = new Level(FATAL_INT, "FATAL", 0);
+
+ /**
+ The ERROR level designates error events that
+ might still allow the application to continue running. */
+ final static public Level ERROR = new Level(ERROR_INT, "ERROR", 3);
+
+ /**
+ The WARN level designates potentially harmful situations.
+ */
+ final static public Level WARN = new Level(WARN_INT, "WARN", 4);
+
+ /**
+ The INFO level designates informational messages
+ that highlight the progress of the application at coarse-grained
+ level. */
+ final static public Level INFO = new Level(INFO_INT, "INFO", 6);
+
+ /**
+ The DEBUG Level designates fine-grained
+ informational events that are most useful to debug an
+ application. */
+ final static public Level DEBUG = new Level(DEBUG_INT, "DEBUG", 7);
+
+ /**
+ * The TRACE Level designates finer-grained
+ * informational events than the DEBUGALL has the lowest possible rank and is intended to
+ turn on all logging. */
+ final static public Level ALL = new Level(ALL_INT, "ALL", 7);
+
+ /**
+ * Serialization version id.
+ */
+ static final long serialVersionUID = 3491141966387921974L;
+
+ /**
+ Instantiate a Level object.
+ */
+ protected
+ Level(int level, String levelStr, int syslogEquivalent) {
+ super(level, levelStr, syslogEquivalent);
+ }
+
+
+ /**
+ Convert the string passed as argument to a level. If the
+ conversion fails, then this method returns {@link #DEBUG}.
+ */
+ public
+ static
+ Level toLevel(String sArg) {
+ return (Level) toLevel(sArg, Level.DEBUG);
+ }
+
+ /**
+ Convert an integer passed as argument to a level. If the
+ conversion fails, then this method returns {@link #DEBUG}.
+
+ */
+ public
+ static
+ Level toLevel(int val) {
+ return (Level) toLevel(val, Level.DEBUG);
+ }
+
+ /**
+ Convert an integer passed as argument to a level. If the
+ conversion fails, then this method returns the specified default.
+ */
+ public
+ static
+ Level toLevel(int val, Level defaultLevel) {
+ switch(val) {
+ case ALL_INT: return ALL;
+ case DEBUG_INT: return Level.DEBUG;
+ case INFO_INT: return Level.INFO;
+ case WARN_INT: return Level.WARN;
+ case ERROR_INT: return Level.ERROR;
+ case FATAL_INT: return Level.FATAL;
+ case OFF_INT: return OFF;
+ case TRACE_INT: return Level.TRACE;
+ default: return defaultLevel;
+ }
+ }
+
+ /**
+ Convert the string passed as argument to a level. If the
+ conversion fails, then this method returns the value of
+ defaultLevel.
+ */
+ public
+ static
+ Level toLevel(String sArg, Level defaultLevel) {
+ if(sArg == null)
+ return defaultLevel;
+
+ String s = sArg.toUpperCase();
+
+ if(s.equals("ALL")) return Level.ALL;
+ if(s.equals("DEBUG")) return Level.DEBUG;
+ if(s.equals("INFO")) return Level.INFO;
+ if(s.equals("WARN")) return Level.WARN;
+ if(s.equals("ERROR")) return Level.ERROR;
+ if(s.equals("FATAL")) return Level.FATAL;
+ if(s.equals("OFF")) return Level.OFF;
+ if(s.equals("TRACE")) return Level.TRACE;
+ //
+ // For Turkish i problem, see bug 40937
+ //
+ if(s.equals("\u0130NFO")) return Level.INFO;
+ return defaultLevel;
+ }
+
+ /**
+ * Custom deserialization of Level.
+ * @param s serialization stream.
+ * @throws IOException if IO exception.
+ * @throws ClassNotFoundException if class not found.
+ */
+ private void readObject(final ObjectInputStream s) throws IOException, ClassNotFoundException {
+ s.defaultReadObject();
+ level = s.readInt();
+ syslogEquivalent = s.readInt();
+ levelStr = s.readUTF();
+ if (levelStr == null) {
+ levelStr = "";
+ }
+ }
+
+ /**
+ * Serialize level.
+ * @param s serialization stream.
+ * @throws IOException if exception during serialization.
+ */
+ private void writeObject(final ObjectOutputStream s) throws IOException {
+ s.defaultWriteObject();
+ s.writeInt(level);
+ s.writeInt(syslogEquivalent);
+ s.writeUTF(levelStr);
+ }
+
+ /**
+ * Resolved deserialized level to one of the stock instances.
+ * May be overriden in classes derived from Level.
+ * @return resolved object.
+ * @throws ObjectStreamException if exception during resolution.
+ */
+ private Object readResolve() throws ObjectStreamException {
+ //
+ // if the deserizalized object is exactly an instance of Level
+ //
+ if (getClass() == Level.class) {
+ return toLevel(level);
+ }
+ //
+ // extension of Level can't substitute stock item
+ //
+ return this;
+ }
+
+}
diff --git a/java/src/org/apache/log4j/LogMF.java b/java/src/org/apache/log4j/LogMF.java
new file mode 100644
index 0000000..2df99fc
--- /dev/null
+++ b/java/src/org/apache/log4j/LogMF.java
@@ -0,0 +1,1677 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.text.DateFormat;
+import java.text.MessageFormat;
+import java.text.NumberFormat;
+import java.util.Date;
+import java.util.ResourceBundle;
+import java.util.Locale;
+
+
+/**
+ * This class provides parameterized logging services
+ * using the pattern syntax of java.text.MessageFormat.
+ * Message formatting is only performed when the
+ * request exceeds the threshold level of the logger.
+ * When the pattern only contains literal text and
+ * default conversion patterns (that is "{0}" and similar)
+ * a simple fast compatible formatter is used.
+ * If the pattern contains more complex conversion patterns,
+ * formatting will be delegated to java.text.MessageFormatter
+ * which can be substantially slower.
+ *
+ * @see org.apache.log4j.LogSF
+ * @since 1.2.16
+ *
+ */
+public final class LogMF extends LogXF {
+ /**
+ * private constructor.
+ *
+ */
+ private LogMF() {
+ }
+
+ /**
+ * Number format.
+ */
+ private static NumberFormat numberFormat = null;
+ /**
+ * Locale at time of last number format request.
+ */
+ private static Locale numberLocale = null;
+ /**
+ * Date format.
+ */
+ private static DateFormat dateFormat = null;
+ /**
+ * Locale at time of last date format request.
+ */
+ private static Locale dateLocale = null;
+
+ /**
+ * Format number.
+ * @param n number to format, may not be null.
+ * @return formatted value.
+ */
+ private static synchronized String formatNumber(final Object n) {
+ Locale currentLocale = Locale.getDefault();
+ if (currentLocale != numberLocale || numberFormat == null) {
+ numberLocale = currentLocale;
+ numberFormat = NumberFormat.getInstance(currentLocale);
+ }
+ return numberFormat.format(n);
+ }
+
+
+ /**
+ * Format date.
+ * @param d date, may not be null.
+ * @return formatted value.
+ */
+ private static synchronized String formatDate(final Object d) {
+ Locale currentLocale = Locale.getDefault();
+ if (currentLocale != dateLocale || dateFormat == null) {
+ dateLocale = currentLocale;
+ dateFormat = DateFormat.getDateTimeInstance(
+ DateFormat.SHORT,
+ DateFormat.SHORT,
+ currentLocale);
+ }
+ return dateFormat.format(d);
+ }
+
+ /**
+ * Format a single parameter like a "{0}" formatting specifier.
+ *
+ * @param arg0 parameter, may be null.
+ * @return string representation of arg0.
+ */
+ private static String formatObject(final Object arg0) {
+ if (arg0 instanceof String) {
+ return arg0.toString();
+ } else if (arg0 instanceof Double ||
+ arg0 instanceof Float) {
+ return formatNumber(arg0);
+ } else if (arg0 instanceof Date) {
+ return formatDate(arg0);
+ }
+ return String.valueOf(arg0);
+ }
+
+
+ /**
+ * Determines if pattern contains only {n} format elements
+ * and not apostrophes.
+ *
+ * @param pattern pattern, may not be null.
+ * @return true if pattern only contains {n} format elements.
+ */
+ private static boolean isSimple(final String pattern) {
+ if (pattern.indexOf('\'') != -1) {
+ return false;
+ }
+ for(int pos = pattern.indexOf('{');
+ pos != -1;
+ pos = pattern.indexOf('{', pos + 1)) {
+ if (pos + 2 >= pattern.length() ||
+ pattern.charAt(pos+2) != '}' ||
+ pattern.charAt(pos+1) < '0' ||
+ pattern.charAt(pos+1) > '9') {
+ return false;
+ }
+ }
+ return true;
+
+ }
+
+ /**
+ * Formats arguments using MessageFormat.
+ * @param pattern pattern, may be malformed or null.
+ * @param arguments arguments, may be null or mismatched.
+ * @return Message string or null
+ */
+ private static String format(final String pattern,
+ final Object[] arguments) {
+ if (pattern == null) {
+ return null;
+ } else if(isSimple(pattern)) {
+ String formatted[] = new String[10];
+ int prev = 0;
+ String retval = "";
+ int pos = pattern.indexOf('{');
+ while(pos >= 0) {
+ if(pos + 2 < pattern.length() &&
+ pattern.charAt(pos+2) == '}' &&
+ pattern.charAt(pos+1) >= '0' &&
+ pattern.charAt(pos+1) <= '9') {
+ int index = pattern.charAt(pos+1) - '0';
+ retval += pattern.substring(prev, pos);
+ if (formatted[index] == null) {
+ if (arguments == null || index >= arguments.length) {
+ formatted[index] = pattern.substring(pos, pos+3);
+ } else {
+ formatted[index] = formatObject(arguments[index]);
+ }
+ }
+ retval += formatted[index];
+ prev = pos + 3;
+ pos = pattern.indexOf('{', prev);
+ } else {
+ pos = pattern.indexOf('{', pos + 1);
+ }
+ }
+ retval += pattern.substring(prev);
+ return retval;
+ }
+ try {
+ return MessageFormat.format(pattern, arguments);
+ } catch (IllegalArgumentException ex) {
+ return pattern;
+ }
+ }
+
+ /**
+ * Formats a single argument using MessageFormat.
+ * @param pattern pattern, may be malformed or null.
+ * @param arguments arguments, may be null or mismatched.
+ * @return Message string or null
+ */
+ private static String format(final String pattern,
+ final Object arg0) {
+ if (pattern == null) {
+ return null;
+ } else if(isSimple(pattern)) {
+ String formatted = null;
+ int prev = 0;
+ String retval = "";
+ int pos = pattern.indexOf('{');
+ while(pos >= 0) {
+ if(pos + 2 < pattern.length() &&
+ pattern.charAt(pos+2) == '}' &&
+ pattern.charAt(pos+1) >= '0' &&
+ pattern.charAt(pos+1) <= '9') {
+ int index = pattern.charAt(pos+1) - '0';
+ retval += pattern.substring(prev, pos);
+ if (index != 0) {
+ retval += pattern.substring(pos, pos+3);
+ } else {
+ if (formatted == null) {
+ formatted = formatObject(arg0);
+ }
+ retval += formatted;
+ }
+ prev = pos + 3;
+ pos = pattern.indexOf('{', prev);
+ } else {
+ pos = pattern.indexOf('{', pos + 1);
+ }
+ }
+ retval += pattern.substring(prev);
+ return retval;
+ }
+ try {
+ return MessageFormat.format(pattern, new Object[] { arg0 });
+ } catch (IllegalArgumentException ex) {
+ return pattern;
+ }
+ }
+
+
+ /**
+ * Formats arguments using MessageFormat using a pattern from
+ * a resource bundle.
+ * @param resourceBundleName name of resource bundle, may be null.
+ * @param key key for pattern in resource bundle, may be null.
+ * @param arguments arguments, may be null or mismatched.
+ * @return Message string or null
+ */
+ private static String format(
+ final String resourceBundleName,
+ final String key,
+ final Object[] arguments) {
+ String pattern;
+ if (resourceBundleName != null) {
+ try {
+ ResourceBundle bundle =
+ ResourceBundle.getBundle(resourceBundleName);
+ pattern = bundle.getString(key);
+ } catch (Exception ex) {
+ pattern = key;
+ }
+ } else {
+ pattern = key;
+ }
+ return format(pattern, arguments);
+ }
+
+
+ /**
+ * Fully Qualified Class Name of this class.
+ */
+ private static final String FQCN = LogMF.class.getName();
+
+ /**
+ * Equivalent of Logger.forcedLog.
+ *
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param msg message, may be null.
+ */
+ private static void forcedLog(final Logger logger,
+ final Level level,
+ final String msg) {
+ logger.callAppenders(new LoggingEvent(FQCN, logger, level, msg, null));
+ }
+
+ /**
+ * Equivalent of Logger.forcedLog.
+ *
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param msg message, may be null.
+ * @param t throwable.
+ */
+ private static void forcedLog(final Logger logger,
+ final Level level,
+ final String msg,
+ final Throwable t) {
+ logger.callAppenders(new LoggingEvent(FQCN, logger, level, msg, t));
+ }
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be
+ * formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at error level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void error(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.ERROR)) {
+ forcedLog(logger, Level.ERROR, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at fatal level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void fatal(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.FATAL)) {
+ forcedLog(logger, Level.FATAL, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be
+ * formatted and substituted.
+ */
+ public static void trace(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, arguments), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void debug(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, arguments), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void info(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, arguments), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void warn(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, arguments), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at error level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void error(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.ERROR)) {
+ forcedLog(logger, Level.ERROR, format(pattern, arguments), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at fatal level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void fatal(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.FATAL)) {
+ forcedLog(logger, Level.FATAL, format(pattern, arguments), t);
+ }
+ }
+
+
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final boolean argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final char argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final byte argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final short argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final int argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final long argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final float argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final double argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final Object argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, argument));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE,
+ format(pattern, toArray(arg0, arg1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE,
+ format(pattern, toArray(arg0, arg1, arg2)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ * @param arg3 a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2,
+ final Object arg3) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE,
+ format(pattern, toArray(arg0, arg1, arg2, arg3)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final boolean argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final char argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final byte argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final short argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final int argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final long argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final float argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final double argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final Object argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, argument));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG,
+ format(pattern, toArray(arg0, arg1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG,
+ format(pattern, toArray(arg0, arg1, arg2)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ * @param arg3 a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2,
+ final Object arg3) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG,
+ format(pattern, toArray(arg0, arg1, arg2, arg3)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final boolean argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final char argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final byte argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final short argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final int argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final long argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final float argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final double argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final Object argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, argument));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, toArray(arg0, arg1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern,
+ toArray(arg0, arg1, arg2)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ * @param arg3 a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2,
+ final Object arg3) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern,
+ toArray(arg0, arg1, arg2, arg3)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final boolean argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final char argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final byte argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final short argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final int argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final long argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final float argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final double argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final Object argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, argument));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN,
+ format(pattern, toArray(arg0, arg1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN,
+ format(pattern, toArray(arg0, arg1, arg2)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ * @param arg3 a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2,
+ final Object arg3) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern,
+ toArray(arg0, arg1, arg2, arg3)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param parameters parameters to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final Object[] parameters) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, parameters));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param parameters parameters to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final Throwable t,
+ final String pattern,
+ final Object[] parameters) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, parameters), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final Object param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(param1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final boolean param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final byte param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final char param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final short param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final int param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final long param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final float param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final double param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final Object arg0, final Object arg1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(arg0, arg1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specifed level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final Object arg0, final Object arg1, final Object arg2) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(arg0, arg1, arg2)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param level level, may not be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ * @param arg3 a value to be formatted and substituted.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final Object arg0, final Object arg1, final Object arg2,
+ final Object arg3) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level, format(pattern,
+ toArray(arg0, arg1, arg2, arg3)));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param parameters parameters to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final Object[] parameters) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, parameters));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param t throwable, may be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param parameters parameters to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final Throwable t,
+ final String bundleName,
+ final String key,
+ final Object[] parameters) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, parameters), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final Object param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(param1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final boolean param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final char param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final byte param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final short param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final int param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final long param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final float param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final double param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param0 Parameter to the log message.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final Object param0,
+ final Object param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(param0, param1)));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param0 Parameter to the log message.
+ * @param param1 Parameter to the log message.
+ * @param param2 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final Object param0,
+ final Object param1,
+ final Object param2) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(param0, param1, param2)));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param0 Parameter to the log message.
+ * @param param1 Parameter to the log message.
+ * @param param2 Parameter to the log message.
+ * @param param3 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final Object param0,
+ final Object param1,
+ final Object param2,
+ final Object param3) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key,
+ toArray(param0, param1, param2, param3)));
+ }
+ }
+
+}
diff --git a/java/src/org/apache/log4j/LogManager.java b/java/src/org/apache/log4j/LogManager.java
new file mode 100644
index 0000000..5b9659c
--- /dev/null
+++ b/java/src/org/apache/log4j/LogManager.java
@@ -0,0 +1,276 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.RepositorySelector;
+import org.apache.log4j.spi.DefaultRepositorySelector;
+import org.apache.log4j.spi.RootLogger;
+import org.apache.log4j.spi.NOPLoggerRepository;
+import org.apache.log4j.helpers.Loader;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.helpers.LogLog;
+
+import java.net.URL;
+import java.net.MalformedURLException;
+
+
+import java.util.Enumeration;
+import java.io.StringWriter;
+import java.io.PrintWriter;
+
+/**
+ * Use the LogManager class to retreive {@link Logger}
+ * instances or to operate on the current {@link
+ * LoggerRepository}. When the LogManager class is loaded
+ * into memory the default initalzation procedure is inititated. The
+ * default intialization procedure is described in the short log4j manual.
+ *
+ * @author Ceki Gülcü */
+public class LogManager {
+
+ /**
+ * @deprecated This variable is for internal use only. It will
+ * become package protected in future versions.
+ * */
+ static public final String DEFAULT_CONFIGURATION_FILE = "log4j.properties";
+
+ static final String DEFAULT_XML_CONFIGURATION_FILE = "log4j.xml";
+
+ /**
+ * @deprecated This variable is for internal use only. It will
+ * become private in future versions.
+ * */
+ static final public String DEFAULT_CONFIGURATION_KEY="log4j.configuration";
+
+ /**
+ * @deprecated This variable is for internal use only. It will
+ * become private in future versions.
+ * */
+ static final public String CONFIGURATOR_CLASS_KEY="log4j.configuratorClass";
+
+ /**
+ * @deprecated This variable is for internal use only. It will
+ * become private in future versions.
+ */
+ public static final String DEFAULT_INIT_OVERRIDE_KEY =
+ "log4j.defaultInitOverride";
+
+
+ static private Object guard = null;
+ static private RepositorySelector repositorySelector;
+
+ static {
+ // By default we use a DefaultRepositorySelector which always returns 'h'.
+ Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
+ repositorySelector = new DefaultRepositorySelector(h);
+
+ /** Search for the properties file log4j.properties in the CLASSPATH. */
+ String override =OptionConverter.getSystemProperty(DEFAULT_INIT_OVERRIDE_KEY,
+ null);
+
+ // if there is no default init override, then get the resource
+ // specified by the user or the default config file.
+ if(override == null || "false".equalsIgnoreCase(override)) {
+
+ String configurationOptionStr = OptionConverter.getSystemProperty(
+ DEFAULT_CONFIGURATION_KEY,
+ null);
+
+ String configuratorClassName = OptionConverter.getSystemProperty(
+ CONFIGURATOR_CLASS_KEY,
+ null);
+
+ URL url = null;
+
+ // if the user has not specified the log4j.configuration
+ // property, we search first for the file "log4j.xml" and then
+ // "log4j.properties"
+ if(configurationOptionStr == null) {
+ url = Loader.getResource(DEFAULT_XML_CONFIGURATION_FILE);
+ if(url == null) {
+ url = Loader.getResource(DEFAULT_CONFIGURATION_FILE);
+ }
+ } else {
+ try {
+ url = new URL(configurationOptionStr);
+ } catch (MalformedURLException ex) {
+ // so, resource is not a URL:
+ // attempt to get the resource from the class path
+ url = Loader.getResource(configurationOptionStr);
+ }
+ }
+
+ // If we have a non-null url, then delegate the rest of the
+ // configuration to the OptionConverter.selectAndConfigure
+ // method.
+ if(url != null) {
+ LogLog.debug("Using URL ["+url+"] for automatic log4j configuration.");
+ try {
+ OptionConverter.selectAndConfigure(url, configuratorClassName,
+ LogManager.getLoggerRepository());
+ } catch (NoClassDefFoundError e) {
+ LogLog.warn("Error during default initialization", e);
+ }
+ } else {
+ LogLog.debug("Could not find resource: ["+configurationOptionStr+"].");
+ }
+ } else {
+ LogLog.debug("Default initialization of overridden by " +
+ DEFAULT_INIT_OVERRIDE_KEY + "property.");
+ }
+ }
+
+ /**
+ Sets LoggerFactory but only if the correct
+ guard is passed as parameter.
+
+
Initally the guard is null. If the guard is
+ null, then invoking this method sets the logger
+ factory and the guard. Following invocations will throw a {@link
+ IllegalArgumentException}, unless the previously set
+ guard is passed as the second parameter.
+
+
This allows a high-level component to set the {@link
+ RepositorySelector} used by the LogManager.
+
+
For example, when tomcat starts it will be able to install its
+ own repository selector. However, if and when Tomcat is embedded
+ within JBoss, then JBoss will install its own repository selector
+ and Tomcat will use the repository selector set by its container,
+ JBoss. */
+ static
+ public
+ void setRepositorySelector(RepositorySelector selector, Object guard)
+ throws IllegalArgumentException {
+ if((LogManager.guard != null) && (LogManager.guard != guard)) {
+ throw new IllegalArgumentException(
+ "Attempted to reset the LoggerFactory without possessing the guard.");
+ }
+
+ if(selector == null) {
+ throw new IllegalArgumentException("RepositorySelector must be non-null.");
+ }
+
+ LogManager.guard = guard;
+ LogManager.repositorySelector = selector;
+ }
+
+
+ /**
+ * This method tests if called from a method that
+ * is known to result in class members being abnormally
+ * set to null but is assumed to be harmless since the
+ * all classes are in the process of being unloaded.
+ *
+ * @param ex exception used to determine calling stack.
+ * @return true if calling stack is recognized as likely safe.
+ */
+ private static boolean isLikelySafeScenario(final Exception ex) {
+ StringWriter stringWriter = new StringWriter();
+ ex.printStackTrace(new PrintWriter(stringWriter));
+ String msg = stringWriter.toString();
+ return msg.indexOf("org.apache.catalina.loader.WebappClassLoader.stop") != -1;
+ }
+
+ static
+ public
+ LoggerRepository getLoggerRepository() {
+ if (repositorySelector == null) {
+ repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository());
+ guard = null;
+ Exception ex = new IllegalStateException("Class invariant violation");
+ String msg =
+ "log4j called after unloading, see http://logging.apache.org/log4j/1.2/faq.html#unload.";
+ if (isLikelySafeScenario(ex)) {
+ LogLog.debug(msg, ex);
+ } else {
+ LogLog.error(msg, ex);
+ }
+ }
+ return repositorySelector.getLoggerRepository();
+ }
+
+ /**
+ Retrieve the appropriate root logger.
+ */
+ public
+ static
+ Logger getRootLogger() {
+ // Delegate the actual manufacturing of the logger to the logger repository.
+ return getLoggerRepository().getRootLogger();
+ }
+
+ /**
+ Retrieve the appropriate {@link Logger} instance.
+ */
+ public
+ static
+ Logger getLogger(final String name) {
+ // Delegate the actual manufacturing of the logger to the logger repository.
+ return getLoggerRepository().getLogger(name);
+ }
+
+ /**
+ Retrieve the appropriate {@link Logger} instance.
+ */
+ public
+ static
+ Logger getLogger(final Class clazz) {
+ // Delegate the actual manufacturing of the logger to the logger repository.
+ return getLoggerRepository().getLogger(clazz.getName());
+ }
+
+
+ /**
+ Retrieve the appropriate {@link Logger} instance.
+ */
+ public
+ static
+ Logger getLogger(final String name, final LoggerFactory factory) {
+ // Delegate the actual manufacturing of the logger to the logger repository.
+ return getLoggerRepository().getLogger(name, factory);
+ }
+
+ public
+ static
+ Logger exists(final String name) {
+ return getLoggerRepository().exists(name);
+ }
+
+ public
+ static
+ Enumeration getCurrentLoggers() {
+ return getLoggerRepository().getCurrentLoggers();
+ }
+
+ public
+ static
+ void shutdown() {
+ getLoggerRepository().shutdown();
+ }
+
+ public
+ static
+ void resetConfiguration() {
+ getLoggerRepository().resetConfiguration();
+ }
+}
+
diff --git a/java/src/org/apache/log4j/LogSF.java b/java/src/org/apache/log4j/LogSF.java
new file mode 100644
index 0000000..8302e20
--- /dev/null
+++ b/java/src/org/apache/log4j/LogSF.java
@@ -0,0 +1,1541 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.ResourceBundle;
+
+
+/**
+ * This class provides parameterized logging services
+ * using the SLF4J pattern syntax.
+ *
+ * Message formatting is only performed when the
+ * request exceeds the threshold level of the logger.
+ *
+ * @since 1.2.16
+ *
+ */
+public final class LogSF extends LogXF {
+ /**
+ * private constructor.
+ *
+ */
+ private LogSF() {
+ }
+
+
+
+
+ /**
+ * Formats arguments using SLF4J-like formatter.
+ * @param pattern pattern, may be malformed.
+ * @param arguments arguments.
+ * @return Message string
+ */
+ private static String format(final String pattern,
+ final Object[] arguments) {
+ if (pattern != null) {
+ String retval = "";
+ int count = 0;
+ int prev = 0;
+ int pos = pattern.indexOf("{");
+ while(pos >= 0) {
+ if (pos == 0 || pattern.charAt(pos-1) != '\\') {
+ retval += pattern.substring(prev, pos);
+ if (pos + 1 < pattern.length() && pattern.charAt(pos+1) == '}') {
+ if(arguments != null && count < arguments.length) {
+ retval += arguments[count++];
+ } else {
+ retval += "{}";
+ }
+ prev = pos + 2;
+ } else {
+ retval += "{";
+ prev = pos + 1;
+ }
+ } else {
+ retval += pattern.substring(prev, pos - 1) + "{";
+ prev = pos + 1;
+ }
+ pos = pattern.indexOf("{", prev);
+ }
+ return retval + pattern.substring(prev);
+ }
+ return null;
+ }
+
+ /**
+ * Formats arguments using MessageFormat.
+ * @param pattern pattern, may be malformed.
+ * @param arg0 argument, may be null or mismatched.
+ * @return Message string
+ */
+ private static String format(final String pattern, final Object arg0) {
+ if (pattern != null) {
+ //
+ // if there is an escaped brace, delegate to multi-param formatter
+ if (pattern.indexOf("\\{") >= 0) {
+ return format(pattern, new Object[] { arg0 });
+ }
+ int pos = pattern.indexOf("{}");
+ if (pos >= 0) {
+ return pattern.substring(0, pos) + arg0 + pattern.substring(pos+2);
+ }
+ }
+ return pattern;
+ }
+
+ /**
+ * Formats arguments using MessageFormat using a pattern from
+ * a resource bundle.
+ * @param resourceBundleName name of resource bundle, may be null.
+ * @param key key for pattern in resource bundle, may be null.
+ * @param arguments arguments, may be null or mismatched.
+ * @return Message string or null
+ */
+ private static String format(
+ final String resourceBundleName,
+ final String key,
+ final Object[] arguments) {
+ String pattern;
+ if (resourceBundleName != null) {
+ try {
+ ResourceBundle bundle =
+ ResourceBundle.getBundle(resourceBundleName);
+ pattern = bundle.getString(key);
+ } catch (Exception ex) {
+ pattern = key;
+ }
+ } else {
+ pattern = key;
+ }
+ return format(pattern, arguments);
+ }
+
+
+ /**
+ * Fully Qualified Class Name of this class.
+ */
+ private static final String FQCN = LogSF.class.getName();
+
+ /**
+ * Equivalent of Logger.forcedLog.
+ *
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param msg message, may be null.
+ */
+ private static void forcedLog(final Logger logger,
+ final Level level,
+ final String msg) {
+ logger.callAppenders(new LoggingEvent(FQCN, logger, level, msg, null));
+ }
+
+ /**
+ * Equivalent of Logger.forcedLog.
+ *
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param msg message, may be null.
+ * @param t throwable.
+ */
+ private static void forcedLog(final Logger logger,
+ final Level level,
+ final String msg,
+ final Throwable t) {
+ logger.callAppenders(new LoggingEvent(FQCN, logger, level, msg, t));
+ }
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be
+ * formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at error level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void error(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.ERROR)) {
+ forcedLog(logger, Level.ERROR, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at fatal level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void fatal(final Logger logger, final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.FATAL)) {
+ forcedLog(logger, Level.FATAL, format(pattern, arguments));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be
+ * formatted and substituted.
+ */
+ public static void trace(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, arguments), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void debug(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, arguments), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void info(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, arguments), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void warn(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, arguments), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at error level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void error(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.ERROR)) {
+ forcedLog(logger, Level.ERROR, format(pattern, arguments), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at fatal level.
+ * @param logger logger, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param arguments an array of arguments to be formatted and substituted.
+ */
+ public static void fatal(final Logger logger,
+ final Throwable t,
+ final String pattern,
+ final Object[] arguments) {
+ if (logger.isEnabledFor(Level.FATAL)) {
+ forcedLog(logger, Level.FATAL, format(pattern, arguments), t);
+ }
+ }
+
+
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final boolean argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final char argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final byte argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final short argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final int argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final long argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final float argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final double argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final Object argument) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE, format(pattern, argument));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE,
+ format(pattern, toArray(arg0, arg1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE,
+ format(pattern, toArray(arg0, arg1, arg2)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at trace level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ * @param arg3 a value to be formatted and substituted.
+ */
+ public static void trace(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2,
+ final Object arg3) {
+ if (logger.isEnabledFor(TRACE)) {
+ forcedLog(logger, TRACE,
+ format(pattern, toArray(arg0, arg1, arg2, arg3)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final boolean argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final char argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final byte argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final short argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final int argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final long argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final float argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final double argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final Object argument) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG, format(pattern, argument));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG,
+ format(pattern, toArray(arg0, arg1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG,
+ format(pattern, toArray(arg0, arg1, arg2)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at debug level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ * @param arg3 a value to be formatted and substituted.
+ */
+ public static void debug(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2,
+ final Object arg3) {
+ if (logger.isDebugEnabled()) {
+ forcedLog(logger, Level.DEBUG,
+ format(pattern, toArray(arg0, arg1, arg2, arg3)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final boolean argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final char argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final byte argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final short argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final int argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final long argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final float argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final double argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final Object argument) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, argument));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern, toArray(arg0, arg1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern,
+ toArray(arg0, arg1, arg2)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at info level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ * @param arg3 a value to be formatted and substituted.
+ */
+ public static void info(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2,
+ final Object arg3) {
+ if (logger.isInfoEnabled()) {
+ forcedLog(logger, Level.INFO, format(pattern,
+ toArray(arg0, arg1, arg2, arg3)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final boolean argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final char argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final byte argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final short argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final int argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final long argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final float argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final double argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, valueOf(argument)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param argument a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final Object argument) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern, argument));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN,
+ format(pattern, toArray(arg0, arg1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN,
+ format(pattern, toArray(arg0, arg1, arg2)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at warn level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ * @param arg3 a value to be formatted and substituted.
+ */
+ public static void warn(final Logger logger, final String pattern,
+ final Object arg0, final Object arg1, final Object arg2,
+ final Object arg3) {
+ if (logger.isEnabledFor(Level.WARN)) {
+ forcedLog(logger, Level.WARN, format(pattern,
+ toArray(arg0, arg1, arg2, arg3)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param parameters parameters to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final Object[] parameters) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, parameters));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param t throwable, may be null.
+ * @param pattern pattern, may be null.
+ * @param parameters parameters to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final Throwable t,
+ final String pattern,
+ final Object[] parameters) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, parameters), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final Object param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(param1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final boolean param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final byte param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final char param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final short param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final int param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final long param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final float param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param param1 parameter to the log message.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final double param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final Object arg0, final Object arg1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(arg0, arg1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specifed level.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param pattern pattern, may be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final Object arg0, final Object arg1, final Object arg2) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(pattern, toArray(arg0, arg1, arg2)));
+ }
+ }
+
+ /**
+ * Log a parameterized message at specified level.
+ * @param logger logger, may not be null.
+ * @param pattern pattern, may be null.
+ * @param level level, may not be null.
+ * @param arg0 a value to be formatted and substituted.
+ * @param arg1 a value to be formatted and substituted.
+ * @param arg2 a value to be formatted and substituted.
+ * @param arg3 a value to be formatted and substituted.
+ */
+ public static void log(final Logger logger,
+ final Level level,
+ final String pattern,
+ final Object arg0, final Object arg1, final Object arg2,
+ final Object arg3) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level, format(pattern,
+ toArray(arg0, arg1, arg2, arg3)));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param parameters parameters to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final Object[] parameters) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, parameters));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param t throwable, may be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param parameters parameters to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final Throwable t,
+ final String bundleName,
+ final String key,
+ final Object[] parameters) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, parameters), t);
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final Object param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(param1)));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final boolean param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final char param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final byte param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final short param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final int param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final long param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final float param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final double param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(valueOf(param1))));
+ }
+ }
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param0 Parameter to the log message.
+ * @param param1 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final Object param0,
+ final Object param1) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(param0, param1)));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param0 Parameter to the log message.
+ * @param param1 Parameter to the log message.
+ * @param param2 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final Object param0,
+ final Object param1,
+ final Object param2) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key, toArray(param0, param1, param2)));
+ }
+ }
+
+
+ /**
+ * Log a parameterized message using a pattern from a resource bundle.
+ * @param logger logger, may not be null.
+ * @param level level, may not be null.
+ * @param bundleName resource bundle name, may be null.
+ * @param key key, may be null.
+ * @param param0 Parameter to the log message.
+ * @param param1 Parameter to the log message.
+ * @param param2 Parameter to the log message.
+ * @param param3 Parameter to the log message.
+ */
+ public static void logrb(final Logger logger,
+ final Level level,
+ final String bundleName,
+ final String key,
+ final Object param0,
+ final Object param1,
+ final Object param2,
+ final Object param3) {
+ if (logger.isEnabledFor(level)) {
+ forcedLog(logger, level,
+ format(bundleName, key,
+ toArray(param0, param1, param2, param3)));
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/LogXF.java b/java/src/org/apache/log4j/LogXF.java
new file mode 100644
index 0000000..de2b94b
--- /dev/null
+++ b/java/src/org/apache/log4j/LogXF.java
@@ -0,0 +1,371 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * This is a base class for LogMF and LogSF parameterized logging classes.
+ *
+ *
+ * @see org.apache.log4j.LogMF
+ * @see org.apache.log4j.LogSF
+ * @since 1.2.16
+ */
+public abstract class LogXF {
+ /**
+ * Trace level.
+ */
+ protected static final Level TRACE = new Level(5000, "TRACE", 7);
+ /**
+ * Fully Qualified Class Name of this class.
+ */
+ private static final String FQCN = LogXF.class.getName();
+
+ protected LogXF() {
+ }
+
+ /**
+ * Returns a Boolean instance representing the specified boolean.
+ * Boolean.valueOf was added in JDK 1.4.
+ *
+ * @param b a boolean value.
+ * @return a Boolean instance representing b.
+ */
+ protected static Boolean valueOf(final boolean b) {
+ if (b) {
+ return Boolean.TRUE;
+ }
+ return Boolean.FALSE;
+ }
+
+ /**
+ * Returns a Character instance representing the specified char.
+ * Character.valueOf was added in JDK 1.5.
+ *
+ * @param c a character value.
+ * @return a Character instance representing c.
+ */
+ protected static Character valueOf(final char c) {
+ return new Character(c);
+ }
+
+ /**
+ * Returns a Byte instance representing the specified byte.
+ * Byte.valueOf was added in JDK 1.5.
+ *
+ * @param b a byte value.
+ * @return a Byte instance representing b.
+ */
+ protected static Byte valueOf(final byte b) {
+ return new Byte(b);
+ }
+
+ /**
+ * Returns a Short instance representing the specified short.
+ * Short.valueOf was added in JDK 1.5.
+ *
+ * @param b a short value.
+ * @return a Byte instance representing b.
+ */
+ protected static Short valueOf(final short b) {
+ return new Short(b);
+ }
+
+ /**
+ * Returns an Integer instance representing the specified int.
+ * Integer.valueOf was added in JDK 1.5.
+ *
+ * @param b an int value.
+ * @return an Integer instance representing b.
+ */
+ protected static Integer valueOf(final int b) {
+ return new Integer(b);
+ }
+
+ /**
+ * Returns a Long instance representing the specified long.
+ * Long.valueOf was added in JDK 1.5.
+ *
+ * @param b a long value.
+ * @return a Long instance representing b.
+ */
+ protected static Long valueOf(final long b) {
+ return new Long(b);
+ }
+
+ /**
+ * Returns a Float instance representing the specified float.
+ * Float.valueOf was added in JDK 1.5.
+ *
+ * @param b a float value.
+ * @return a Float instance representing b.
+ */
+ protected static Float valueOf(final float b) {
+ return new Float(b);
+ }
+
+ /**
+ * Returns a Double instance representing the specified double.
+ * Double.valueOf was added in JDK 1.5.
+ *
+ * @param b a double value.
+ * @return a Byte instance representing b.
+ */
+ protected static Double valueOf(final double b) {
+ return new Double(b);
+ }
+
+ /**
+ * Create new array.
+ *
+ * @param param1 parameter 1.
+ * @return new array.
+ */
+ protected static Object[] toArray(final Object param1) {
+ return new Object[]{
+ param1
+ };
+ }
+
+ /**
+ * Create new array.
+ *
+ * @param param1 parameter 1.
+ * @param param2 parameter 2.
+ * @return new array.
+ */
+ protected static Object[] toArray(final Object param1,
+ final Object param2) {
+ return new Object[]{
+ param1, param2
+ };
+ }
+
+ /**
+ * Create new array.
+ *
+ * @param param1 parameter 1.
+ * @param param2 parameter 2.
+ * @param param3 parameter 3.
+ * @return new array.
+ */
+ protected static Object[] toArray(final Object param1,
+ final Object param2,
+ final Object param3) {
+ return new Object[]{
+ param1, param2, param3
+ };
+ }
+
+ /**
+ * Create new array.
+ *
+ * @param param1 parameter 1.
+ * @param param2 parameter 2.
+ * @param param3 parameter 3.
+ * @param param4 parameter 4.
+ * @return new array.
+ */
+ protected static Object[] toArray(final Object param1,
+ final Object param2,
+ final Object param3,
+ final Object param4) {
+ return new Object[]{
+ param1, param2, param3, param4
+ };
+ }
+
+ /**
+ * Log an entering message at DEBUG level.
+ *
+ * @param logger logger, may not be null.
+ * @param sourceClass source class, may be null.
+ * @param sourceMethod method, may be null.
+ */
+ public static void entering(final Logger logger,
+ final String sourceClass,
+ final String sourceMethod) {
+ if (logger.isDebugEnabled()) {
+ logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG,
+ sourceClass + "." + sourceMethod + " ENTRY", null));
+ }
+ }
+
+ /**
+ * Log an entering message with a parameter at DEBUG level.
+ *
+ * @param logger logger, may not be null.
+ * @param sourceClass source class, may be null.
+ * @param sourceMethod method, may be null.
+ * @param param parameter, may be null.
+ */
+ public static void entering(final Logger logger,
+ final String sourceClass,
+ final String sourceMethod,
+ final String param) {
+ if (logger.isDebugEnabled()) {
+ String msg = sourceClass + "." + sourceMethod + " ENTRY " + param;
+ logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG,
+ msg, null));
+ }
+ }
+
+ /**
+ * Log an entering message with a parameter at DEBUG level.
+ *
+ * @param logger logger, may not be null.
+ * @param sourceClass source class, may be null.
+ * @param sourceMethod method, may be null.
+ * @param param parameter, may be null.
+ */
+ public static void entering(final Logger logger,
+ final String sourceClass,
+ final String sourceMethod,
+ final Object param) {
+ if (logger.isDebugEnabled()) {
+ String msg = sourceClass + "." + sourceMethod + " ENTRY ";
+ if (param == null) {
+ msg += "null";
+ } else {
+ try {
+ msg += param;
+ } catch(Throwable ex) {
+ msg += "?";
+ }
+ }
+ logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG,
+ msg, null));
+ }
+ }
+
+ /**
+ * Log an entering message with an array of parameters at DEBUG level.
+ *
+ * @param logger logger, may not be null.
+ * @param sourceClass source class, may be null.
+ * @param sourceMethod method, may be null.
+ * @param params parameters, may be null.
+ */
+ public static void entering(final Logger logger,
+ final String sourceClass,
+ final String sourceMethod,
+ final Object[] params) {
+ if (logger.isDebugEnabled()) {
+ String msg = sourceClass + "." + sourceMethod + " ENTRY ";
+ if (params != null && params.length > 0) {
+ String delim = "{";
+ for (int i = 0; i < params.length; i++) {
+ try {
+ msg += delim + params[i];
+ } catch(Throwable ex) {
+ msg += delim + "?";
+ }
+ delim = ",";
+ }
+ msg += "}";
+ } else {
+ msg += "{}";
+ }
+ logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG,
+ msg, null));
+ }
+ }
+
+ /**
+ * Log an exiting message at DEBUG level.
+ *
+ * @param logger logger, may not be null.
+ * @param sourceClass source class, may be null.
+ * @param sourceMethod method, may be null.
+ */
+ public static void exiting(final Logger logger,
+ final String sourceClass,
+ final String sourceMethod) {
+ if (logger.isDebugEnabled()) {
+ logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG,
+ sourceClass + "." + sourceMethod + " RETURN", null));
+ }
+ }
+
+ /**
+ * Log an exiting message with result at DEBUG level.
+ *
+ * @param logger logger, may not be null.
+ * @param sourceClass source class, may be null.
+ * @param sourceMethod method, may be null.
+ * @param result result, may be null.
+ */
+ public static void exiting(
+ final Logger logger,
+ final String sourceClass,
+ final String sourceMethod,
+ final String result) {
+ if (logger.isDebugEnabled()) {
+ logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG,
+ sourceClass + "." + sourceMethod + " RETURN " + result, null));
+ }
+ }
+
+ /**
+ * Log an exiting message with result at DEBUG level.
+ *
+ * @param logger logger, may not be null.
+ * @param sourceClass source class, may be null.
+ * @param sourceMethod method, may be null.
+ * @param result result, may be null.
+ */
+ public static void exiting(
+ final Logger logger,
+ final String sourceClass,
+ final String sourceMethod,
+ final Object result) {
+ if (logger.isDebugEnabled()) {
+ String msg = sourceClass + "." + sourceMethod + " RETURN ";
+ if (result == null) {
+ msg += "null";
+ } else {
+ try {
+ msg += result;
+ } catch(Throwable ex) {
+ msg += "?";
+ }
+ }
+ logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG,
+ msg, null));
+ }
+ }
+
+ /**
+ * Logs a throwing message at DEBUG level.
+ *
+ * @param logger logger, may not be null.
+ * @param sourceClass source class, may be null.
+ * @param sourceMethod method, may be null.
+ * @param thrown throwable, may be null.
+ */
+ public static void throwing(
+ final Logger logger,
+ final String sourceClass,
+ final String sourceMethod,
+ final Throwable thrown) {
+ if (logger.isDebugEnabled()) {
+ logger.callAppenders(new LoggingEvent(FQCN, logger, Level.DEBUG,
+ sourceClass + "." + sourceMethod + " THROW", thrown));
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/Logger.java b/java/src/org/apache/log4j/Logger.java
new file mode 100644
index 0000000..957145f
--- /dev/null
+++ b/java/src/org/apache/log4j/Logger.java
@@ -0,0 +1,212 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggerFactory;
+
+
+/**
+ This is the central class in the log4j package. Most logging
+ operations, except configuration, are done through this class.
+
+ @since log4j 1.2
+
+ @author Ceki Gülcü */
+public class Logger extends Category {
+
+ /**
+ The fully qualified name of the Logger class. See also the
+ getFQCN method. */
+ private static final String FQCN = Logger.class.getName();
+
+
+ protected
+ Logger(String name) {
+ super(name);
+ }
+
+ /**
+ Log a message object with the {@link Level#FINE FINE} level which
+ is just an alias for the {@link Level#DEBUG DEBUG} level.
+
+
This method first checks if this category is DEBUG
+ enabled by comparing the level of this category with the {@link
+ Level#DEBUG DEBUG} level. If this category is
+ DEBUG enabled, then it converts the message object
+ (passed as parameter) to a string by invoking the appropriate
+ {@link org.apache.log4j.or.ObjectRenderer}. It then proceeds to call all the
+ registered appenders in this category and also higher in the
+ hierarchy depending on the value of the additivity flag.
+
+
WARNING Note that passing a {@link Throwable} to this
+ method will print the name of the Throwable but no
+ stack trace. To print a stack trace use the {@link #debug(Object,
+ Throwable)} form instead.
+
+ @param message the message object to log. */
+ //public
+ //void fine(Object message) {
+ // if(repository.isDisabled(Level.DEBUG_INT))
+ // return;
+ // if(Level.DEBUG.isGreaterOrEqual(this.getChainedLevel())) {
+ // forcedLog(FQCN, Level.DEBUG, message, null);
+ // }
+ //}
+
+
+ /**
+ Log a message object with the FINE level including
+ the stack trace of the {@link Throwable} t passed as
+ parameter.
+
+
See {@link #fine(Object)} form for more detailed information.
+
+ @param message the message object to log.
+ @param t the exception to log, including its stack trace. */
+ //public
+ //void fine(Object message, Throwable t) {
+ // if(repository.isDisabled(Level.DEBUG_INT))
+ // return;
+ // if(Level.DEBUG.isGreaterOrEqual(this.getChainedLevel()))
+ // forcedLog(FQCN, Level.FINE, message, t);
+ //}
+
+ /**
+ * Retrieve a logger named according to the value of the
+ * name parameter. If the named logger already exists,
+ * then the existing instance will be returned. Otherwise, a new
+ * instance is created.
+ *
+ *
By default, loggers do not have a set level but inherit it
+ * from their neareast ancestor with a set level. This is one of the
+ * central features of log4j.
+ *
+ * @param name The name of the logger to retrieve.
+ */
+ static
+ public
+ Logger getLogger(String name) {
+ return LogManager.getLogger(name);
+ }
+
+ /**
+ * Shorthand for getLogger(clazz.getName()).
+ *
+ * @param clazz The name of clazz will be used as the
+ * name of the logger to retrieve. See {@link #getLogger(String)}
+ * for more detailed information.
+ */
+ static
+ public
+ Logger getLogger(Class clazz) {
+ return LogManager.getLogger(clazz.getName());
+ }
+
+
+ /**
+ * Return the root logger for the current logger repository.
+ *
+ * The {@link #getName Logger.getName()} method for the root logger always returns
+ * stirng value: "root". However, calling
+ * Logger.getLogger("root") does not retrieve the root
+ * logger but a logger just under root named "root".
+ *
+ * In other words, calling this method is the only way to retrieve the
+ * root logger.
+ */
+ public
+ static
+ Logger getRootLogger() {
+ return LogManager.getRootLogger();
+ }
+
+ /**
+ Like {@link #getLogger(String)} except that the type of logger
+ instantiated depends on the type returned by the {@link
+ LoggerFactory#makeNewLoggerInstance} method of the
+ factory parameter.
+
+
This method is intended to be used by sub-classes.
+
+ @param name The name of the logger to retrieve.
+
+ @param factory A {@link LoggerFactory} implementation that will
+ actually create a new Instance.
+
+ @since 0.8.5 */
+ public
+ static
+ Logger getLogger(String name, LoggerFactory factory) {
+ return LogManager.getLogger(name, factory);
+ }
+
+ /**
+ * Log a message object with the {@link org.apache.log4j.Level#TRACE TRACE} level.
+ *
+ * @param message the message object to log.
+ * @see #debug(Object) for an explanation of the logic applied.
+ * @since 1.2.12
+ */
+ public void trace(Object message) {
+ if (repository.isDisabled(Level.TRACE_INT)) {
+ return;
+ }
+
+ if (Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel())) {
+ forcedLog(FQCN, Level.TRACE, message, null);
+ }
+ }
+
+ /**
+ * Log a message object with the TRACE level including the
+ * stack trace of the {@link Throwable}t passed as parameter.
+ *
+ *
+ * See {@link #debug(Object)} form for more detailed information.
+ *
+ *
+ * @param message the message object to log.
+ * @param t the exception to log, including its stack trace.
+ * @since 1.2.12
+ */
+ public void trace(Object message, Throwable t) {
+ if (repository.isDisabled(Level.TRACE_INT)) {
+ return;
+ }
+
+ if (Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel())) {
+ forcedLog(FQCN, Level.TRACE, message, t);
+ }
+ }
+
+ /**
+ * Check whether this category is enabled for the TRACE Level.
+ * @since 1.2.12
+ *
+ * @return boolean - true if this category is enabled for level
+ * TRACE, false otherwise.
+ */
+ public boolean isTraceEnabled() {
+ if (repository.isDisabled(Level.TRACE_INT)) {
+ return false;
+ }
+
+ return Level.TRACE.isGreaterOrEqual(this.getEffectiveLevel());
+ }
+
+}
diff --git a/java/src/org/apache/log4j/MDC.java b/java/src/org/apache/log4j/MDC.java
new file mode 100644
index 0000000..5d861af
--- /dev/null
+++ b/java/src/org/apache/log4j/MDC.java
@@ -0,0 +1,187 @@
+/*
+ * 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.log4j;
+
+import java.util.Hashtable;
+import org.apache.log4j.helpers.Loader;
+import org.apache.log4j.helpers.ThreadLocalMap;
+
+/**
+ The MDC class is similar to the {@link NDC} class except that it is
+ based on a map instead of a stack. It provides mapped
+ diagnostic contexts. A Mapped Diagnostic Context, or
+ MDC in short, is an instrument for distinguishing interleaved log
+ output from different sources. Log output is typically interleaved
+ when a server handles multiple clients near-simultaneously.
+
+ The MDC is managed on a per thread basis. A
+ child thread automatically inherits a copy of the mapped
+ diagnostic context of its parent.
+
+
The MDC class requires JDK 1.2 or above. Under JDK 1.1 the MDC
+ will always return empty values but otherwise will not affect or
+ harm your application.
+
+ @since 1.2
+
+ @author Ceki Gülcü */
+public class MDC {
+
+ final static MDC mdc = new MDC();
+
+ static final int HT_SIZE = 7;
+
+ boolean java1;
+
+ Object tlm;
+
+ private
+ MDC() {
+ java1 = Loader.isJava1();
+ if(!java1) {
+ tlm = new ThreadLocalMap();
+ }
+ }
+
+ /**
+ Put a context value (the o parameter) as identified
+ with the key parameter into the current thread's
+ context map.
+
+
If the current thread does not have a context map it is
+ created as a side effect.
+
+ */
+ static
+ public
+ void put(String key, Object o) {
+ if (mdc != null) {
+ mdc.put0(key, o);
+ }
+ }
+
+ /**
+ Get the context identified by the key parameter.
+
+
This method has no side effects.
+ */
+ static
+ public
+ Object get(String key) {
+ if (mdc != null) {
+ return mdc.get0(key);
+ }
+ return null;
+ }
+
+ /**
+ Remove the the context identified by the key
+ parameter.
+
+ */
+ static
+ public
+ void remove(String key) {
+ if (mdc != null) {
+ mdc.remove0(key);
+ }
+ }
+
+
+ /**
+ * Get the current thread's MDC as a hashtable. This method is
+ * intended to be used internally.
+ * */
+ public static Hashtable getContext() {
+ if (mdc != null) {
+ return mdc.getContext0();
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * Remove all values from the MDC.
+ * @since 1.2.16
+ */
+ public static void clear() {
+ if (mdc != null) {
+ mdc.clear0();
+ }
+ }
+
+
+ private
+ void put0(String key, Object o) {
+ if(java1 || tlm == null) {
+ return;
+ } else {
+ Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
+ if(ht == null) {
+ ht = new Hashtable(HT_SIZE);
+ ((ThreadLocalMap)tlm).set(ht);
+ }
+ ht.put(key, o);
+ }
+ }
+
+ private
+ Object get0(String key) {
+ if(java1 || tlm == null) {
+ return null;
+ } else {
+ Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
+ if(ht != null && key != null) {
+ return ht.get(key);
+ } else {
+ return null;
+ }
+ }
+ }
+
+ private
+ void remove0(String key) {
+ if(!java1 && tlm != null) {
+ Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
+ if(ht != null) {
+ ht.remove(key);
+ }
+ }
+ }
+
+
+ private
+ Hashtable getContext0() {
+ if(java1 || tlm == null) {
+ return null;
+ } else {
+ return (Hashtable) ((ThreadLocalMap)tlm).get();
+ }
+ }
+
+ private
+ void clear0() {
+ if(!java1 && tlm != null) {
+ Hashtable ht = (Hashtable) ((ThreadLocalMap)tlm).get();
+ if(ht != null) {
+ ht.clear();
+ }
+ }
+ }
+
+}
diff --git a/java/src/org/apache/log4j/NDC.java b/java/src/org/apache/log4j/NDC.java
new file mode 100644
index 0000000..d374225
--- /dev/null
+++ b/java/src/org/apache/log4j/NDC.java
@@ -0,0 +1,436 @@
+/*
+ * 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.
+ */
+
+// Contributors: Dan Milstein
+// Ray Millard
+
+package org.apache.log4j;
+
+import java.util.Hashtable;
+import java.util.Stack;
+import java.util.Enumeration;
+import java.util.Vector;
+
+import org.apache.log4j.helpers.LogLog;
+
+/**
+ The NDC class implements nested diagnostic contexts as
+ defined by Neil Harrison in the article "Patterns for Logging
+ Diagnostic Messages" part of the book "Pattern Languages of
+ Program Design 3" edited by Martin et al.
+
+
A Nested Diagnostic Context, or NDC in short, is an instrument
+ to distinguish interleaved log output from different sources. Log
+ output is typically interleaved when a server handles multiple
+ clients near-simultaneously.
+
+
Interleaved log output can still be meaningful if each log entry
+ from different contexts had a distinctive stamp. This is where NDCs
+ come into play.
+
+
Note that NDCs are managed on a per thread
+ basis. NDC operations such as {@link #push push}, {@link
+ #pop}, {@link #clear}, {@link #getDepth} and {@link #setMaxDepth}
+ affect the NDC of the current thread only. NDCs of other
+ threads remain unaffected.
+
+
For example, a servlet can build a per client request NDC
+ consisting the clients host name and other information contained in
+ the the request. Cookies are another source of distinctive
+ information. To build an NDC one uses the {@link #push push}
+ operation. Simply put,
+
+
+ - Contexts can be nested.
+
+
- When entering a context, call
NDC.push. As a
+ side effect, if there is no nested diagnostic context for the
+ current thread, this method will create it.
+
+ - When leaving a context, call
NDC.pop.
+
+ - When exiting a thread make sure to call {@link #remove
+ NDC.remove()}.
+
+
+ There is no penalty for forgetting to match each
+ push operation with a corresponding pop,
+ except the obvious mismatch between the real application context
+ and the context set in the NDC.
+
+
If configured to do so, {@link PatternLayout} and {@link
+ TTCCLayout} instances automatically retrieve the nested diagnostic
+ context for the current thread without any user intervention.
+ Hence, even if a servlet is serving multiple clients
+ simultaneously, the logs emanating from the same code (belonging to
+ the same category) can still be distinguished because each client
+ request will have a different NDC tag.
+
+
Heavy duty systems should call the {@link #remove} method when
+ leaving the run method of a thread. This ensures that the memory
+ used by the thread can be freed by the Java garbage
+ collector. There is a mechanism to lazily remove references to dead
+ threads. In practice, this means that you can be a little sloppy
+ and sometimes forget to call {@link #remove} before exiting a
+ thread.
+
+
A thread may inherit the nested diagnostic context of another
+ (possibly parent) thread using the {@link #inherit inherit}
+ method. A thread may obtain a copy of its NDC with the {@link
+ #cloneStack cloneStack} method and pass the reference to any other
+ thread, in particular to a child.
+
+ @author Ceki Gülcü
+ @since 0.7.0
+
+*/
+
+public class NDC {
+
+ // The synchronized keyword is not used in this class. This may seem
+ // dangerous, especially since the class will be used by
+ // multiple-threads. In particular, all threads share the same
+ // hashtable (the "ht" variable). This is OK since java hashtables
+ // are thread safe. Same goes for Stacks.
+
+ // More importantly, when inheriting diagnostic contexts the child
+ // thread is handed a clone of the parent's NDC. It follows that
+ // each thread has its own NDC (i.e. stack).
+
+ static Hashtable ht = new Hashtable();
+
+ static int pushCounter = 0; // the number of times push has been called
+ // after the latest call to lazyRemove
+
+ // The number of times we allow push to be called before we call lazyRemove
+ // 5 is a relatively small number. As such, lazyRemove is not called too
+ // frequently. We thus avoid the cost of creating an Enumeration too often.
+ // The higher this number, the longer is the avarage period for which all
+ // logging calls in all threads are blocked.
+ static final int REAP_THRESHOLD = 5;
+
+ // No instances allowed.
+ private NDC() {}
+
+ /**
+ * Get NDC stack for current thread.
+ * @return NDC stack for current thread.
+ */
+ private static Stack getCurrentStack() {
+ if (ht != null) {
+ return (Stack) ht.get(Thread.currentThread());
+ }
+ return null;
+ }
+
+
+ /**
+ Clear any nested diagnostic information if any. This method is
+ useful in cases where the same thread can be potentially used
+ over and over in different unrelated contexts.
+
+
This method is equivalent to calling the {@link #setMaxDepth}
+ method with a zero maxDepth argument.
+
+ @since 0.8.4c */
+ public
+ static
+ void clear() {
+ Stack stack = getCurrentStack();
+ if(stack != null)
+ stack.setSize(0);
+ }
+
+
+ /**
+ Clone the diagnostic context for the current thread.
+
+
Internally a diagnostic context is represented as a stack. A
+ given thread can supply the stack (i.e. diagnostic context) to a
+ child thread so that the child can inherit the parent thread's
+ diagnostic context.
+
+
The child thread uses the {@link #inherit inherit} method to
+ inherit the parent's diagnostic context.
+
+ @return Stack A clone of the current thread's diagnostic context.
+
+ */
+ public
+ static
+ Stack cloneStack() {
+ Stack stack = getCurrentStack();
+ if(stack == null)
+ return null;
+ else {
+ return (Stack) stack.clone();
+ }
+ }
+
+
+ /**
+ Inherit the diagnostic context of another thread.
+
+
The parent thread can obtain a reference to its diagnostic
+ context using the {@link #cloneStack} method. It should
+ communicate this information to its child so that it may inherit
+ the parent's diagnostic context.
+
+
The parent's diagnostic context is cloned before being
+ inherited. In other words, once inherited, the two diagnostic
+ contexts can be managed independently.
+
+
In java, a child thread cannot obtain a reference to its
+ parent, unless it is directly handed the reference. Consequently,
+ there is no client-transparent way of inheriting diagnostic
+ contexts. Do you know any solution to this problem?
+
+ @param stack The diagnostic context of the parent thread.
+
+ */
+ public
+ static
+ void inherit(Stack stack) {
+ if(stack != null)
+ ht.put(Thread.currentThread(), stack);
+ }
+
+
+ /**
+ Never use this method directly, use the {@link
+ org.apache.log4j.spi.LoggingEvent#getNDC} method instead.
+ */
+ static
+ public
+ String get() {
+ Stack s = getCurrentStack();
+ if(s != null && !s.isEmpty())
+ return ((DiagnosticContext) s.peek()).fullMessage;
+ else
+ return null;
+ }
+
+ /**
+ * Get the current nesting depth of this diagnostic context.
+ *
+ * @see #setMaxDepth
+ * @since 0.7.5
+ */
+ public
+ static
+ int getDepth() {
+ Stack stack = getCurrentStack();
+ if(stack == null)
+ return 0;
+ else
+ return stack.size();
+ }
+
+ private
+ static
+ void lazyRemove() {
+ if (ht == null) return;
+
+ // The synchronization on ht is necessary to prevent JDK 1.2.x from
+ // throwing ConcurrentModificationExceptions at us. This sucks BIG-TIME.
+ // One solution is to write our own hashtable implementation.
+ Vector v;
+
+ synchronized(ht) {
+ // Avoid calling clean-up too often.
+ if(++pushCounter <= REAP_THRESHOLD) {
+ return; // We release the lock ASAP.
+ } else {
+ pushCounter = 0; // OK let's do some work.
+ }
+
+ int misses = 0;
+ v = new Vector();
+ Enumeration enumeration = ht.keys();
+ // We give up after 4 straigt missses. That is 4 consecutive
+ // inspected threads in 'ht' that turn out to be alive.
+ // The higher the proportion on dead threads in ht, the higher the
+ // chances of removal.
+ while(enumeration.hasMoreElements() && (misses <= 4)) {
+ Thread t = (Thread) enumeration.nextElement();
+ if(t.isAlive()) {
+ misses++;
+ } else {
+ misses = 0;
+ v.addElement(t);
+ }
+ }
+ } // synchronized
+
+ int size = v.size();
+ for(int i = 0; i < size; i++) {
+ Thread t = (Thread) v.elementAt(i);
+ LogLog.debug("Lazy NDC removal for thread [" + t.getName() + "] ("+
+ ht.size() + ").");
+ ht.remove(t);
+ }
+ }
+
+ /**
+ Clients should call this method before leaving a diagnostic
+ context.
+
+
The returned value is the value that was pushed last. If no
+ context is available, then the empty string "" is returned.
+
+ @return String The innermost diagnostic context.
+
+ */
+ public
+ static
+ String pop() {
+ Stack stack = getCurrentStack();
+ if(stack != null && !stack.isEmpty())
+ return ((DiagnosticContext) stack.pop()).message;
+ else
+ return "";
+ }
+
+ /**
+ Looks at the last diagnostic context at the top of this NDC
+ without removing it.
+
+
The returned value is the value that was pushed last. If no
+ context is available, then the empty string "" is returned.
+
+ @return String The innermost diagnostic context.
+
+ */
+ public
+ static
+ String peek() {
+ Stack stack = getCurrentStack();
+ if(stack != null && !stack.isEmpty())
+ return ((DiagnosticContext) stack.peek()).message;
+ else
+ return "";
+ }
+
+ /**
+ Push new diagnostic context information for the current thread.
+
+
The contents of the message parameter is
+ determined solely by the client.
+
+ @param message The new diagnostic context information. */
+ public
+ static
+ void push(String message) {
+ Stack stack = getCurrentStack();
+
+ if(stack == null) {
+ DiagnosticContext dc = new DiagnosticContext(message, null);
+ stack = new Stack();
+ Thread key = Thread.currentThread();
+ ht.put(key, stack);
+ stack.push(dc);
+ } else if (stack.isEmpty()) {
+ DiagnosticContext dc = new DiagnosticContext(message, null);
+ stack.push(dc);
+ } else {
+ DiagnosticContext parent = (DiagnosticContext) stack.peek();
+ stack.push(new DiagnosticContext(message, parent));
+ }
+ }
+
+ /**
+ Remove the diagnostic context for this thread.
+
+
Each thread that created a diagnostic context by calling
+ {@link #push} should call this method before exiting. Otherwise,
+ the memory used by the thread cannot be reclaimed by the
+ VM.
+
+
As this is such an important problem in heavy duty systems and
+ because it is difficult to always guarantee that the remove
+ method is called before exiting a thread, this method has been
+ augmented to lazily remove references to dead threads. In
+ practice, this means that you can be a little sloppy and
+ occasionally forget to call {@link #remove} before exiting a
+ thread. However, you must call remove sometime. If
+ you never call it, then your application is sure to run out of
+ memory.
+
+ */
+ static
+ public
+ void remove() {
+ if (ht != null) {
+ ht.remove(Thread.currentThread());
+
+ // Lazily remove dead-thread references in ht.
+ lazyRemove();
+ }
+ }
+
+ /**
+ Set maximum depth of this diagnostic context. If the current
+ depth is smaller or equal to maxDepth, then no
+ action is taken.
+
+
This method is a convenient alternative to multiple {@link
+ #pop} calls. Moreover, it is often the case that at the end of
+ complex call sequences, the depth of the NDC is
+ unpredictable. The setMaxDepth method circumvents
+ this problem.
+
+
For example, the combination
+
+ void foo() {
+ int depth = NDC.getDepth();
+
+ ... complex sequence of calls
+
+ NDC.setMaxDepth(depth);
+ }
+
+
+ ensures that between the entry and exit of foo the depth of the
+ diagnostic stack is conserved.
+
+ @see #getDepth
+ @since 0.7.5 */
+ static
+ public
+ void setMaxDepth(int maxDepth) {
+ Stack stack = getCurrentStack();
+ if(stack != null && maxDepth < stack.size())
+ stack.setSize(maxDepth);
+ }
+
+ // =====================================================================
+ private static class DiagnosticContext {
+
+ String fullMessage;
+ String message;
+
+ DiagnosticContext(String message, DiagnosticContext parent) {
+ this.message = message;
+ if(parent != null) {
+ fullMessage = parent.fullMessage + ' ' + message;
+ } else {
+ fullMessage = message;
+ }
+ }
+ }
+}
+
diff --git a/java/src/org/apache/log4j/PatternLayout.java b/java/src/org/apache/log4j/PatternLayout.java
new file mode 100644
index 0000000..d668a75
--- /dev/null
+++ b/java/src/org/apache/log4j/PatternLayout.java
@@ -0,0 +1,511 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.helpers.PatternParser;
+import org.apache.log4j.helpers.PatternConverter;
+
+
+// Contributors: Nelson Minar
+// Anders Kristensen
+
+/**
+
+ A flexible layout configurable with pattern string.
+
+ This code is known to have synchronization and other issues
+ which are not present in org.apache.log4j.EnhancedPatternLayout.
+ EnhancedPatternLayout should be used in preference to PatternLayout.
+ EnhancedPatternLayout is distributed in the log4j extras companion.
+
+ The goal of this class is to {@link #format format} a {@link
+ LoggingEvent} and return the results as a String. The results
+ depend on the conversion pattern.
+
+
The conversion pattern is closely related to the conversion
+ pattern of the printf function in C. A conversion pattern is
+ composed of literal text and format control expressions called
+ conversion specifiers.
+
+
You are free to insert any literal text within the conversion
+ pattern.
+
+
Each conversion specifier starts with a percent sign (%) and is
+ followed by optional format modifiers and a conversion
+ character. The conversion character specifies the type of
+ data, e.g. category, priority, date, thread name. The format
+ modifiers control such things as field width, padding, left and
+ right justification. The following is a simple example.
+
+
Let the conversion pattern be "%-5p [%t]: %m%n" and assume
+ that the log4j environment was set to use a PatternLayout. Then the
+ statements
+
+ Category root = Category.getRoot();
+ root.debug("Message 1");
+ root.warn("Message 2");
+
+ would yield the output
+
+ DEBUG [main]: Message 1
+ WARN [main]: Message 2
+
+
+ Note that there is no explicit separator between text and
+ conversion specifiers. The pattern parser knows when it has reached
+ the end of a conversion specifier when it reads a conversion
+ character. In the example above the conversion specifier
+ %-5p means the priority of the logging event should be left
+ justified to a width of five characters.
+
+ The recognized conversion characters are
+
+
+
+ | Conversion Character |
+ Effect |
+
+
+ | c |
+
+ Used to output the category of the logging event. The
+ category conversion specifier can be optionally followed by
+ precision specifier, that is a decimal constant in
+ brackets.
+
+ If a precision specifier is given, then only the corresponding
+ number of right most components of the category name will be
+ printed. By default the category name is printed in full.
+
+ For example, for the category name "a.b.c" the pattern
+ %c{2} will output "b.c".
+
+ |
+
+
+
+ | C |
+
+ Used to output the fully qualified class name of the caller
+ issuing the logging request. This conversion specifier
+ can be optionally followed by precision specifier, that
+ is a decimal constant in brackets.
+
+ If a precision specifier is given, then only the corresponding
+ number of right most components of the class name will be
+ printed. By default the class name is output in fully qualified form.
+
+ For example, for the class name "org.apache.xyz.SomeClass", the
+ pattern %C{1} will output "SomeClass".
+
+ WARNING Generating the caller class information is
+ slow. Thus, use should be avoided unless execution speed is
+ not an issue.
+
+ |
+
+
+ | d | Used to output the date of
+ the logging event. The date conversion specifier may be
+ followed by a date format specifier enclosed between
+ braces. For example, %d{HH:mm:ss,SSS} or
+ %d{dd MMM yyyy HH:mm:ss,SSS}. If no
+ date format specifier is given then ISO8601 format is
+ assumed.
+
+ The date format specifier admits the same syntax as the
+ time pattern string of the {@link
+ java.text.SimpleDateFormat}. Although part of the standard
+ JDK, the performance of SimpleDateFormat is
+ quite poor.
+
+ For better results it is recommended to use the log4j date
+ formatters. These can be specified using one of the strings
+ "ABSOLUTE", "DATE" and "ISO8601" for specifying {@link
+ org.apache.log4j.helpers.AbsoluteTimeDateFormat
+ AbsoluteTimeDateFormat}, {@link
+ org.apache.log4j.helpers.DateTimeDateFormat DateTimeDateFormat}
+ and respectively {@link
+ org.apache.log4j.helpers.ISO8601DateFormat
+ ISO8601DateFormat}. For example, %d{ISO8601} or
+ %d{ABSOLUTE}.
+
+ These dedicated date formatters perform significantly
+ better than {@link java.text.SimpleDateFormat}.
+ |
+
+
+
+ | F |
+
+ Used to output the file name where the logging request was
+ issued.
+
+ WARNING Generating caller location information is
+ extremely slow and should be avoided unless execution speed
+ is not an issue.
+
+ |
+
+
+ | l |
+
+ Used to output location information of the caller which generated
+ the logging event.
+
+ The location information depends on the JVM implementation but
+ usually consists of the fully qualified name of the calling
+ method followed by the callers source the file name and line
+ number between parentheses.
+
+ The location information can be very useful. However, its
+ generation is extremely slow and should be avoided
+ unless execution speed is not an issue.
+
+ |
+
+
+
+ | L |
+
+ Used to output the line number from where the logging request
+ was issued.
+
+ WARNING Generating caller location information is
+ extremely slow and should be avoided unless execution speed
+ is not an issue.
+
+ |
+
+
+
+ | m |
+ Used to output the application supplied message associated with
+ the logging event. |
+
+
+
+ | M |
+
+ Used to output the method name where the logging request was
+ issued.
+
+ WARNING Generating caller location information is
+ extremely slow and should be avoided unless execution speed
+ is not an issue.
+
+ |
+
+
+ | n |
+
+ Outputs the platform dependent line separator character or
+ characters.
+
+ This conversion character offers practically the same
+ performance as using non-portable line separator strings such as
+ "\n", or "\r\n". Thus, it is the preferred way of specifying a
+ line separator.
+
+
+ |
+
+
+ | p |
+ Used to output the priority of the logging event. |
+
+
+
+
+ | r |
+
+ Used to output the number of milliseconds elapsed from the construction
+ of the layout until the creation of the logging event. |
+
+
+
+
+ | t |
+
+ Used to output the name of the thread that generated the
+ logging event. |
+
+
+
+
+
+ | x |
+
+ Used to output the NDC (nested diagnostic context) associated
+ with the thread that generated the logging event.
+ |
+
+
+
+
+ | X |
+
+
+
+ Used to output the MDC (mapped diagnostic context) associated
+ with the thread that generated the logging event. The X
+ conversion character must be followed by the key for the
+ map placed between braces, as in %X{clientNumber} where
+ clientNumber is the key. The value in the MDC
+ corresponding to the key will be output.
+
+ See {@link MDC} class for more details.
+
+
+ |
+
+
+
+
+ | % |
+
+ The sequence %% outputs a single percent sign.
+ |
+
+
+
+
+ By default the relevant information is output as is. However,
+ with the aid of format modifiers it is possible to change the
+ minimum field width, the maximum field width and justification.
+
+
The optional format modifier is placed between the percent sign
+ and the conversion character.
+
+
The first optional format modifier is the left justification
+ flag which is just the minus (-) character. Then comes the
+ optional minimum field width modifier. This is a decimal
+ constant that represents the minimum number of characters to
+ output. If the data item requires fewer characters, it is padded on
+ either the left or the right until the minimum width is
+ reached. The default is to pad on the left (right justify) but you
+ can specify right padding with the left justification flag. The
+ padding character is space. If the data item is larger than the
+ minimum field width, the field is expanded to accommodate the
+ data. The value is never truncated.
+
+
This behavior can be changed using the maximum field
+ width modifier which is designated by a period followed by a
+ decimal constant. If the data item is longer than the maximum
+ field, then the extra characters are removed from the
+ beginning of the data item and not from the end. For
+ example, it the maximum field width is eight and the data item is
+ ten characters long, then the first two characters of the data item
+ are dropped. This behavior deviates from the printf function in C
+ where truncation is done from the end.
+
+
Below are various format modifier examples for the category
+ conversion specifier.
+
+
+
+ | Format modifier
+ | left justify
+ | minimum width
+ | maximum width
+ | comment
+
+ |
+ | %20c |
+ false |
+ 20 |
+ none |
+
+ Left pad with spaces if the category name is less than 20
+ characters long.
+
+ |
| %-20c | true | 20 | none | Right pad with
+ spaces if the category name is less than 20 characters long.
+
+ |
+ | %.30c |
+ NA |
+ none |
+ 30 |
+
+ Truncate from the beginning if the category name is longer than 30
+ characters.
+
+ |
+ | %20.30c |
+ false |
+ 20 |
+ 30 |
+
+ Left pad with spaces if the category name is shorter than 20
+ characters. However, if category name is longer than 30 characters,
+ then truncate from the beginning.
+
+ |
+ | %-20.30c |
+ true |
+ 20 |
+ 30 |
+
+ Right pad with spaces if the category name is shorter than 20
+ characters. However, if category name is longer than 30 characters,
+ then truncate from the beginning.
+
+ |
+
+ Below are some examples of conversion patterns.
+
+
+
+ - %r [%t] %-5p %c %x - %m%n
+
- This is essentially the TTCC layout.
+
+
- %-6r [%15.15t] %-5p %30.30c %x - %m%n
+
+
- Similar to the TTCC layout except that the relative time is
+ right padded if less than 6 digits, thread name is right padded if
+ less than 15 characters and truncated if longer and the category
+ name is left padded if shorter than 30 characters and truncated if
+ longer.
+
+
+
+ The above text is largely inspired from Peter A. Darnell and
+ Philip E. Margolis' highly recommended book "C -- a Software
+ Engineering Approach", ISBN 0-387-97389-3.
+
+ @author James P. Cakalic
+ @author Ceki Gülcü
+
+
+ @since 0.8.2 */
+public class PatternLayout extends Layout {
+
+
+ /** Default pattern string for log output. Currently set to the
+ string "%m%n" which just prints the application supplied
+ message. */
+ public final static String DEFAULT_CONVERSION_PATTERN ="%m%n";
+
+ /** A conversion pattern equivalent to the TTCCCLayout.
+ Current value is %r [%t] %p %c %x - %m%n. */
+ public final static String TTCC_CONVERSION_PATTERN
+ = "%r [%t] %p %c %x - %m%n";
+
+
+ protected final int BUF_SIZE = 256;
+ protected final int MAX_CAPACITY = 1024;
+
+
+ // output buffer appended to when format() is invoked
+ private StringBuffer sbuf = new StringBuffer(BUF_SIZE);
+
+ private String pattern;
+
+ private PatternConverter head;
+
+ /**
+ Constructs a PatternLayout using the DEFAULT_LAYOUT_PATTERN.
+
+ The default pattern just produces the application supplied message.
+ */
+ public PatternLayout() {
+ this(DEFAULT_CONVERSION_PATTERN);
+ }
+
+ /**
+ Constructs a PatternLayout using the supplied conversion pattern.
+ */
+ public PatternLayout(String pattern) {
+ this.pattern = pattern;
+ head = createPatternParser((pattern == null) ? DEFAULT_CONVERSION_PATTERN :
+ pattern).parse();
+ }
+
+ /**
+ Set the ConversionPattern option. This is the string which
+ controls formatting and consists of a mix of literal content and
+ conversion specifiers.
+ */
+ public
+ void setConversionPattern(String conversionPattern) {
+ pattern = conversionPattern;
+ head = createPatternParser(conversionPattern).parse();
+ }
+
+ /**
+ Returns the value of the ConversionPattern option.
+ */
+ public
+ String getConversionPattern() {
+ return pattern;
+ }
+
+ /**
+ Does not do anything as options become effective
+ */
+ public
+ void activateOptions() {
+ // nothing to do.
+ }
+
+ /**
+ The PatternLayout does not handle the throwable contained within
+ {@link LoggingEvent LoggingEvents}. Thus, it returns
+ true.
+
+ @since 0.8.4 */
+ public
+ boolean ignoresThrowable() {
+ return true;
+ }
+
+ /**
+ Returns PatternParser used to parse the conversion string. Subclasses
+ may override this to return a subclass of PatternParser which recognize
+ custom conversion characters.
+
+ @since 0.9.0
+ */
+ protected PatternParser createPatternParser(String pattern) {
+ return new PatternParser(pattern);
+ }
+
+
+ /**
+ Produces a formatted string as specified by the conversion pattern.
+ */
+ public String format(LoggingEvent event) {
+ // Reset working stringbuffer
+ if(sbuf.capacity() > MAX_CAPACITY) {
+ sbuf = new StringBuffer(BUF_SIZE);
+ } else {
+ sbuf.setLength(0);
+ }
+
+ PatternConverter c = head;
+
+ while(c != null) {
+ c.format(sbuf, event);
+ c = c.next;
+ }
+ return sbuf.toString();
+ }
+}
diff --git a/java/src/org/apache/log4j/Priority.java b/java/src/org/apache/log4j/Priority.java
new file mode 100644
index 0000000..53e94b4
--- /dev/null
+++ b/java/src/org/apache/log4j/Priority.java
@@ -0,0 +1,193 @@
+/*
+ * 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.
+ */
+
+// Contributors: Kitching Simon
+
+package org.apache.log4j;
+
+/**
+ Refrain from using this class directly, use
+ the {@link Level} class instead.
+
+ @author Ceki Gülcü */
+public class Priority {
+
+ transient int level;
+ transient String levelStr;
+ transient int syslogEquivalent;
+
+ public final static int OFF_INT = Integer.MAX_VALUE;
+ public final static int FATAL_INT = 50000;
+ public final static int ERROR_INT = 40000;
+ public final static int WARN_INT = 30000;
+ public final static int INFO_INT = 20000;
+ public final static int DEBUG_INT = 10000;
+ //public final static int FINE_INT = DEBUG_INT;
+ public final static int ALL_INT = Integer.MIN_VALUE;
+
+ /**
+ * @deprecated Use {@link Level#FATAL} instead.
+ */
+ final static public Priority FATAL = new Level(FATAL_INT, "FATAL", 0);
+
+ /**
+ * @deprecated Use {@link Level#ERROR} instead.
+ */
+ final static public Priority ERROR = new Level(ERROR_INT, "ERROR", 3);
+
+ /**
+ * @deprecated Use {@link Level#WARN} instead.
+ */
+ final static public Priority WARN = new Level(WARN_INT, "WARN", 4);
+
+ /**
+ * @deprecated Use {@link Level#INFO} instead.
+ */
+ final static public Priority INFO = new Level(INFO_INT, "INFO", 6);
+
+ /**
+ * @deprecated Use {@link Level#DEBUG} instead.
+ */
+ final static public Priority DEBUG = new Level(DEBUG_INT, "DEBUG", 7);
+
+
+ /**
+ * Default constructor for deserialization.
+ */
+ protected Priority() {
+ level = DEBUG_INT;
+ levelStr = "DEBUG";
+ syslogEquivalent = 7;
+ }
+
+ /**
+ Instantiate a level object.
+ */
+ protected
+ Priority(int level, String levelStr, int syslogEquivalent) {
+ this.level = level;
+ this.levelStr = levelStr;
+ this.syslogEquivalent = syslogEquivalent;
+ }
+
+ /**
+ Two priorities are equal if their level fields are equal.
+ @since 1.2
+ */
+ public
+ boolean equals(Object o) {
+ if(o instanceof Priority) {
+ Priority r = (Priority) o;
+ return (this.level == r.level);
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ Return the syslog equivalent of this priority as an integer.
+ */
+ public
+ final
+ int getSyslogEquivalent() {
+ return syslogEquivalent;
+ }
+
+
+
+ /**
+ Returns true if this level has a higher or equal
+ level than the level passed as argument, false
+ otherwise.
+
+ You should think twice before overriding the default
+ implementation of isGreaterOrEqual method.
+
+ */
+ public
+ boolean isGreaterOrEqual(Priority r) {
+ return level >= r.level;
+ }
+
+ /**
+ Return all possible priorities as an array of Level objects in
+ descending order.
+
+ @deprecated This method will be removed with no replacement.
+ */
+ public
+ static
+ Priority[] getAllPossiblePriorities() {
+ return new Priority[] {Priority.FATAL, Priority.ERROR, Level.WARN,
+ Priority.INFO, Priority.DEBUG};
+ }
+
+
+ /**
+ Returns the string representation of this priority.
+ */
+ final
+ public
+ String toString() {
+ return levelStr;
+ }
+
+ /**
+ Returns the integer representation of this level.
+ */
+ public
+ final
+ int toInt() {
+ return level;
+ }
+
+ /**
+ * @deprecated Please use the {@link Level#toLevel(String)} method instead.
+ */
+ public
+ static
+ Priority toPriority(String sArg) {
+ return Level.toLevel(sArg);
+ }
+
+ /**
+ * @deprecated Please use the {@link Level#toLevel(int)} method instead.
+ */
+ public
+ static
+ Priority toPriority(int val) {
+ return toPriority(val, Priority.DEBUG);
+ }
+
+ /**
+ * @deprecated Please use the {@link Level#toLevel(int, Level)} method instead.
+ */
+ public
+ static
+ Priority toPriority(int val, Priority defaultPriority) {
+ return Level.toLevel(val, (Level) defaultPriority);
+ }
+
+ /**
+ * @deprecated Please use the {@link Level#toLevel(String, Level)} method instead.
+ */
+ public
+ static
+ Priority toPriority(String sArg, Priority defaultPriority) {
+ return Level.toLevel(sArg, (Level) defaultPriority);
+ }
+}
diff --git a/java/src/org/apache/log4j/PropertyConfigurator.java b/java/src/org/apache/log4j/PropertyConfigurator.java
new file mode 100644
index 0000000..5672344
--- /dev/null
+++ b/java/src/org/apache/log4j/PropertyConfigurator.java
@@ -0,0 +1,963 @@
+/*
+ * 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.
+ */
+
+
+// Contibutors: "Luke Blanshard"
+// "Mark DONSZELMANN"
+// Anders Kristensen
+
+package org.apache.log4j;
+
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.net.URLConnection;
+import java.util.Enumeration;
+import java.util.Hashtable;
+import java.util.Properties;
+import java.util.StringTokenizer;
+import java.util.Vector;
+import java.util.Iterator;
+import java.util.Map;
+
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.FileWatchdog;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.or.RendererMap;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.apache.log4j.spi.ErrorHandler;
+
+/**
+ Allows the configuration of log4j from an external file. See
+ {@link #doConfigure(String, LoggerRepository)} for the
+ expected format.
+
+ It is sometimes useful to see how log4j is reading configuration
+ files. You can enable log4j internal logging by defining the
+ log4j.debug variable.
+
+
As of log4j version 0.8.5, at class initialization time class,
+ the file log4j.properties will be searched from the search
+ path used to load classes. If the file can be found, then it will
+ be fed to the {@link PropertyConfigurator#configure(java.net.URL)}
+ method.
+
+
The PropertyConfigurator does not handle the
+ advanced configuration features supported by the {@link
+ org.apache.log4j.xml.DOMConfigurator DOMConfigurator} such as
+ support custom {@link org.apache.log4j.spi.ErrorHandler ErrorHandlers},
+ nested appenders such as the {@link org.apache.log4j.AsyncAppender
+ AsyncAppender}, etc.
+
+
All option values admit variable substitution. The
+ syntax of variable substitution is similar to that of Unix
+ shells. The string between an opening "${" and
+ closing "}" is interpreted as a key. The value of
+ the substituted variable can be defined as a system property or in
+ the configuration file itself. The value of the key is first
+ searched in the system properties, and if not found there, it is
+ then searched in the configuration file being parsed. The
+ corresponding value replaces the ${variableName} sequence. For
+ example, if java.home system property is set to
+ /home/xyz, then every occurrence of the sequence
+ ${java.home} will be interpreted as
+ /home/xyz.
+
+
+ @author Ceki Gülcü
+ @author Anders Kristensen
+ @since 0.8.1 */
+public class PropertyConfigurator implements Configurator {
+
+ /**
+ Used internally to keep track of configured appenders.
+ */
+ protected Hashtable registry = new Hashtable(11);
+ private LoggerRepository repository;
+ protected LoggerFactory loggerFactory = new DefaultCategoryFactory();
+
+ static final String CATEGORY_PREFIX = "log4j.category.";
+ static final String LOGGER_PREFIX = "log4j.logger.";
+ static final String FACTORY_PREFIX = "log4j.factory";
+ static final String ADDITIVITY_PREFIX = "log4j.additivity.";
+ static final String ROOT_CATEGORY_PREFIX = "log4j.rootCategory";
+ static final String ROOT_LOGGER_PREFIX = "log4j.rootLogger";
+ static final String APPENDER_PREFIX = "log4j.appender.";
+ static final String RENDERER_PREFIX = "log4j.renderer.";
+ static final String THRESHOLD_PREFIX = "log4j.threshold";
+ private static final String THROWABLE_RENDERER_PREFIX = "log4j.throwableRenderer";
+ private static final String LOGGER_REF = "logger-ref";
+ private static final String ROOT_REF = "root-ref";
+ private static final String APPENDER_REF_TAG = "appender-ref";
+
+
+ /** Key for specifying the {@link org.apache.log4j.spi.LoggerFactory
+ LoggerFactory}. Currently set to "log4j.loggerFactory". */
+ public static final String LOGGER_FACTORY_KEY = "log4j.loggerFactory";
+
+ /**
+ * If property set to true, then hierarchy will be reset before configuration.
+ */
+ private static final String RESET_KEY = "log4j.reset";
+
+ static final private String INTERNAL_ROOT_NAME = "root";
+
+ /**
+ Read configuration from a file. The existing configuration is
+ not cleared nor reset. If you require a different behavior,
+ then call {@link LogManager#resetConfiguration
+ resetConfiguration} method before calling
+ doConfigure.
+
+
The configuration file consists of statements in the format
+ key=value. The syntax of different configuration
+ elements are discussed below.
+
+
Repository-wide threshold
+
+ The repository-wide threshold filters logging requests by level
+ regardless of logger. The syntax is:
+
+
+ log4j.threshold=[level]
+
+
+ The level value can consist of the string values OFF, FATAL,
+ ERROR, WARN, INFO, DEBUG, ALL or a custom level value. A
+ custom level value can be specified in the form
+ level#classname. By default the repository-wide threshold is set
+ to the lowest possible value, namely the level ALL.
+
+
+
+ Appender configuration
+
+ Appender configuration syntax is:
+
+ # For appender named appenderName, set its class.
+ # Note: The appender name can contain dots.
+ log4j.appender.appenderName=fully.qualified.name.of.appender.class
+
+ # Set appender specific options.
+ log4j.appender.appenderName.option1=value1
+ ...
+ log4j.appender.appenderName.optionN=valueN
+
+
+ For each named appender you can configure its {@link Layout}. The
+ syntax for configuring an appender's layout is:
+
+ log4j.appender.appenderName.layout=fully.qualified.name.of.layout.class
+ log4j.appender.appenderName.layout.option1=value1
+ ....
+ log4j.appender.appenderName.layout.optionN=valueN
+
+
+ The syntax for adding {@link Filter}s to an appender is:
+
+ log4j.appender.appenderName.filter.ID=fully.qualified.name.of.filter.class
+ log4j.appender.appenderName.filter.ID.option1=value1
+ ...
+ log4j.appender.appenderName.filter.ID.optionN=valueN
+
+ The first line defines the class name of the filter identified by ID;
+ subsequent lines with the same ID specify filter option - value
+ paris. Multiple filters are added to the appender in the lexicographic
+ order of IDs.
+
+ The syntax for adding an {@link ErrorHandler} to an appender is:
+
+ log4j.appender.appenderName.errorhandler=fully.qualified.name.of.filter.class
+ log4j.appender.appenderName.errorhandler.root-ref={true|false}
+ log4j.appender.appenderName.errorhandler.logger-ref=loggerName
+ log4j.appender.appenderName.errorhandler.appender-ref=appenderName
+ log4j.appender.appenderName.errorhandler.option1=value1
+ ...
+ log4j.appender.appenderName.errorhandler.optionN=valueN
+
+
+ Configuring loggers
+
+ The syntax for configuring the root logger is:
+
+ log4j.rootLogger=[level], appenderName, appenderName, ...
+
+
+ This syntax means that an optional level can be
+ supplied followed by appender names separated by commas.
+
+
The level value can consist of the string values OFF, FATAL,
+ ERROR, WARN, INFO, DEBUG, ALL or a custom level value. A
+ custom level value can be specified in the form
+ level#classname.
+
+
If a level value is specified, then the root level is set
+ to the corresponding level. If no level value is specified,
+ then the root level remains untouched.
+
+
The root logger can be assigned multiple appenders.
+
+
Each appenderName (separated by commas) will be added to
+ the root logger. The named appender is defined using the
+ appender syntax defined above.
+
+
For non-root categories the syntax is almost the same:
+
+ log4j.logger.logger_name=[level|INHERITED|NULL], appenderName, appenderName, ...
+
+
+ The meaning of the optional level value is discussed above
+ in relation to the root logger. In addition however, the value
+ INHERITED can be specified meaning that the named logger should
+ inherit its level from the logger hierarchy.
+
+
If no level value is supplied, then the level of the
+ named logger remains untouched.
+
+
By default categories inherit their level from the
+ hierarchy. However, if you set the level of a logger and later
+ decide that that logger should inherit its level, then you should
+ specify INHERITED as the value for the level value. NULL is a
+ synonym for INHERITED.
+
+
Similar to the root logger syntax, each appenderName
+ (separated by commas) will be attached to the named logger.
+
+
See the appender
+ additivity rule in the user manual for the meaning of the
+ additivity flag.
+
+
ObjectRenderers
+
+ You can customize the way message objects of a given type are
+ converted to String before being logged. This is done by
+ specifying an {@link org.apache.log4j.or.ObjectRenderer ObjectRenderer}
+ for the object type would like to customize.
+
+ The syntax is:
+
+
+ log4j.renderer.fully.qualified.name.of.rendered.class=fully.qualified.name.of.rendering.class
+
+
+ As in,
+
+ log4j.renderer.my.Fruit=my.FruitRenderer
+
+
+ ThrowableRenderer
+
+ You can customize the way an instance of Throwable is
+ converted to String before being logged. This is done by
+ specifying an {@link org.apache.log4j.spi.ThrowableRenderer ThrowableRenderer}.
+
+ The syntax is:
+
+
+ log4j.throwableRenderer=fully.qualified.name.of.rendering.class
+ log4j.throwableRenderer.paramName=paramValue
+
+
+ As in,
+
+ log4j.throwableRenderer=org.apache.log4j.EnhancedThrowableRenderer
+
+
+ Logger Factories
+
+ The usage of custom logger factories is discouraged and no longer
+ documented.
+
+ Resetting Hierarchy
+
+ The hierarchy will be reset before configuration when
+ log4j.reset=true is present in the properties file.
+
+ Example
+
+ An example configuration is given below. Other configuration
+ file examples are given in the examples folder.
+
+
+
+ # Set options for appender named "A1".
+ # Appender "A1" will be a SyslogAppender
+ log4j.appender.A1=org.apache.log4j.net.SyslogAppender
+
+ # The syslog daemon resides on www.abc.net
+ log4j.appender.A1.SyslogHost=www.abc.net
+
+ # A1's layout is a PatternLayout, using the conversion pattern
+ # %r %-5p %c{2} %M.%L %x - %m\n. Thus, the log output will
+ # include # the relative time since the start of the application in
+ # milliseconds, followed by the level of the log request,
+ # followed by the two rightmost components of the logger name,
+ # followed by the callers method name, followed by the line number,
+ # the nested disgnostic context and finally the message itself.
+ # Refer to the documentation of {@link PatternLayout} for further information
+ # on the syntax of the ConversionPattern key.
+ log4j.appender.A1.layout=org.apache.log4j.PatternLayout
+ log4j.appender.A1.layout.ConversionPattern=%-4r %-5p %c{2} %M.%L %x - %m\n
+
+ # Set options for appender named "A2"
+ # A2 should be a RollingFileAppender, with maximum file size of 10 MB
+ # using at most one backup file. A2's layout is TTCC, using the
+ # ISO8061 date format with context printing enabled.
+ log4j.appender.A2=org.apache.log4j.RollingFileAppender
+ log4j.appender.A2.MaxFileSize=10MB
+ log4j.appender.A2.MaxBackupIndex=1
+ log4j.appender.A2.layout=org.apache.log4j.TTCCLayout
+ log4j.appender.A2.layout.ContextPrinting=enabled
+ log4j.appender.A2.layout.DateFormat=ISO8601
+
+ # Root logger set to DEBUG using the A2 appender defined above.
+ log4j.rootLogger=DEBUG, A2
+
+ # Logger definitions:
+ # The SECURITY logger inherits is level from root. However, it's output
+ # will go to A1 appender defined above. It's additivity is non-cumulative.
+ log4j.logger.SECURITY=INHERIT, A1
+ log4j.additivity.SECURITY=false
+
+ # Only warnings or above will be logged for the logger "SECURITY.access".
+ # Output will go to A1.
+ log4j.logger.SECURITY.access=WARN
+
+
+ # The logger "class.of.the.day" inherits its level from the
+ # logger hierarchy. Output will go to the appender's of the root
+ # logger, A2 in this case.
+ log4j.logger.class.of.the.day=INHERIT
+
+
+ Refer to the setOption method in each Appender and
+ Layout for class specific options.
+
+
Use the # or ! characters at the
+ beginning of a line for comments.
+
+
+ @param configFileName The name of the configuration file where the
+ configuration information is stored.
+
+ */
+ public
+ void doConfigure(String configFileName, LoggerRepository hierarchy) {
+ Properties props = new Properties();
+ FileInputStream istream = null;
+ try {
+ istream = new FileInputStream(configFileName);
+ props.load(istream);
+ istream.close();
+ }
+ catch (Exception e) {
+ if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not read configuration file ["+configFileName+"].", e);
+ LogLog.error("Ignoring configuration file [" + configFileName+"].");
+ return;
+ } finally {
+ if(istream != null) {
+ try {
+ istream.close();
+ } catch(InterruptedIOException ignore) {
+ Thread.currentThread().interrupt();
+ } catch(Throwable ignore) {
+ }
+
+ }
+ }
+ // If we reach here, then the config file is alright.
+ doConfigure(props, hierarchy);
+ }
+
+ /**
+ */
+ static
+ public
+ void configure(String configFilename) {
+ new PropertyConfigurator().doConfigure(configFilename,
+ LogManager.getLoggerRepository());
+ }
+
+ /**
+ Read configuration options from url configURL.
+
+ @since 0.8.2
+ */
+ public
+ static
+ void configure(java.net.URL configURL) {
+ new PropertyConfigurator().doConfigure(configURL,
+ LogManager.getLoggerRepository());
+ }
+
+
+ /**
+ Read configuration options from properties.
+
+ See {@link #doConfigure(String, LoggerRepository)} for the expected format.
+ */
+ static
+ public
+ void configure(Properties properties) {
+ new PropertyConfigurator().doConfigure(properties,
+ LogManager.getLoggerRepository());
+ }
+
+ /**
+ Like {@link #configureAndWatch(String, long)} except that the
+ default delay as defined by {@link FileWatchdog#DEFAULT_DELAY} is
+ used.
+
+ @param configFilename A file in key=value format.
+
+ */
+ static
+ public
+ void configureAndWatch(String configFilename) {
+ configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
+ }
+
+
+ /**
+ Read the configuration file configFilename if it
+ exists. Moreover, a thread will be created that will periodically
+ check if configFilename has been created or
+ modified. The period is determined by the delay
+ argument. If a change or file creation is detected, then
+ configFilename is read to configure log4j.
+
+ @param configFilename A file in key=value format.
+ @param delay The delay in milliseconds to wait between each check.
+ */
+ static
+ public
+ void configureAndWatch(String configFilename, long delay) {
+ PropertyWatchdog pdog = new PropertyWatchdog(configFilename);
+ pdog.setDelay(delay);
+ pdog.start();
+ }
+
+
+ /**
+ Read configuration options from properties.
+
+ See {@link #doConfigure(String, LoggerRepository)} for the expected format.
+ */
+ public
+ void doConfigure(Properties properties, LoggerRepository hierarchy) {
+ repository = hierarchy;
+ String value = properties.getProperty(LogLog.DEBUG_KEY);
+ if(value == null) {
+ value = properties.getProperty("log4j.configDebug");
+ if(value != null)
+ LogLog.warn("[log4j.configDebug] is deprecated. Use [log4j.debug] instead.");
+ }
+
+ if(value != null) {
+ LogLog.setInternalDebugging(OptionConverter.toBoolean(value, true));
+ }
+
+ //
+ // if log4j.reset=true then
+ // reset hierarchy
+ String reset = properties.getProperty(RESET_KEY);
+ if (reset != null && OptionConverter.toBoolean(reset, false)) {
+ hierarchy.resetConfiguration();
+ }
+
+ String thresholdStr = OptionConverter.findAndSubst(THRESHOLD_PREFIX,
+ properties);
+ if(thresholdStr != null) {
+ hierarchy.setThreshold(OptionConverter.toLevel(thresholdStr,
+ (Level) Level.ALL));
+ LogLog.debug("Hierarchy threshold set to ["+hierarchy.getThreshold()+"].");
+ }
+
+ configureRootCategory(properties, hierarchy);
+ configureLoggerFactory(properties);
+ parseCatsAndRenderers(properties, hierarchy);
+
+ LogLog.debug("Finished configuring.");
+ // We don't want to hold references to appenders preventing their
+ // garbage collection.
+ registry.clear();
+ }
+
+ /**
+ Read configuration options from url configURL.
+ */
+ public
+ void doConfigure(java.net.URL configURL, LoggerRepository hierarchy) {
+ Properties props = new Properties();
+ LogLog.debug("Reading configuration from URL " + configURL);
+ InputStream istream = null;
+ URLConnection uConn = null;
+ try {
+ uConn = configURL.openConnection();
+ uConn.setUseCaches(false);
+ istream = uConn.getInputStream();
+ props.load(istream);
+ }
+ catch (Exception e) {
+ if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not read configuration file from URL [" + configURL
+ + "].", e);
+ LogLog.error("Ignoring configuration file [" + configURL +"].");
+ return;
+ }
+ finally {
+ if (istream != null) {
+ try {
+ istream.close();
+ } catch(InterruptedIOException ignore) {
+ Thread.currentThread().interrupt();
+ } catch(IOException ignore) {
+ } catch(RuntimeException ignore) {
+ }
+ }
+ }
+ doConfigure(props, hierarchy);
+ }
+
+
+ // --------------------------------------------------------------------------
+ // Internal stuff
+ // --------------------------------------------------------------------------
+
+ /**
+ Check the provided Properties object for a
+ {@link org.apache.log4j.spi.LoggerFactory LoggerFactory}
+ entry specified by {@link #LOGGER_FACTORY_KEY}. If such an entry
+ exists, an attempt is made to create an instance using the default
+ constructor. This instance is used for subsequent Category creations
+ within this configurator.
+
+ @see #parseCatsAndRenderers
+ */
+ protected void configureLoggerFactory(Properties props) {
+ String factoryClassName = OptionConverter.findAndSubst(LOGGER_FACTORY_KEY,
+ props);
+ if(factoryClassName != null) {
+ LogLog.debug("Setting category factory to ["+factoryClassName+"].");
+ loggerFactory = (LoggerFactory)
+ OptionConverter.instantiateByClassName(factoryClassName,
+ LoggerFactory.class,
+ loggerFactory);
+ PropertySetter.setProperties(loggerFactory, props, FACTORY_PREFIX + ".");
+ }
+ }
+
+ /*
+ void configureOptionHandler(OptionHandler oh, String prefix,
+ Properties props) {
+ String[] options = oh.getOptionStrings();
+ if(options == null)
+ return;
+
+ String value;
+ for(int i = 0; i < options.length; i++) {
+ value = OptionConverter.findAndSubst(prefix + options[i], props);
+ LogLog.debug(
+ "Option " + options[i] + "=[" + (value == null? "N/A" : value)+"].");
+ // Some option handlers assume that null value are not passed to them.
+ // So don't remove this check
+ if(value != null) {
+ oh.setOption(options[i], value);
+ }
+ }
+ oh.activateOptions();
+ }
+ */
+
+
+ void configureRootCategory(Properties props, LoggerRepository hierarchy) {
+ String effectiveFrefix = ROOT_LOGGER_PREFIX;
+ String value = OptionConverter.findAndSubst(ROOT_LOGGER_PREFIX, props);
+
+ if(value == null) {
+ value = OptionConverter.findAndSubst(ROOT_CATEGORY_PREFIX, props);
+ effectiveFrefix = ROOT_CATEGORY_PREFIX;
+ }
+
+ if(value == null)
+ LogLog.debug("Could not find root logger information. Is this OK?");
+ else {
+ Logger root = hierarchy.getRootLogger();
+ synchronized(root) {
+ parseCategory(props, root, effectiveFrefix, INTERNAL_ROOT_NAME, value);
+ }
+ }
+ }
+
+
+ /**
+ Parse non-root elements, such non-root categories and renderers.
+ */
+ protected
+ void parseCatsAndRenderers(Properties props, LoggerRepository hierarchy) {
+ Enumeration enumeration = props.propertyNames();
+ while(enumeration.hasMoreElements()) {
+ String key = (String) enumeration.nextElement();
+ if(key.startsWith(CATEGORY_PREFIX) || key.startsWith(LOGGER_PREFIX)) {
+ String loggerName = null;
+ if(key.startsWith(CATEGORY_PREFIX)) {
+ loggerName = key.substring(CATEGORY_PREFIX.length());
+ } else if(key.startsWith(LOGGER_PREFIX)) {
+ loggerName = key.substring(LOGGER_PREFIX.length());
+ }
+ String value = OptionConverter.findAndSubst(key, props);
+ Logger logger = hierarchy.getLogger(loggerName, loggerFactory);
+ synchronized(logger) {
+ parseCategory(props, logger, key, loggerName, value);
+ parseAdditivityForLogger(props, logger, loggerName);
+ }
+ } else if(key.startsWith(RENDERER_PREFIX)) {
+ String renderedClass = key.substring(RENDERER_PREFIX.length());
+ String renderingClass = OptionConverter.findAndSubst(key, props);
+ if(hierarchy instanceof RendererSupport) {
+ RendererMap.addRenderer((RendererSupport) hierarchy, renderedClass,
+ renderingClass);
+ }
+ } else if (key.equals(THROWABLE_RENDERER_PREFIX)) {
+ if (hierarchy instanceof ThrowableRendererSupport) {
+ ThrowableRenderer tr = (ThrowableRenderer)
+ OptionConverter.instantiateByKey(props,
+ THROWABLE_RENDERER_PREFIX,
+ org.apache.log4j.spi.ThrowableRenderer.class,
+ null);
+ if(tr == null) {
+ LogLog.error(
+ "Could not instantiate throwableRenderer.");
+ } else {
+ PropertySetter setter = new PropertySetter(tr);
+ setter.setProperties(props, THROWABLE_RENDERER_PREFIX + ".");
+ ((ThrowableRendererSupport) hierarchy).setThrowableRenderer(tr);
+
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ Parse the additivity option for a non-root category.
+ */
+ void parseAdditivityForLogger(Properties props, Logger cat,
+ String loggerName) {
+ String value = OptionConverter.findAndSubst(ADDITIVITY_PREFIX + loggerName,
+ props);
+ LogLog.debug("Handling "+ADDITIVITY_PREFIX + loggerName+"=["+value+"]");
+ // touch additivity only if necessary
+ if((value != null) && (!value.equals(""))) {
+ boolean additivity = OptionConverter.toBoolean(value, true);
+ LogLog.debug("Setting additivity for \""+loggerName+"\" to "+
+ additivity);
+ cat.setAdditivity(additivity);
+ }
+ }
+
+ /**
+ This method must work for the root category as well.
+ */
+ void parseCategory(Properties props, Logger logger, String optionKey,
+ String loggerName, String value) {
+
+ LogLog.debug("Parsing for [" +loggerName +"] with value=[" + value+"].");
+ // We must skip over ',' but not white space
+ StringTokenizer st = new StringTokenizer(value, ",");
+
+ // If value is not in the form ", appender.." or "", then we should set
+ // the level of the loggeregory.
+
+ if(!(value.startsWith(",") || value.equals(""))) {
+
+ // just to be on the safe side...
+ if(!st.hasMoreTokens())
+ return;
+
+ String levelStr = st.nextToken();
+ LogLog.debug("Level token is [" + levelStr + "].");
+
+ // If the level value is inherited, set category level value to
+ // null. We also check that the user has not specified inherited for the
+ // root category.
+ if(INHERITED.equalsIgnoreCase(levelStr) ||
+ NULL.equalsIgnoreCase(levelStr)) {
+ if(loggerName.equals(INTERNAL_ROOT_NAME)) {
+ LogLog.warn("The root logger cannot be set to null.");
+ } else {
+ logger.setLevel(null);
+ }
+ } else {
+ logger.setLevel(OptionConverter.toLevel(levelStr, (Level) Level.DEBUG));
+ }
+ LogLog.debug("Category " + loggerName + " set to " + logger.getLevel());
+ }
+
+ // Begin by removing all existing appenders.
+ logger.removeAllAppenders();
+
+ Appender appender;
+ String appenderName;
+ while(st.hasMoreTokens()) {
+ appenderName = st.nextToken().trim();
+ if(appenderName == null || appenderName.equals(","))
+ continue;
+ LogLog.debug("Parsing appender named \"" + appenderName +"\".");
+ appender = parseAppender(props, appenderName);
+ if(appender != null) {
+ logger.addAppender(appender);
+ }
+ }
+ }
+
+ Appender parseAppender(Properties props, String appenderName) {
+ Appender appender = registryGet(appenderName);
+ if((appender != null)) {
+ LogLog.debug("Appender \"" + appenderName + "\" was already parsed.");
+ return appender;
+ }
+ // Appender was not previously initialized.
+ String prefix = APPENDER_PREFIX + appenderName;
+ String layoutPrefix = prefix + ".layout";
+
+ appender = (Appender) OptionConverter.instantiateByKey(props, prefix,
+ org.apache.log4j.Appender.class,
+ null);
+ if(appender == null) {
+ LogLog.error(
+ "Could not instantiate appender named \"" + appenderName+"\".");
+ return null;
+ }
+ appender.setName(appenderName);
+
+ if(appender instanceof OptionHandler) {
+ if(appender.requiresLayout()) {
+ Layout layout = (Layout) OptionConverter.instantiateByKey(props,
+ layoutPrefix,
+ Layout.class,
+ null);
+ if(layout != null) {
+ appender.setLayout(layout);
+ LogLog.debug("Parsing layout options for \"" + appenderName +"\".");
+ //configureOptionHandler(layout, layoutPrefix + ".", props);
+ PropertySetter.setProperties(layout, props, layoutPrefix + ".");
+ LogLog.debug("End of parsing for \"" + appenderName +"\".");
+ }
+ }
+ final String errorHandlerPrefix = prefix + ".errorhandler";
+ String errorHandlerClass = OptionConverter.findAndSubst(errorHandlerPrefix, props);
+ if (errorHandlerClass != null) {
+ ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByKey(props,
+ errorHandlerPrefix,
+ ErrorHandler.class,
+ null);
+ if (eh != null) {
+ appender.setErrorHandler(eh);
+ LogLog.debug("Parsing errorhandler options for \"" + appenderName +"\".");
+ parseErrorHandler(eh, errorHandlerPrefix, props, repository);
+ final Properties edited = new Properties();
+ final String[] keys = new String[] {
+ errorHandlerPrefix + "." + ROOT_REF,
+ errorHandlerPrefix + "." + LOGGER_REF,
+ errorHandlerPrefix + "." + APPENDER_REF_TAG
+ };
+ for(Iterator iter = props.entrySet().iterator();iter.hasNext();) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ int i = 0;
+ for(; i < keys.length; i++) {
+ if(keys[i].equals(entry.getKey())) break;
+ }
+ if (i == keys.length) {
+ edited.put(entry.getKey(), entry.getValue());
+ }
+ }
+ PropertySetter.setProperties(eh, edited, errorHandlerPrefix + ".");
+ LogLog.debug("End of errorhandler parsing for \"" + appenderName +"\".");
+ }
+
+ }
+ //configureOptionHandler((OptionHandler) appender, prefix + ".", props);
+ PropertySetter.setProperties(appender, props, prefix + ".");
+ LogLog.debug("Parsed \"" + appenderName +"\" options.");
+ }
+ parseAppenderFilters(props, appenderName, appender);
+ registryPut(appender);
+ return appender;
+ }
+
+ private void parseErrorHandler(
+ final ErrorHandler eh,
+ final String errorHandlerPrefix,
+ final Properties props,
+ final LoggerRepository hierarchy) {
+ boolean rootRef = OptionConverter.toBoolean(
+ OptionConverter.findAndSubst(errorHandlerPrefix + ROOT_REF, props), false);
+ if (rootRef) {
+ eh.setLogger(hierarchy.getRootLogger());
+ }
+ String loggerName = OptionConverter.findAndSubst(errorHandlerPrefix + LOGGER_REF , props);
+ if (loggerName != null) {
+ Logger logger = (loggerFactory == null) ? hierarchy.getLogger(loggerName)
+ : hierarchy.getLogger(loggerName, loggerFactory);
+ eh.setLogger(logger);
+ }
+ String appenderName = OptionConverter.findAndSubst(errorHandlerPrefix + APPENDER_REF_TAG, props);
+ if (appenderName != null) {
+ Appender backup = parseAppender(props, appenderName);
+ if (backup != null) {
+ eh.setBackupAppender(backup);
+ }
+ }
+ }
+
+
+ void parseAppenderFilters(Properties props, String appenderName, Appender appender) {
+ // extract filters and filter options from props into a hashtable mapping
+ // the property name defining the filter class to a list of pre-parsed
+ // name-value pairs associated to that filter
+ final String filterPrefix = APPENDER_PREFIX + appenderName + ".filter.";
+ int fIdx = filterPrefix.length();
+ Hashtable filters = new Hashtable();
+ Enumeration e = props.keys();
+ String name = "";
+ while (e.hasMoreElements()) {
+ String key = (String) e.nextElement();
+ if (key.startsWith(filterPrefix)) {
+ int dotIdx = key.indexOf('.', fIdx);
+ String filterKey = key;
+ if (dotIdx != -1) {
+ filterKey = key.substring(0, dotIdx);
+ name = key.substring(dotIdx+1);
+ }
+ Vector filterOpts = (Vector) filters.get(filterKey);
+ if (filterOpts == null) {
+ filterOpts = new Vector();
+ filters.put(filterKey, filterOpts);
+ }
+ if (dotIdx != -1) {
+ String value = OptionConverter.findAndSubst(key, props);
+ filterOpts.add(new NameValue(name, value));
+ }
+ }
+ }
+
+ // sort filters by IDs, insantiate filters, set filter options,
+ // add filters to the appender
+ Enumeration g = new SortedKeyEnumeration(filters);
+ while (g.hasMoreElements()) {
+ String key = (String) g.nextElement();
+ String clazz = props.getProperty(key);
+ if (clazz != null) {
+ LogLog.debug("Filter key: ["+key+"] class: ["+props.getProperty(key) +"] props: "+filters.get(key));
+ Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz, Filter.class, null);
+ if (filter != null) {
+ PropertySetter propSetter = new PropertySetter(filter);
+ Vector v = (Vector)filters.get(key);
+ Enumeration filterProps = v.elements();
+ while (filterProps.hasMoreElements()) {
+ NameValue kv = (NameValue)filterProps.nextElement();
+ propSetter.setProperty(kv.key, kv.value);
+ }
+ propSetter.activate();
+ LogLog.debug("Adding filter of type ["+filter.getClass()
+ +"] to appender named ["+appender.getName()+"].");
+ appender.addFilter(filter);
+ }
+ } else {
+ LogLog.warn("Missing class definition for filter: ["+key+"]");
+ }
+ }
+ }
+
+
+ void registryPut(Appender appender) {
+ registry.put(appender.getName(), appender);
+ }
+
+ Appender registryGet(String name) {
+ return (Appender) registry.get(name);
+ }
+}
+
+class PropertyWatchdog extends FileWatchdog {
+
+ PropertyWatchdog(String filename) {
+ super(filename);
+ }
+
+ /**
+ Call {@link PropertyConfigurator#configure(String)} with the
+ filename to reconfigure log4j. */
+ public
+ void doOnChange() {
+ new PropertyConfigurator().doConfigure(filename,
+ LogManager.getLoggerRepository());
+ }
+}
+
+class NameValue {
+ String key, value;
+ public NameValue(String key, String value) {
+ this.key = key;
+ this.value = value;
+ }
+ public String toString() {
+ return key + "=" + value;
+ }
+}
+
+class SortedKeyEnumeration implements Enumeration {
+
+ private Enumeration e;
+
+ public SortedKeyEnumeration(Hashtable ht) {
+ Enumeration f = ht.keys();
+ Vector keys = new Vector(ht.size());
+ for (int i, last = 0; f.hasMoreElements(); ++last) {
+ String key = (String) f.nextElement();
+ for (i = 0; i < last; ++i) {
+ String s = (String) keys.get(i);
+ if (key.compareTo(s) <= 0) break;
+ }
+ keys.add(i, key);
+ }
+ e = keys.elements();
+ }
+
+ public boolean hasMoreElements() {
+ return e.hasMoreElements();
+ }
+
+ public Object nextElement() {
+ return e.nextElement();
+ }
+}
diff --git a/java/src/org/apache/log4j/ProvisionNode.java b/java/src/org/apache/log4j/ProvisionNode.java
new file mode 100644
index 0000000..f112682
--- /dev/null
+++ b/java/src/org/apache/log4j/ProvisionNode.java
@@ -0,0 +1,29 @@
+/*
+ * 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.log4j;
+
+import java.util.Vector;
+
+class ProvisionNode extends Vector {
+ private static final long serialVersionUID = -4479121426311014469L;
+
+ ProvisionNode(Logger logger) {
+ super();
+ this.addElement(logger);
+ }
+}
diff --git a/java/src/org/apache/log4j/RollingFileAppender.java b/java/src/org/apache/log4j/RollingFileAppender.java
new file mode 100644
index 0000000..8a7c656
--- /dev/null
+++ b/java/src/org/apache/log4j/RollingFileAppender.java
@@ -0,0 +1,284 @@
+/*
+ * 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.log4j;
+
+import java.io.IOException;
+import java.io.Writer;
+import java.io.File;
+import java.io.InterruptedIOException;
+
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.CountingQuietWriter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ RollingFileAppender extends FileAppender to backup the log files when
+ they reach a certain size.
+
+ The log4j extras companion includes alternatives which should be considered
+ for new deployments and which are discussed in the documentation
+ for org.apache.log4j.rolling.RollingFileAppender.
+
+
+ @author Heinz Richter
+ @author Ceki Gülcü
+
+*/
+public class RollingFileAppender extends FileAppender {
+
+ /**
+ The default maximum file size is 10MB.
+ */
+ protected long maxFileSize = 10*1024*1024;
+
+ /**
+ There is one backup file by default.
+ */
+ protected int maxBackupIndex = 1;
+
+ private long nextRollover = 0;
+
+ /**
+ The default constructor simply calls its {@link
+ FileAppender#FileAppender parents constructor}. */
+ public
+ RollingFileAppender() {
+ super();
+ }
+
+ /**
+ Instantiate a RollingFileAppender and open the file designated by
+ filename. The opened filename will become the ouput
+ destination for this appender.
+
+
If the append parameter is true, the file will be
+ appended to. Otherwise, the file desginated by
+ filename will be truncated before being opened.
+ */
+ public
+ RollingFileAppender(Layout layout, String filename, boolean append)
+ throws IOException {
+ super(layout, filename, append);
+ }
+
+ /**
+ Instantiate a FileAppender and open the file designated by
+ filename. The opened filename will become the output
+ destination for this appender.
+
+
The file will be appended to. */
+ public
+ RollingFileAppender(Layout layout, String filename) throws IOException {
+ super(layout, filename);
+ }
+
+ /**
+ Returns the value of the MaxBackupIndex option.
+ */
+ public
+ int getMaxBackupIndex() {
+ return maxBackupIndex;
+ }
+
+ /**
+ Get the maximum size that the output file is allowed to reach
+ before being rolled over to backup files.
+
+ @since 1.1
+ */
+ public
+ long getMaximumFileSize() {
+ return maxFileSize;
+ }
+
+ /**
+ Implements the usual roll over behaviour.
+
+
If MaxBackupIndex is positive, then files
+ {File.1, ..., File.MaxBackupIndex -1}
+ are renamed to {File.2, ...,
+ File.MaxBackupIndex}. Moreover, File is
+ renamed File.1 and closed. A new File is
+ created to receive further log output.
+
+
If MaxBackupIndex is equal to zero, then the
+ File is truncated with no backup files created.
+
+ */
+ public // synchronization not necessary since doAppend is alreasy synched
+ void rollOver() {
+ File target;
+ File file;
+
+ if (qw != null) {
+ long size = ((CountingQuietWriter) qw).getCount();
+ LogLog.debug("rolling over count=" + size);
+ // if operation fails, do not roll again until
+ // maxFileSize more bytes are written
+ nextRollover = size + maxFileSize;
+ }
+ LogLog.debug("maxBackupIndex="+maxBackupIndex);
+
+ boolean renameSucceeded = true;
+ // If maxBackups <= 0, then there is no file renaming to be done.
+ if(maxBackupIndex > 0) {
+ // Delete the oldest file, to keep Windows happy.
+ file = new File(fileName + '.' + maxBackupIndex);
+ if (file.exists())
+ renameSucceeded = file.delete();
+
+ // Map {(maxBackupIndex - 1), ..., 2, 1} to {maxBackupIndex, ..., 3, 2}
+ for (int i = maxBackupIndex - 1; i >= 1 && renameSucceeded; i--) {
+ file = new File(fileName + "." + i);
+ if (file.exists()) {
+ target = new File(fileName + '.' + (i + 1));
+ LogLog.debug("Renaming file " + file + " to " + target);
+ renameSucceeded = file.renameTo(target);
+ }
+ }
+
+ if(renameSucceeded) {
+ // Rename fileName to fileName.1
+ target = new File(fileName + "." + 1);
+
+ this.closeFile(); // keep windows happy.
+
+ file = new File(fileName);
+ LogLog.debug("Renaming file " + file + " to " + target);
+ renameSucceeded = file.renameTo(target);
+ //
+ // if file rename failed, reopen file with append = true
+ //
+ if (!renameSucceeded) {
+ try {
+ this.setFile(fileName, true, bufferedIO, bufferSize);
+ }
+ catch(IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("setFile("+fileName+", true) call failed.", e);
+ }
+ }
+ }
+ }
+
+ //
+ // if all renames were successful, then
+ //
+ if (renameSucceeded) {
+ try {
+ // This will also close the file. This is OK since multiple
+ // close operations are safe.
+ this.setFile(fileName, false, bufferedIO, bufferSize);
+ nextRollover = 0;
+ }
+ catch(IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("setFile("+fileName+", false) call failed.", e);
+ }
+ }
+ }
+
+ public
+ synchronized
+ void setFile(String fileName, boolean append, boolean bufferedIO, int bufferSize)
+ throws IOException {
+ super.setFile(fileName, append, this.bufferedIO, this.bufferSize);
+ if(append) {
+ File f = new File(fileName);
+ ((CountingQuietWriter) qw).setCount(f.length());
+ }
+ }
+
+
+ /**
+ Set the maximum number of backup files to keep around.
+
+
The MaxBackupIndex option determines how many backup
+ files are kept before the oldest is erased. This option takes
+ a positive integer value. If set to zero, then there will be no
+ backup files and the log file will be truncated when it reaches
+ MaxFileSize.
+ */
+ public
+ void setMaxBackupIndex(int maxBackups) {
+ this.maxBackupIndex = maxBackups;
+ }
+
+ /**
+ Set the maximum size that the output file is allowed to reach
+ before being rolled over to backup files.
+
+
This method is equivalent to {@link #setMaxFileSize} except
+ that it is required for differentiating the setter taking a
+ long argument from the setter taking a
+ String argument by the JavaBeans {@link
+ java.beans.Introspector Introspector}.
+
+ @see #setMaxFileSize(String)
+ */
+ public
+ void setMaximumFileSize(long maxFileSize) {
+ this.maxFileSize = maxFileSize;
+ }
+
+
+ /**
+ Set the maximum size that the output file is allowed to reach
+ before being rolled over to backup files.
+
+
In configuration files, the MaxFileSize option takes an
+ long integer in the range 0 - 2^63. You can specify the value
+ with the suffixes "KB", "MB" or "GB" so that the integer is
+ interpreted being expressed respectively in kilobytes, megabytes
+ or gigabytes. For example, the value "10KB" will be interpreted
+ as 10240.
+ */
+ public
+ void setMaxFileSize(String value) {
+ maxFileSize = OptionConverter.toFileSize(value, maxFileSize + 1);
+ }
+
+ protected
+ void setQWForFiles(Writer writer) {
+ this.qw = new CountingQuietWriter(writer, errorHandler);
+ }
+
+ /**
+ This method differentiates RollingFileAppender from its super
+ class.
+
+ @since 0.9.0
+ */
+ protected
+ void subAppend(LoggingEvent event) {
+ super.subAppend(event);
+ if(fileName != null && qw != null) {
+ long size = ((CountingQuietWriter) qw).getCount();
+ if (size >= maxFileSize && size >= nextRollover) {
+ rollOver();
+ }
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/SimpleLayout.java b/java/src/org/apache/log4j/SimpleLayout.java
new file mode 100644
index 0000000..5699661
--- /dev/null
+++ b/java/src/org/apache/log4j/SimpleLayout.java
@@ -0,0 +1,78 @@
+/*
+ * 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.log4j;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ SimpleLayout consists of the level of the log statement,
+ followed by " - " and then the log message itself. For example,
+
+
+ DEBUG - Hello world
+
+
+
+ @author Ceki Gülcü
+ @since version 0.7.0
+
+
{@link PatternLayout} offers a much more powerful alternative.
+*/
+public class SimpleLayout extends Layout {
+
+ StringBuffer sbuf = new StringBuffer(128);
+
+ public SimpleLayout() {
+ }
+
+ public
+ void activateOptions() {
+ }
+
+ /**
+ Returns the log statement in a format consisting of the
+ level, followed by " - " and then the
+ message. For example,
INFO - "A message"
+
+
+ The category parameter is ignored.
+
+ @return A byte array in SimpleLayout format.
+ */
+ public
+ String format(LoggingEvent event) {
+
+ sbuf.setLength(0);
+ sbuf.append(event.getLevel().toString());
+ sbuf.append(" - ");
+ sbuf.append(event.getRenderedMessage());
+ sbuf.append(LINE_SEP);
+ return sbuf.toString();
+ }
+
+/**
+ The SimpleLayout does not handle the throwable contained within
+ {@link LoggingEvent LoggingEvents}. Thus, it returns
+ true.
+
+ @since version 0.8.4 */
+ public
+ boolean ignoresThrowable() {
+ return true;
+ }
+}
diff --git a/java/src/org/apache/log4j/TTCCLayout.java b/java/src/org/apache/log4j/TTCCLayout.java
new file mode 100644
index 0000000..3b0e98f
--- /dev/null
+++ b/java/src/org/apache/log4j/TTCCLayout.java
@@ -0,0 +1,217 @@
+/*
+ * 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.
+ */
+
+// Contributors: Christopher Williams
+// Mathias Bogaert
+
+package org.apache.log4j;
+
+import org.apache.log4j.helpers.DateLayout;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ TTCC layout format consists of time, thread, category and nested
+ diagnostic context information, hence the name.
+
+
Each of the four fields can be individually enabled or
+ disabled. The time format depends on the DateFormat
+ used.
+
+
Here is an example TTCCLayout output with the
+ {@link org.apache.log4j.helpers.RelativeTimeDateFormat}.
+
+
+176 [main] INFO org.apache.log4j.examples.Sort - Populating an array of 2 elements in reverse order.
+225 [main] INFO org.apache.log4j.examples.SortAlgo - Entered the sort method.
+262 [main] DEBUG org.apache.log4j.examples.SortAlgo.OUTER i=1 - Outer loop.
+276 [main] DEBUG org.apache.log4j.examples.SortAlgo.SWAP i=1 j=0 - Swapping intArray[0] = 1 and intArray[1] = 0
+290 [main] DEBUG org.apache.log4j.examples.SortAlgo.OUTER i=0 - Outer loop.
+304 [main] INFO org.apache.log4j.examples.SortAlgo.DUMP - Dump of interger array:
+317 [main] INFO org.apache.log4j.examples.SortAlgo.DUMP - Element [0] = 0
+331 [main] INFO org.apache.log4j.examples.SortAlgo.DUMP - Element [1] = 1
+343 [main] INFO org.apache.log4j.examples.Sort - The next log statement should be an error message.
+346 [main] ERROR org.apache.log4j.examples.SortAlgo.DUMP - Tried to dump an uninitialized array.
+ at org.apache.log4j.examples.SortAlgo.dump(SortAlgo.java:58)
+ at org.apache.log4j.examples.Sort.main(Sort.java:64)
+467 [main] INFO org.apache.log4j.examples.Sort - Exiting main method.
+
+
+ The first field is the number of milliseconds elapsed since the
+ start of the program. The second field is the thread outputting the
+ log statement. The third field is the level, the fourth field is
+ the category to which the statement belongs.
+
+
The fifth field (just before the '-') is the nested diagnostic
+ context. Note the nested diagnostic context may be empty as in the
+ first two statements. The text after the '-' is the message of the
+ statement.
+
+
WARNING Do not use the same TTCCLayout instance from
+ within different appenders. The TTCCLayout is not thread safe when
+ used in his way. However, it is perfectly safe to use a TTCCLayout
+ instance from just one appender.
+
+
{@link PatternLayout} offers a much more flexible alternative.
+
+ @author Ceki Gülcü
+ @author Heinz Richter
+
+*/
+public class TTCCLayout extends DateLayout {
+
+ // Internal representation of options
+ private boolean threadPrinting = true;
+ private boolean categoryPrefixing = true;
+ private boolean contextPrinting = true;
+
+
+ protected final StringBuffer buf = new StringBuffer(256);
+
+
+ /**
+ Instantiate a TTCCLayout object with {@link
+ org.apache.log4j.helpers.RelativeTimeDateFormat} as the date
+ formatter in the local time zone.
+
+ @since 0.7.5 */
+ public TTCCLayout() {
+ this.setDateFormat(RELATIVE_TIME_DATE_FORMAT, null);
+ }
+
+
+ /**
+ Instantiate a TTCCLayout object using the local time zone. The
+ DateFormat used will depend on the dateFormatType.
+
+
This constructor just calls the {@link
+ DateLayout#setDateFormat} method.
+
+ */
+ public TTCCLayout(String dateFormatType) {
+ this.setDateFormat(dateFormatType);
+ }
+
+
+ /**
+ The ThreadPrinting option specifies whether the name of the
+ current thread is part of log output or not. This is true by default.
+ */
+ public
+ void setThreadPrinting(boolean threadPrinting) {
+ this.threadPrinting = threadPrinting;
+ }
+
+ /**
+ Returns value of the ThreadPrinting option.
+ */
+ public
+ boolean getThreadPrinting() {
+ return threadPrinting;
+ }
+
+ /**
+ The CategoryPrefixing option specifies whether {@link Category}
+ name is part of log output or not. This is true by default.
+ */
+ public
+ void setCategoryPrefixing(boolean categoryPrefixing) {
+ this.categoryPrefixing = categoryPrefixing;
+ }
+
+ /**
+ Returns value of the CategoryPrefixing option.
+ */
+ public
+ boolean getCategoryPrefixing() {
+ return categoryPrefixing;
+ }
+
+ /**
+ The ContextPrinting option specifies log output will include
+ the nested context information belonging to the current thread.
+ This is true by default.
+ */
+ public
+ void setContextPrinting(boolean contextPrinting) {
+ this.contextPrinting = contextPrinting;
+ }
+
+ /**
+ Returns value of the ContextPrinting option.
+ */
+ public
+ boolean getContextPrinting() {
+ return contextPrinting;
+ }
+
+ /**
+ In addition to the level of the statement and message, the
+ returned byte array includes time, thread, category and {@link NDC}
+ information.
+
+
Time, thread, category and diagnostic context are printed
+ depending on options.
+
+ @param event The event to format
+
+ */
+ public
+ String format(LoggingEvent event) {
+
+ // Reset buf
+ buf.setLength(0);
+
+ dateFormat(buf, event);
+
+ if(this.threadPrinting) {
+ buf.append('[');
+ buf.append(event.getThreadName());
+ buf.append("] ");
+ }
+ buf.append(event.getLevel().toString());
+ buf.append(' ');
+
+ if(this.categoryPrefixing) {
+ buf.append(event.getLoggerName());
+ buf.append(' ');
+ }
+
+ if(this.contextPrinting) {
+ String ndc = event.getNDC();
+
+ if(ndc != null) {
+ buf.append(ndc);
+ buf.append(' ');
+ }
+ }
+ buf.append("- ");
+ buf.append(event.getRenderedMessage());
+ buf.append(LINE_SEP);
+ return buf.toString();
+ }
+
+ /**
+ The TTCCLayout does not handle the throwable contained within
+ {@link LoggingEvent LoggingEvents}. Thus, it returns
+ true.
+
+ @since version 0.8.4 */
+ public
+ boolean ignoresThrowable() {
+ return true;
+ }
+}
diff --git a/java/src/org/apache/log4j/WriterAppender.java b/java/src/org/apache/log4j/WriterAppender.java
new file mode 100644
index 0000000..981c02d
--- /dev/null
+++ b/java/src/org/apache/log4j/WriterAppender.java
@@ -0,0 +1,387 @@
+/*
+ * 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.log4j;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.OutputStream;
+import java.io.OutputStreamWriter;
+import java.io.Writer;
+
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.QuietWriter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+
+// Contibutors: Jens Uwe Pipka
+// Ben Sandee
+
+/**
+ WriterAppender appends log events to a {@link java.io.Writer} or an
+ {@link java.io.OutputStream} depending on the user's choice.
+
+ @author Ceki Gülcü
+ @since 1.1 */
+public class WriterAppender extends AppenderSkeleton {
+
+
+ /**
+ Immediate flush means that the underlying writer or output stream
+ will be flushed at the end of each append operation unless shouldFlush()
+ is overridden. Immediate
+ flush is slower but ensures that each append request is actually
+ written. If immediateFlush is set to
+ false, then there is a good chance that the last few
+ logs events are not actually written to persistent media if and
+ when the application crashes.
+
+ The immediateFlush variable is set to
+ true by default.
+
+ */
+ protected boolean immediateFlush = true;
+
+ /**
+ The encoding to use when writing.
The
+ encoding variable is set to null by
+ default which results in the utilization of the system's default
+ encoding. */
+ protected String encoding;
+
+ /**
+ This is the {@link QuietWriter quietWriter} where we will write
+ to.
+ */
+ protected QuietWriter qw;
+
+
+ /**
+ This default constructor does nothing. */
+ public
+ WriterAppender() {
+ }
+
+ /**
+ Instantiate a WriterAppender and set the output destination to a
+ new {@link OutputStreamWriter} initialized with os
+ as its {@link OutputStream}. */
+ public
+ WriterAppender(Layout layout, OutputStream os) {
+ this(layout, new OutputStreamWriter(os));
+ }
+
+ /**
+ Instantiate a WriterAppender and set the output destination to
+ writer.
+
+ The writer must have been previously opened by
+ the user. */
+ public
+ WriterAppender(Layout layout, Writer writer) {
+ this.layout = layout;
+ this.setWriter(writer);
+ }
+
+ /**
+ If the ImmediateFlush option is set to
+ true, the appender will flush at the end of each
+ write. This is the default behavior. If the option is set to
+ false, then the underlying stream can defer writing
+ to physical medium to a later time.
+
+
Avoiding the flush operation at the end of each append results in
+ a performance gain of 10 to 20 percent. However, there is safety
+ tradeoff involved in skipping flushing. Indeed, when flushing is
+ skipped, then it is likely that the last few log events will not
+ be recorded on disk when the application exits. This is a high
+ price to pay even for a 20% performance gain.
+ */
+ public
+ void setImmediateFlush(boolean value) {
+ immediateFlush = value;
+ }
+
+ /**
+ Returns value of the ImmediateFlush option.
+ */
+ public
+ boolean getImmediateFlush() {
+ return immediateFlush;
+ }
+
+ /**
+ Does nothing.
+ */
+ public
+ void activateOptions() {
+ }
+
+
+ /**
+ This method is called by the {@link AppenderSkeleton#doAppend}
+ method.
+
+
If the output stream exists and is writable then write a log
+ statement to the output stream. Otherwise, write a single warning
+ message to System.err.
+
+
The format of the output will depend on this appender's
+ layout.
+
+ */
+ public
+ void append(LoggingEvent event) {
+
+ // Reminder: the nesting of calls is:
+ //
+ // doAppend()
+ // - check threshold
+ // - filter
+ // - append();
+ // - checkEntryConditions();
+ // - subAppend();
+
+ if(!checkEntryConditions()) {
+ return;
+ }
+ subAppend(event);
+ }
+
+ /**
+ This method determines if there is a sense in attempting to append.
+
+
It checks whether there is a set output target and also if
+ there is a set layout. If these checks fail, then the boolean
+ value false is returned. */
+ protected
+ boolean checkEntryConditions() {
+ if(this.closed) {
+ LogLog.warn("Not allowed to write to a closed appender.");
+ return false;
+ }
+
+ if(this.qw == null) {
+ errorHandler.error("No output stream or file set for the appender named ["+
+ name+"].");
+ return false;
+ }
+
+ if(this.layout == null) {
+ errorHandler.error("No layout set for the appender named ["+ name+"].");
+ return false;
+ }
+ return true;
+ }
+
+
+ /**
+ Close this appender instance. The underlying stream or writer is
+ also closed.
+
+
Closed appenders cannot be reused.
+
+ @see #setWriter
+ @since 0.8.4 */
+ public
+ synchronized
+ void close() {
+ if(this.closed)
+ return;
+ this.closed = true;
+ writeFooter();
+ reset();
+ }
+
+ /**
+ * Close the underlying {@link java.io.Writer}.
+ * */
+ protected void closeWriter() {
+ if(qw != null) {
+ try {
+ qw.close();
+ } catch(IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // There is do need to invoke an error handler at this late
+ // stage.
+ LogLog.error("Could not close " + qw, e);
+ }
+ }
+ }
+
+ /**
+ Returns an OutputStreamWriter when passed an OutputStream. The
+ encoding used will depend on the value of the
+ encoding property. If the encoding value is
+ specified incorrectly the writer will be opened using the default
+ system encoding (an error message will be printed to the loglog. */
+ protected
+ OutputStreamWriter createWriter(OutputStream os) {
+ OutputStreamWriter retval = null;
+
+ String enc = getEncoding();
+ if(enc != null) {
+ try {
+ retval = new OutputStreamWriter(os, enc);
+ } catch(IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.warn("Error initializing output writer.");
+ LogLog.warn("Unsupported encoding?");
+ }
+ }
+ if(retval == null) {
+ retval = new OutputStreamWriter(os);
+ }
+ return retval;
+ }
+
+ public String getEncoding() {
+ return encoding;
+ }
+
+ public void setEncoding(String value) {
+ encoding = value;
+ }
+
+
+
+
+ /**
+ Set the {@link ErrorHandler} for this WriterAppender and also the
+ underlying {@link QuietWriter} if any. */
+ public synchronized void setErrorHandler(ErrorHandler eh) {
+ if(eh == null) {
+ LogLog.warn("You have tried to set a null error-handler.");
+ } else {
+ this.errorHandler = eh;
+ if(this.qw != null) {
+ this.qw.setErrorHandler(eh);
+ }
+ }
+ }
+
+ /**
+
Sets the Writer where the log output will go. The
+ specified Writer must be opened by the user and be
+ writable.
+
+
The java.io.Writer will be closed when the
+ appender instance is closed.
+
+
+
WARNING: Logging to an unopened Writer will fail.
+
+ @param writer An already opened Writer. */
+ public synchronized void setWriter(Writer writer) {
+ reset();
+ this.qw = new QuietWriter(writer, errorHandler);
+ //this.tp = new TracerPrintWriter(qw);
+ writeHeader();
+ }
+
+
+ /**
+ Actual writing occurs here.
+
+
Most subclasses of WriterAppender will need to
+ override this method.
+
+ @since 0.9.0 */
+ protected
+ void subAppend(LoggingEvent event) {
+ this.qw.write(this.layout.format(event));
+
+ if(layout.ignoresThrowable()) {
+ String[] s = event.getThrowableStrRep();
+ if (s != null) {
+ int len = s.length;
+ for(int i = 0; i < len; i++) {
+ this.qw.write(s[i]);
+ this.qw.write(Layout.LINE_SEP);
+ }
+ }
+ }
+
+ if(shouldFlush(event)) {
+ this.qw.flush();
+ }
+ }
+
+
+
+ /**
+ The WriterAppender requires a layout. Hence, this method returns
+ true.
+ */
+ public
+ boolean requiresLayout() {
+ return true;
+ }
+
+ /**
+ Clear internal references to the writer and other variables.
+
+ Subclasses can override this method for an alternate closing
+ behavior. */
+ protected
+ void reset() {
+ closeWriter();
+ this.qw = null;
+ //this.tp = null;
+ }
+
+
+ /**
+ Write a footer as produced by the embedded layout's {@link
+ Layout#getFooter} method. */
+ protected
+ void writeFooter() {
+ if(layout != null) {
+ String f = layout.getFooter();
+ if(f != null && this.qw != null) {
+ this.qw.write(f);
+ this.qw.flush();
+ }
+ }
+ }
+
+ /**
+ Write a header as produced by the embedded layout's {@link
+ Layout#getHeader} method. */
+ protected
+ void writeHeader() {
+ if(layout != null) {
+ String h = layout.getHeader();
+ if(h != null && this.qw != null)
+ this.qw.write(h);
+ }
+ }
+
+ /**
+ * Determines whether the writer should be flushed after
+ * this event is written.
+ *
+ * @since 1.2.16
+ */
+ protected boolean shouldFlush(final LoggingEvent event) {
+ return immediateFlush;
+ }
+}
diff --git a/java/src/org/apache/log4j/chainsaw/ControlPanel.java b/java/src/org/apache/log4j/chainsaw/ControlPanel.java
new file mode 100644
index 0000000..53b0b1b
--- /dev/null
+++ b/java/src/org/apache/log4j/chainsaw/ControlPanel.java
@@ -0,0 +1,222 @@
+/*
+ * 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.log4j.chainsaw;
+
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import javax.swing.BorderFactory;
+import javax.swing.JButton;
+import javax.swing.JComboBox;
+import javax.swing.JLabel;
+import javax.swing.JPanel;
+import javax.swing.JTextField;
+import javax.swing.event.DocumentEvent;
+import javax.swing.event.DocumentListener;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+import org.apache.log4j.Level;
+
+/**
+ * Represents the controls for filtering, pausing, exiting, etc.
+ *
+ * @author Oliver Burn
+ */
+class ControlPanel extends JPanel {
+ /** use the log messages **/
+ private static final Logger LOG =
+ Logger.getLogger(ControlPanel.class);
+
+ /**
+ * Creates a new ControlPanel instance.
+ *
+ * @param aModel the model to control
+ */
+ ControlPanel(final MyTableModel aModel) {
+ setBorder(BorderFactory.createTitledBorder("Controls: "));
+ final GridBagLayout gridbag = new GridBagLayout();
+ final GridBagConstraints c = new GridBagConstraints();
+ setLayout(gridbag);
+
+ // Pad everything
+ c.ipadx = 5;
+ c.ipady = 5;
+
+ // Add the 1st column of labels
+ c.gridx = 0;
+ c.anchor = GridBagConstraints.EAST;
+
+ c.gridy = 0;
+ JLabel label = new JLabel("Filter Level:");
+ gridbag.setConstraints(label, c);
+ add(label);
+
+ c.gridy++;
+ label = new JLabel("Filter Thread:");
+ gridbag.setConstraints(label, c);
+ add(label);
+
+ c.gridy++;
+ label = new JLabel("Filter Logger:");
+ gridbag.setConstraints(label, c);
+ add(label);
+
+ c.gridy++;
+ label = new JLabel("Filter NDC:");
+ gridbag.setConstraints(label, c);
+ add(label);
+
+ c.gridy++;
+ label = new JLabel("Filter Message:");
+ gridbag.setConstraints(label, c);
+ add(label);
+
+ // Add the 2nd column of filters
+ c.weightx = 1;
+ //c.weighty = 1;
+ c.gridx = 1;
+ c.anchor = GridBagConstraints.WEST;
+
+ c.gridy = 0;
+ final Level[] allPriorities = new Level[] {Level.FATAL,
+ Level.ERROR,
+ Level.WARN,
+ Level.INFO,
+ Level.DEBUG,
+ Level.TRACE };
+
+ final JComboBox priorities = new JComboBox(allPriorities);
+ final Level lowest = allPriorities[allPriorities.length - 1];
+ priorities.setSelectedItem(lowest);
+ aModel.setPriorityFilter(lowest);
+ gridbag.setConstraints(priorities, c);
+ add(priorities);
+ priorities.setEditable(false);
+ priorities.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent aEvent) {
+ aModel.setPriorityFilter(
+ (Priority) priorities.getSelectedItem());
+ }
+ });
+
+
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.gridy++;
+ final JTextField threadField = new JTextField("");
+ threadField.getDocument().addDocumentListener(new DocumentListener () {
+ public void insertUpdate(DocumentEvent aEvent) {
+ aModel.setThreadFilter(threadField.getText());
+ }
+ public void removeUpdate(DocumentEvent aEvente) {
+ aModel.setThreadFilter(threadField.getText());
+ }
+ public void changedUpdate(DocumentEvent aEvent) {
+ aModel.setThreadFilter(threadField.getText());
+ }
+ });
+ gridbag.setConstraints(threadField, c);
+ add(threadField);
+
+ c.gridy++;
+ final JTextField catField = new JTextField("");
+ catField.getDocument().addDocumentListener(new DocumentListener () {
+ public void insertUpdate(DocumentEvent aEvent) {
+ aModel.setCategoryFilter(catField.getText());
+ }
+ public void removeUpdate(DocumentEvent aEvent) {
+ aModel.setCategoryFilter(catField.getText());
+ }
+ public void changedUpdate(DocumentEvent aEvent) {
+ aModel.setCategoryFilter(catField.getText());
+ }
+ });
+ gridbag.setConstraints(catField, c);
+ add(catField);
+
+ c.gridy++;
+ final JTextField ndcField = new JTextField("");
+ ndcField.getDocument().addDocumentListener(new DocumentListener () {
+ public void insertUpdate(DocumentEvent aEvent) {
+ aModel.setNDCFilter(ndcField.getText());
+ }
+ public void removeUpdate(DocumentEvent aEvent) {
+ aModel.setNDCFilter(ndcField.getText());
+ }
+ public void changedUpdate(DocumentEvent aEvent) {
+ aModel.setNDCFilter(ndcField.getText());
+ }
+ });
+ gridbag.setConstraints(ndcField, c);
+ add(ndcField);
+
+ c.gridy++;
+ final JTextField msgField = new JTextField("");
+ msgField.getDocument().addDocumentListener(new DocumentListener () {
+ public void insertUpdate(DocumentEvent aEvent) {
+ aModel.setMessageFilter(msgField.getText());
+ }
+ public void removeUpdate(DocumentEvent aEvent) {
+ aModel.setMessageFilter(msgField.getText());
+ }
+ public void changedUpdate(DocumentEvent aEvent) {
+ aModel.setMessageFilter(msgField.getText());
+ }
+ });
+
+
+ gridbag.setConstraints(msgField, c);
+ add(msgField);
+
+ // Add the 3rd column of buttons
+ c.weightx = 0;
+ c.fill = GridBagConstraints.HORIZONTAL;
+ c.anchor = GridBagConstraints.EAST;
+ c.gridx = 2;
+
+ c.gridy = 0;
+ final JButton exitButton = new JButton("Exit");
+ exitButton.setMnemonic('x');
+ exitButton.addActionListener(ExitAction.INSTANCE);
+ gridbag.setConstraints(exitButton, c);
+ add(exitButton);
+
+ c.gridy++;
+ final JButton clearButton = new JButton("Clear");
+ clearButton.setMnemonic('c');
+ clearButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent aEvent) {
+ aModel.clear();
+ }
+ });
+ gridbag.setConstraints(clearButton, c);
+ add(clearButton);
+
+ c.gridy++;
+ final JButton toggleButton = new JButton("Pause");
+ toggleButton.setMnemonic('p');
+ toggleButton.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent aEvent) {
+ aModel.toggle();
+ toggleButton.setText(
+ aModel.isPaused() ? "Resume" : "Pause");
+ }
+ });
+ gridbag.setConstraints(toggleButton, c);
+ add(toggleButton);
+ }
+}
diff --git a/java/src/org/apache/log4j/chainsaw/DetailPanel.java b/java/src/org/apache/log4j/chainsaw/DetailPanel.java
new file mode 100644
index 0000000..1f5dfe2
--- /dev/null
+++ b/java/src/org/apache/log4j/chainsaw/DetailPanel.java
@@ -0,0 +1,170 @@
+/*
+ * 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.log4j.chainsaw;
+
+import java.awt.BorderLayout;
+import java.text.MessageFormat;
+import java.util.Date;
+import javax.swing.BorderFactory;
+import javax.swing.JEditorPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import org.apache.log4j.Logger;
+
+/**
+ * A panel for showing a stack trace.
+ *
+ * @author Oliver Burn
+ */
+class DetailPanel
+ extends JPanel
+ implements ListSelectionListener
+{
+ /** used to log events **/
+ private static final Logger LOG =
+ Logger.getLogger(DetailPanel.class);
+
+ /** used to format the logging event **/
+ private static final MessageFormat FORMATTER = new MessageFormat(
+ "Time: {0,time,medium}" +
+ " Priority: {1}" +
+ " Thread: {2}" +
+ " NDC: {3}" +
+ "
Logger: {4}" +
+ "
Location: {5}" +
+ "
Message:" +
+ "
{6}" +
+ "Throwable:" +
+ "{7}");
+
+ /** the model for the data to render **/
+ private final MyTableModel mModel;
+ /** pane for rendering detail **/
+ private final JEditorPane mDetails;
+
+ /**
+ * Creates a new DetailPanel instance.
+ *
+ * @param aTable the table to listen for selections on
+ * @param aModel the model backing the table
+ */
+ DetailPanel(JTable aTable, final MyTableModel aModel) {
+ mModel = aModel;
+ setLayout(new BorderLayout());
+ setBorder(BorderFactory.createTitledBorder("Details: "));
+
+ mDetails = new JEditorPane();
+ mDetails.setEditable(false);
+ mDetails.setContentType("text/html");
+ add(new JScrollPane(mDetails), BorderLayout.CENTER);
+
+ final ListSelectionModel rowSM = aTable.getSelectionModel();
+ rowSM.addListSelectionListener(this);
+ }
+
+ /** @see ListSelectionListener **/
+ public void valueChanged(ListSelectionEvent aEvent) {
+ //Ignore extra messages.
+ if (aEvent.getValueIsAdjusting()) {
+ return;
+ }
+
+ final ListSelectionModel lsm = (ListSelectionModel) aEvent.getSource();
+ if (lsm.isSelectionEmpty()) {
+ mDetails.setText("Nothing selected");
+ } else {
+ final int selectedRow = lsm.getMinSelectionIndex();
+ final EventDetails e = mModel.getEventDetails(selectedRow);
+ final Object[] args =
+ {
+ new Date(e.getTimeStamp()),
+ e.getPriority(),
+ escape(e.getThreadName()),
+ escape(e.getNDC()),
+ escape(e.getCategoryName()),
+ escape(e.getLocationDetails()),
+ escape(e.getMessage()),
+ escape(getThrowableStrRep(e))
+ };
+ mDetails.setText(FORMATTER.format(args));
+ mDetails.setCaretPosition(0);
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Private methods
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Returns a string representation of a throwable.
+ *
+ * @param aEvent contains the throwable information
+ * @return a String value
+ */
+ private static String getThrowableStrRep(EventDetails aEvent) {
+ final String[] strs = aEvent.getThrowableStrRep();
+ if (strs == null) {
+ return null;
+ }
+
+ final StringBuffer sb = new StringBuffer();
+ for (int i = 0; i < strs.length; i++) {
+ sb.append(strs[i]).append("\n");
+ }
+
+ return sb.toString();
+ }
+
+ /**
+ * Escape <, > & and " as their entities. It is very
+ * dumb about & handling.
+ * @param aStr the String to escape.
+ * @return the escaped String
+ */
+ private String escape(String aStr) {
+ if (aStr == null) {
+ return null;
+ }
+
+ final StringBuffer buf = new StringBuffer();
+ for (int i = 0; i < aStr.length(); i++) {
+ char c = aStr.charAt(i);
+ switch (c) {
+ case '<':
+ buf.append("<");
+ break;
+ case '>':
+ buf.append(">");
+ break;
+ case '\"':
+ buf.append(""");
+ break;
+ case '&':
+ buf.append("&");
+ break;
+ default:
+ buf.append(c);
+ break;
+ }
+ }
+ return buf.toString();
+ }
+}
diff --git a/java/src/org/apache/log4j/chainsaw/EventDetails.java b/java/src/org/apache/log4j/chainsaw/EventDetails.java
new file mode 100644
index 0000000..4b3ad94
--- /dev/null
+++ b/java/src/org/apache/log4j/chainsaw/EventDetails.java
@@ -0,0 +1,135 @@
+/*
+ * 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.log4j.chainsaw;
+
+import org.apache.log4j.Priority;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * Represents the details of a logging event. It is intended to overcome the
+ * problem that a LoggingEvent cannot be constructed with purely fake data.
+ *
+ * @author Oliver Burn
+ * @version 1.0
+ */
+class EventDetails {
+
+ /** the time of the event **/
+ private final long mTimeStamp;
+ /** the priority of the event **/
+ private final Priority mPriority;
+ /** the category of the event **/
+ private final String mCategoryName;
+ /** the NDC for the event **/
+ private final String mNDC;
+ /** the thread for the event **/
+ private final String mThreadName;
+ /** the msg for the event **/
+ private final String mMessage;
+ /** the throwable details the event **/
+ private final String[] mThrowableStrRep;
+ /** the location details for the event **/
+ private final String mLocationDetails;
+
+ /**
+ * Creates a new EventDetails instance.
+ * @param aTimeStamp a long value
+ * @param aPriority a Priority value
+ * @param aCategoryName a String value
+ * @param aNDC a String value
+ * @param aThreadName a String value
+ * @param aMessage a String value
+ * @param aThrowableStrRep a String[] value
+ * @param aLocationDetails a String value
+ */
+ EventDetails(long aTimeStamp,
+ Priority aPriority,
+ String aCategoryName,
+ String aNDC,
+ String aThreadName,
+ String aMessage,
+ String[] aThrowableStrRep,
+ String aLocationDetails)
+ {
+ mTimeStamp = aTimeStamp;
+ mPriority = aPriority;
+ mCategoryName = aCategoryName;
+ mNDC = aNDC;
+ mThreadName = aThreadName;
+ mMessage = aMessage;
+ mThrowableStrRep = aThrowableStrRep;
+ mLocationDetails = aLocationDetails;
+ }
+
+ /**
+ * Creates a new EventDetails instance.
+ *
+ * @param aEvent a LoggingEvent value
+ */
+ EventDetails(LoggingEvent aEvent) {
+
+ this(aEvent.timeStamp,
+ aEvent.getLevel(),
+ aEvent.getLoggerName(),
+ aEvent.getNDC(),
+ aEvent.getThreadName(),
+ aEvent.getRenderedMessage(),
+ aEvent.getThrowableStrRep(),
+ (aEvent.getLocationInformation() == null)
+ ? null : aEvent.getLocationInformation().fullInfo);
+ }
+
+ /** @see #mTimeStamp **/
+ long getTimeStamp() {
+ return mTimeStamp;
+ }
+
+ /** @see #mPriority **/
+ Priority getPriority() {
+ return mPriority;
+ }
+
+ /** @see #mCategoryName **/
+ String getCategoryName() {
+ return mCategoryName;
+ }
+
+ /** @see #mNDC **/
+ String getNDC() {
+ return mNDC;
+ }
+
+ /** @see #mThreadName **/
+ String getThreadName() {
+ return mThreadName;
+ }
+
+ /** @see #mMessage **/
+ String getMessage() {
+ return mMessage;
+ }
+
+ /** @see #mLocationDetails **/
+ String getLocationDetails(){
+ return mLocationDetails;
+ }
+
+ /** @see #mThrowableStrRep **/
+ String[] getThrowableStrRep() {
+ return mThrowableStrRep;
+ }
+}
diff --git a/java/src/org/apache/log4j/chainsaw/ExitAction.java b/java/src/org/apache/log4j/chainsaw/ExitAction.java
new file mode 100644
index 0000000..55a100e
--- /dev/null
+++ b/java/src/org/apache/log4j/chainsaw/ExitAction.java
@@ -0,0 +1,48 @@
+/*
+ * 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.log4j.chainsaw;
+
+import java.awt.event.ActionEvent;
+import javax.swing.AbstractAction;
+import org.apache.log4j.Logger;
+
+/**
+ * Encapsulates the action to exit.
+ *
+ * @author Oliver Burn
+ * @version 1.0
+ */
+class ExitAction
+ extends AbstractAction
+{
+ /** use to log messages **/
+ private static final Logger LOG = Logger.getLogger(ExitAction.class);
+ /** The instance to share **/
+ public static final ExitAction INSTANCE = new ExitAction();
+
+ /** Stop people creating instances **/
+ private ExitAction() {}
+
+ /**
+ * Will shutdown the application.
+ * @param aIgnore ignored
+ */
+ public void actionPerformed(ActionEvent aIgnore) {
+ LOG.info("shutting down");
+ System.exit(0);
+ }
+}
diff --git a/java/src/org/apache/log4j/chainsaw/LoadXMLAction.java b/java/src/org/apache/log4j/chainsaw/LoadXMLAction.java
new file mode 100644
index 0000000..33e5d13
--- /dev/null
+++ b/java/src/org/apache/log4j/chainsaw/LoadXMLAction.java
@@ -0,0 +1,139 @@
+/*
+ * 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.log4j.chainsaw;
+
+import java.awt.event.ActionEvent;
+import java.io.File;
+import java.io.IOException;
+import java.io.StringReader;
+import javax.swing.AbstractAction;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JOptionPane;
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParserFactory;
+import org.apache.log4j.Logger;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+import org.xml.sax.XMLReader;
+
+/**
+ * Encapsulates the action to load an XML file.
+ *
+ * @author Oliver Burn
+ * @version 1.0
+ */
+class LoadXMLAction
+ extends AbstractAction
+{
+ /** use to log messages **/
+ private static final Logger LOG = Logger.getLogger(LoadXMLAction.class);
+
+ /** the parent frame **/
+ private final JFrame mParent;
+
+ /**
+ * the file chooser - configured to allow only the selection of a
+ * single file.
+ */
+ private final JFileChooser mChooser = new JFileChooser();
+ {
+ mChooser.setMultiSelectionEnabled(false);
+ mChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
+ }
+
+ /** parser to read XML files **/
+ private final XMLReader mParser;
+ /** the content handler **/
+ private final XMLFileHandler mHandler;
+
+
+ /**
+ * Creates a new LoadXMLAction instance.
+ *
+ * @param aParent the parent frame
+ * @param aModel the model to add events to
+ * @exception SAXException if an error occurs
+ * @throws ParserConfigurationException if an error occurs
+ */
+ LoadXMLAction(JFrame aParent, MyTableModel aModel)
+ throws SAXException, ParserConfigurationException
+ {
+ mParent = aParent;
+ mHandler = new XMLFileHandler(aModel);
+ mParser = SAXParserFactory.newInstance().newSAXParser().getXMLReader();
+ mParser.setContentHandler(mHandler);
+ }
+
+ /**
+ * Prompts the user for a file to load events from.
+ * @param aIgnore an ActionEvent value
+ */
+ public void actionPerformed(ActionEvent aIgnore) {
+ LOG.info("load file called");
+ if (mChooser.showOpenDialog(mParent) == JFileChooser.APPROVE_OPTION) {
+ LOG.info("Need to load a file");
+ final File chosen = mChooser.getSelectedFile();
+ LOG.info("loading the contents of " + chosen.getAbsolutePath());
+ try {
+ final int num = loadFile(chosen.getAbsolutePath());
+ JOptionPane.showMessageDialog(
+ mParent,
+ "Loaded " + num + " events.",
+ "CHAINSAW",
+ JOptionPane.INFORMATION_MESSAGE);
+ } catch (Exception e) {
+ LOG.warn("caught an exception loading the file", e);
+ JOptionPane.showMessageDialog(
+ mParent,
+ "Error parsing file - " + e.getMessage(),
+ "CHAINSAW",
+ JOptionPane.ERROR_MESSAGE);
+ }
+ }
+ }
+
+ /**
+ * Loads the contents of file into the model
+ *
+ * @param aFile the file to extract events from
+ * @return the number of events loaded
+ * @throws SAXException if an error occurs
+ * @throws IOException if an error occurs
+ */
+ private int loadFile(String aFile)
+ throws SAXException, IOException
+ {
+ synchronized (mParser) {
+ // Create a dummy document to parse the file
+ final StringBuffer buf = new StringBuffer();
+ buf.append("\n");
+ buf.append("]>\n");
+ buf.append("\n");
+ buf.append("&data;\n");
+ buf.append("\n");
+
+ final InputSource is =
+ new InputSource(new StringReader(buf.toString()));
+ mParser.parse(is);
+ return mHandler.getNumEvents();
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/chainsaw/LoggingReceiver.java b/java/src/org/apache/log4j/chainsaw/LoggingReceiver.java
new file mode 100644
index 0000000..ca087ad
--- /dev/null
+++ b/java/src/org/apache/log4j/chainsaw/LoggingReceiver.java
@@ -0,0 +1,121 @@
+/*
+ * 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.log4j.chainsaw;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A daemon thread the processes connections from a
+ * org.apache.log4j.net.SocketAppender.html.
+ *
+ * @author Oliver Burn
+ */
+class LoggingReceiver extends Thread {
+ /** used to log messages **/
+ private static final Logger LOG = Logger.getLogger(LoggingReceiver.class);
+
+ /**
+ * Helper that actually processes a client connection. It receives events
+ * and adds them to the supplied model.
+ *
+ * @author Oliver Burn
+ */
+ private class Slurper implements Runnable {
+ /** socket connection to read events from **/
+ private final Socket mClient;
+
+ /**
+ * Creates a new Slurper instance.
+ *
+ * @param aClient socket to receive events from
+ */
+ Slurper(Socket aClient) {
+ mClient = aClient;
+ }
+
+ /** loops getting the events **/
+ public void run() {
+ LOG.debug("Starting to get data");
+ try {
+ final ObjectInputStream ois =
+ new ObjectInputStream(mClient.getInputStream());
+ while (true) {
+ final LoggingEvent event = (LoggingEvent) ois.readObject();
+ mModel.addEvent(new EventDetails(event));
+ }
+ } catch (EOFException e) {
+ LOG.info("Reached EOF, closing connection");
+ } catch (SocketException e) {
+ LOG.info("Caught SocketException, closing connection");
+ } catch (IOException e) {
+ LOG.warn("Got IOException, closing connection", e);
+ } catch (ClassNotFoundException e) {
+ LOG.warn("Got ClassNotFoundException, closing connection", e);
+ }
+
+ try {
+ mClient.close();
+ } catch (IOException e) {
+ LOG.warn("Error closing connection", e);
+ }
+ }
+ }
+
+ /** where to put the events **/
+ private MyTableModel mModel;
+
+ /** server for listening for connections **/
+ private ServerSocket mSvrSock;
+
+ /**
+ * Creates a new LoggingReceiver instance.
+ *
+ * @param aModel model to place put received into
+ * @param aPort port to listen on
+ * @throws IOException if an error occurs
+ */
+ LoggingReceiver(MyTableModel aModel, int aPort) throws IOException {
+ setDaemon(true);
+ mModel = aModel;
+ mSvrSock = new ServerSocket(aPort);
+ }
+
+ /** Listens for client connections **/
+ public void run() {
+ LOG.info("Thread started");
+ try {
+ while (true) {
+ LOG.debug("Waiting for a connection");
+ final Socket client = mSvrSock.accept();
+ LOG.debug("Got a connection from " +
+ client.getInetAddress().getHostName());
+ final Thread t = new Thread(new Slurper(client));
+ t.setDaemon(true);
+ t.start();
+ }
+ } catch (IOException e) {
+ LOG.error("Error in accepting connections, stopping.", e);
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/chainsaw/Main.java b/java/src/org/apache/log4j/chainsaw/Main.java
new file mode 100644
index 0000000..c0c9aad
--- /dev/null
+++ b/java/src/org/apache/log4j/chainsaw/Main.java
@@ -0,0 +1,192 @@
+/*
+ * 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.log4j.chainsaw;
+
+import java.awt.BorderLayout;
+import java.awt.Dimension;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.IOException;
+import java.util.Properties;
+import javax.swing.BorderFactory;
+import javax.swing.JFrame;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+
+/**
+ * The main application.
+ *
+ * @author Oliver Burn
+ */
+public class Main
+ extends JFrame
+{
+ /** the default port number to listen on **/
+ private static final int DEFAULT_PORT = 4445;
+
+ /** name of property for port name **/
+ public static final String PORT_PROP_NAME = "chainsaw.port";
+
+ /** use to log messages **/
+ private static final Logger LOG = Logger.getLogger(Main.class);
+
+
+ /**
+ * Creates a new Main instance.
+ */
+ private Main() {
+ super("CHAINSAW - Log4J Log Viewer");
+ // create the all important model
+ final MyTableModel model = new MyTableModel();
+
+ //Create the menu bar.
+ final JMenuBar menuBar = new JMenuBar();
+ setJMenuBar(menuBar);
+ final JMenu menu = new JMenu("File");
+ menuBar.add(menu);
+
+ try {
+ final LoadXMLAction lxa = new LoadXMLAction(this, model);
+ final JMenuItem loadMenuItem = new JMenuItem("Load file...");
+ menu.add(loadMenuItem);
+ loadMenuItem.addActionListener(lxa);
+ } catch (NoClassDefFoundError e) {
+ LOG.info("Missing classes for XML parser", e);
+ JOptionPane.showMessageDialog(
+ this,
+ "XML parser not in classpath - unable to load XML events.",
+ "CHAINSAW",
+ JOptionPane.ERROR_MESSAGE);
+ } catch (Exception e) {
+ LOG.info("Unable to create the action to load XML files", e);
+ JOptionPane.showMessageDialog(
+ this,
+ "Unable to create a XML parser - unable to load XML events.",
+ "CHAINSAW",
+ JOptionPane.ERROR_MESSAGE);
+ }
+
+ final JMenuItem exitMenuItem = new JMenuItem("Exit");
+ menu.add(exitMenuItem);
+ exitMenuItem.addActionListener(ExitAction.INSTANCE);
+
+ // Add control panel
+ final ControlPanel cp = new ControlPanel(model);
+ getContentPane().add(cp, BorderLayout.NORTH);
+
+ // Create the table
+ final JTable table = new JTable(model);
+ table.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ final JScrollPane scrollPane = new JScrollPane(table);
+ scrollPane.setBorder(BorderFactory.createTitledBorder("Events: "));
+ scrollPane.setPreferredSize(new Dimension(900, 300));
+
+ // Create the details
+ final JPanel details = new DetailPanel(table, model);
+ details.setPreferredSize(new Dimension(900, 300));
+
+ // Add the table and stack trace into a splitter
+ final JSplitPane jsp =
+ new JSplitPane(JSplitPane.VERTICAL_SPLIT, scrollPane, details);
+ getContentPane().add(jsp, BorderLayout.CENTER);
+
+ addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent aEvent) {
+ ExitAction.INSTANCE.actionPerformed(null);
+ }
+ });
+
+ pack();
+ setVisible(true);
+
+ setupReceiver(model);
+ }
+
+ /**
+ * Setup recieving messages.
+ *
+ * @param aModel a MyTableModel value
+ */
+ private void setupReceiver(MyTableModel aModel) {
+ int port = DEFAULT_PORT;
+ final String strRep = System.getProperty(PORT_PROP_NAME);
+ if (strRep != null) {
+ try {
+ port = Integer.parseInt(strRep);
+ } catch (NumberFormatException nfe) {
+ LOG.fatal("Unable to parse " + PORT_PROP_NAME +
+ " property with value " + strRep + ".");
+ JOptionPane.showMessageDialog(
+ this,
+ "Unable to parse port number from '" + strRep +
+ "', quitting.",
+ "CHAINSAW",
+ JOptionPane.ERROR_MESSAGE);
+ System.exit(1);
+ }
+ }
+
+ try {
+ final LoggingReceiver lr = new LoggingReceiver(aModel, port);
+ lr.start();
+ } catch (IOException e) {
+ LOG.fatal("Unable to connect to socket server, quiting", e);
+ JOptionPane.showMessageDialog(
+ this,
+ "Unable to create socket on port " + port + ", quitting.",
+ "CHAINSAW",
+ JOptionPane.ERROR_MESSAGE);
+ System.exit(1);
+ }
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ // static methods
+ ////////////////////////////////////////////////////////////////////////////
+
+
+ /** initialise log4j **/
+ private static void initLog4J() {
+ final Properties props = new Properties();
+ props.setProperty("log4j.rootLogger", "DEBUG, A1");
+ props.setProperty("log4j.appender.A1",
+ "org.apache.log4j.ConsoleAppender");
+ props.setProperty("log4j.appender.A1.layout",
+ "org.apache.log4j.TTCCLayout");
+ PropertyConfigurator.configure(props);
+ }
+
+ /**
+ * The main method.
+ *
+ * @param aArgs ignored
+ */
+ public static void main(String[] aArgs) {
+ initLog4J();
+ new Main();
+ }
+}
diff --git a/java/src/org/apache/log4j/chainsaw/MyTableModel.java b/java/src/org/apache/log4j/chainsaw/MyTableModel.java
new file mode 100644
index 0000000..0d43272
--- /dev/null
+++ b/java/src/org/apache/log4j/chainsaw/MyTableModel.java
@@ -0,0 +1,390 @@
+/*
+ * 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.log4j.chainsaw;
+
+import java.text.DateFormat;
+import java.util.ArrayList;
+import java.util.Comparator;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+import java.util.SortedSet;
+import java.util.TreeSet;
+import javax.swing.table.AbstractTableModel;
+import org.apache.log4j.Priority;
+import org.apache.log4j.Logger;
+
+/**
+ * Represents a list of EventDetails objects that are sorted on
+ * logging time. Methods are provided to filter the events that are visible.
+ *
+ * @author Oliver Burn
+ */
+class MyTableModel
+ extends AbstractTableModel
+{
+
+ /** used to log messages **/
+ private static final Logger LOG = Logger.getLogger(MyTableModel.class);
+
+ /** use the compare logging events **/
+ private static final Comparator MY_COMP = new Comparator()
+ {
+ /** @see Comparator **/
+ public int compare(Object aObj1, Object aObj2) {
+ if ((aObj1 == null) && (aObj2 == null)) {
+ return 0; // treat as equal
+ } else if (aObj1 == null) {
+ return -1; // null less than everything
+ } else if (aObj2 == null) {
+ return 1; // think about it. :->
+ }
+
+ // will assume only have LoggingEvent
+ final EventDetails le1 = (EventDetails) aObj1;
+ final EventDetails le2 = (EventDetails) aObj2;
+
+ if (le1.getTimeStamp() < le2.getTimeStamp()) {
+ return 1;
+ }
+ // assume not two events are logged at exactly the same time
+ return -1;
+ }
+ };
+
+ /**
+ * Helper that actually processes incoming events.
+ * @author Oliver Burn
+ */
+ private class Processor
+ implements Runnable
+ {
+ /** loops getting the events **/
+ public void run() {
+ while (true) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ // ignore
+ }
+
+ synchronized (mLock) {
+ if (mPaused) {
+ continue;
+ }
+
+ boolean toHead = true; // were events added to head
+ boolean needUpdate = false;
+ final Iterator it = mPendingEvents.iterator();
+ while (it.hasNext()) {
+ final EventDetails event = (EventDetails) it.next();
+ mAllEvents.add(event);
+ toHead = toHead && (event == mAllEvents.first());
+ needUpdate = needUpdate || matchFilter(event);
+ }
+ mPendingEvents.clear();
+
+ if (needUpdate) {
+ updateFilteredEvents(toHead);
+ }
+ }
+ }
+
+ }
+ }
+
+
+ /** names of the columns in the table **/
+ private static final String[] COL_NAMES = {
+ "Time", "Priority", "Trace", "Category", "NDC", "Message"};
+
+ /** definition of an empty list **/
+ private static final EventDetails[] EMPTY_LIST = new EventDetails[] {};
+
+ /** used to format dates **/
+ private static final DateFormat DATE_FORMATTER =
+ DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.MEDIUM);
+
+ /** the lock to control access **/
+ private final Object mLock = new Object();
+ /** set of all logged events - not filtered **/
+ private final SortedSet mAllEvents = new TreeSet(MY_COMP);
+ /** events that are visible after filtering **/
+ private EventDetails[] mFilteredEvents = EMPTY_LIST;
+ /** list of events that are buffered for processing **/
+ private final List mPendingEvents = new ArrayList();
+ /** indicates whether event collection is paused to the UI **/
+ private boolean mPaused = false;
+
+ /** filter for the thread **/
+ private String mThreadFilter = "";
+ /** filter for the message **/
+ private String mMessageFilter = "";
+ /** filter for the NDC **/
+ private String mNDCFilter = "";
+ /** filter for the category **/
+ private String mCategoryFilter = "";
+ /** filter for the priority **/
+ private Priority mPriorityFilter = Priority.DEBUG;
+
+
+ /**
+ * Creates a new MyTableModel instance.
+ *
+ */
+ MyTableModel() {
+ final Thread t = new Thread(new Processor());
+ t.setDaemon(true);
+ t.start();
+ }
+
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Table Methods
+ ////////////////////////////////////////////////////////////////////////////
+
+ /** @see javax.swing.table.TableModel **/
+ public int getRowCount() {
+ synchronized (mLock) {
+ return mFilteredEvents.length;
+ }
+ }
+
+ /** @see javax.swing.table.TableModel **/
+ public int getColumnCount() {
+ // does not need to be synchronized
+ return COL_NAMES.length;
+ }
+
+ /** @see javax.swing.table.TableModel **/
+ public String getColumnName(int aCol) {
+ // does not need to be synchronized
+ return COL_NAMES[aCol];
+ }
+
+ /** @see javax.swing.table.TableModel **/
+ public Class getColumnClass(int aCol) {
+ // does not need to be synchronized
+ return (aCol == 2) ? Boolean.class : Object.class;
+ }
+
+ /** @see javax.swing.table.TableModel **/
+ public Object getValueAt(int aRow, int aCol) {
+ synchronized (mLock) {
+ final EventDetails event = mFilteredEvents[aRow];
+
+ if (aCol == 0) {
+ return DATE_FORMATTER.format(new Date(event.getTimeStamp()));
+ } else if (aCol == 1) {
+ return event.getPriority();
+ } else if (aCol == 2) {
+ return (event.getThrowableStrRep() == null)
+ ? Boolean.FALSE : Boolean.TRUE;
+ } else if (aCol == 3) {
+ return event.getCategoryName();
+ } else if (aCol == 4) {
+ return event.getNDC();
+ }
+ return event.getMessage();
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Public Methods
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Sets the priority to filter events on. Only events of equal or higher
+ * property are now displayed.
+ *
+ * @param aPriority the priority to filter on
+ */
+ public void setPriorityFilter(Priority aPriority) {
+ synchronized (mLock) {
+ mPriorityFilter = aPriority;
+ updateFilteredEvents(false);
+ }
+ }
+
+ /**
+ * Set the filter for the thread field.
+ *
+ * @param aStr the string to match
+ */
+ public void setThreadFilter(String aStr) {
+ synchronized (mLock) {
+ mThreadFilter = aStr.trim();
+ updateFilteredEvents(false);
+ }
+ }
+
+ /**
+ * Set the filter for the message field.
+ *
+ * @param aStr the string to match
+ */
+ public void setMessageFilter(String aStr) {
+ synchronized (mLock) {
+ mMessageFilter = aStr.trim();
+ updateFilteredEvents(false);
+ }
+ }
+
+ /**
+ * Set the filter for the NDC field.
+ *
+ * @param aStr the string to match
+ */
+ public void setNDCFilter(String aStr) {
+ synchronized (mLock) {
+ mNDCFilter = aStr.trim();
+ updateFilteredEvents(false);
+ }
+ }
+
+ /**
+ * Set the filter for the category field.
+ *
+ * @param aStr the string to match
+ */
+ public void setCategoryFilter(String aStr) {
+ synchronized (mLock) {
+ mCategoryFilter = aStr.trim();
+ updateFilteredEvents(false);
+ }
+ }
+
+ /**
+ * Add an event to the list.
+ *
+ * @param aEvent a EventDetails value
+ */
+ public void addEvent(EventDetails aEvent) {
+ synchronized (mLock) {
+ mPendingEvents.add(aEvent);
+ }
+ }
+
+ /**
+ * Clear the list of all events.
+ */
+ public void clear() {
+ synchronized (mLock) {
+ mAllEvents.clear();
+ mFilteredEvents = new EventDetails[0];
+ mPendingEvents.clear();
+ fireTableDataChanged();
+ }
+ }
+
+ /** Toggle whether collecting events **/
+ public void toggle() {
+ synchronized (mLock) {
+ mPaused = !mPaused;
+ }
+ }
+
+ /** @return whether currently paused collecting events **/
+ public boolean isPaused() {
+ synchronized (mLock) {
+ return mPaused;
+ }
+ }
+
+ /**
+ * Get the throwable information at a specified row in the filtered events.
+ *
+ * @param aRow the row index of the event
+ * @return the throwable information
+ */
+ public EventDetails getEventDetails(int aRow) {
+ synchronized (mLock) {
+ return mFilteredEvents[aRow];
+ }
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Private methods
+ ////////////////////////////////////////////////////////////////////////////
+
+ /**
+ * Update the filtered events data structure.
+ * @param aInsertedToFront indicates whether events were added to front of
+ * the events. If true, then the current first event must still exist
+ * in the list after the filter is applied.
+ */
+ private void updateFilteredEvents(boolean aInsertedToFront) {
+ final long start = System.currentTimeMillis();
+ final List filtered = new ArrayList();
+ final int size = mAllEvents.size();
+ final Iterator it = mAllEvents.iterator();
+
+ while (it.hasNext()) {
+ final EventDetails event = (EventDetails) it.next();
+ if (matchFilter(event)) {
+ filtered.add(event);
+ }
+ }
+
+ final EventDetails lastFirst = (mFilteredEvents.length == 0)
+ ? null
+ : mFilteredEvents[0];
+ mFilteredEvents = (EventDetails[]) filtered.toArray(EMPTY_LIST);
+
+ if (aInsertedToFront && (lastFirst != null)) {
+ final int index = filtered.indexOf(lastFirst);
+ if (index < 1) {
+ LOG.warn("In strange state");
+ fireTableDataChanged();
+ } else {
+ fireTableRowsInserted(0, index - 1);
+ }
+ } else {
+ fireTableDataChanged();
+ }
+
+ final long end = System.currentTimeMillis();
+ LOG.debug("Total time [ms]: " + (end - start)
+ + " in update, size: " + size);
+ }
+
+ /**
+ * Returns whether an event matches the filters.
+ *
+ * @param aEvent the event to check for a match
+ * @return whether the event matches
+ */
+ private boolean matchFilter(EventDetails aEvent) {
+ if (aEvent.getPriority().isGreaterOrEqual(mPriorityFilter) &&
+ (aEvent.getThreadName().indexOf(mThreadFilter) >= 0) &&
+ (aEvent.getCategoryName().indexOf(mCategoryFilter) >= 0) &&
+ ((mNDCFilter.length() == 0) ||
+ ((aEvent.getNDC() != null) &&
+ (aEvent.getNDC().indexOf(mNDCFilter) >= 0))))
+ {
+ final String rm = aEvent.getMessage();
+ if (rm == null) {
+ // only match if we have not filtering in place
+ return (mMessageFilter.length() == 0);
+ } else {
+ return (rm.indexOf(mMessageFilter) >= 0);
+ }
+ }
+
+ return false; // by default not match
+ }
+}
diff --git a/java/src/org/apache/log4j/chainsaw/XMLFileHandler.java b/java/src/org/apache/log4j/chainsaw/XMLFileHandler.java
new file mode 100644
index 0000000..2f9af51
--- /dev/null
+++ b/java/src/org/apache/log4j/chainsaw/XMLFileHandler.java
@@ -0,0 +1,170 @@
+/*
+ * 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.log4j.chainsaw;
+
+import java.util.StringTokenizer;
+import org.apache.log4j.Level;
+import org.xml.sax.Attributes;
+import org.xml.sax.SAXException;
+import org.xml.sax.helpers.DefaultHandler;
+
+/**
+ * A content handler for document containing Log4J events logged using the
+ * XMLLayout class. It will create events and add them to a supplied model.
+ *
+ * @author Oliver Burn
+ * @version 1.0
+ */
+class XMLFileHandler
+ extends DefaultHandler
+{
+ /** represents the event tag **/
+ private static final String TAG_EVENT = "log4j:event";
+ /** represents the message tag **/
+ private static final String TAG_MESSAGE = "log4j:message";
+ /** represents the ndc tag **/
+ private static final String TAG_NDC = "log4j:NDC";
+ /** represents the throwable tag **/
+ private static final String TAG_THROWABLE = "log4j:throwable";
+ /** represents the location info tag **/
+ private static final String TAG_LOCATION_INFO = "log4j:locationInfo";
+
+ /** where to put the events **/
+ private final MyTableModel mModel;
+ /** the number of events in the document **/
+ private int mNumEvents;
+
+ /** the time of the event **/
+ private long mTimeStamp;
+ /** the priority (level) of the event **/
+ private Level mLevel;
+ /** the category of the event **/
+ private String mCategoryName;
+ /** the NDC for the event **/
+ private String mNDC;
+ /** the thread for the event **/
+ private String mThreadName;
+ /** the msg for the event **/
+ private String mMessage;
+ /** the throwable details the event **/
+ private String[] mThrowableStrRep;
+ /** the location details for the event **/
+ private String mLocationDetails;
+ /** buffer for collecting text **/
+ private final StringBuffer mBuf = new StringBuffer();
+
+ /**
+ * Creates a new XMLFileHandler instance.
+ *
+ * @param aModel where to add the events
+ */
+ XMLFileHandler(MyTableModel aModel) {
+ mModel = aModel;
+ }
+
+ /** @see DefaultHandler **/
+ public void startDocument()
+ throws SAXException
+ {
+ mNumEvents = 0;
+ }
+
+ /** @see DefaultHandler **/
+ public void characters(char[] aChars, int aStart, int aLength) {
+ mBuf.append(String.valueOf(aChars, aStart, aLength));
+ }
+
+ /** @see DefaultHandler **/
+ public void endElement(String aNamespaceURI,
+ String aLocalName,
+ String aQName)
+ {
+ if (TAG_EVENT.equals(aQName)) {
+ addEvent();
+ resetData();
+ } else if (TAG_NDC.equals(aQName)) {
+ mNDC = mBuf.toString();
+ } else if (TAG_MESSAGE.equals(aQName)) {
+ mMessage = mBuf.toString();
+ } else if (TAG_THROWABLE.equals(aQName)) {
+ final StringTokenizer st =
+ new StringTokenizer(mBuf.toString(), "\n\t");
+ mThrowableStrRep = new String[st.countTokens()];
+ if (mThrowableStrRep.length > 0) {
+ mThrowableStrRep[0] = st.nextToken();
+ for (int i = 1; i < mThrowableStrRep.length; i++) {
+ mThrowableStrRep[i] = "\t" + st.nextToken();
+ }
+ }
+ }
+ }
+
+ /** @see DefaultHandler **/
+ public void startElement(String aNamespaceURI,
+ String aLocalName,
+ String aQName,
+ Attributes aAtts)
+ {
+ mBuf.setLength(0);
+
+ if (TAG_EVENT.equals(aQName)) {
+ mThreadName = aAtts.getValue("thread");
+ mTimeStamp = Long.parseLong(aAtts.getValue("timestamp"));
+ mCategoryName = aAtts.getValue("logger");
+ mLevel = Level.toLevel(aAtts.getValue("level"));
+ } else if (TAG_LOCATION_INFO.equals(aQName)) {
+ mLocationDetails = aAtts.getValue("class") + "."
+ + aAtts.getValue("method")
+ + "(" + aAtts.getValue("file") + ":" + aAtts.getValue("line")
+ + ")";
+ }
+ }
+
+ /** @return the number of events in the document **/
+ int getNumEvents() {
+ return mNumEvents;
+ }
+
+ ////////////////////////////////////////////////////////////////////////////
+ // Private methods
+ ////////////////////////////////////////////////////////////////////////////
+
+ /** Add an event to the model **/
+ private void addEvent() {
+ mModel.addEvent(new EventDetails(mTimeStamp,
+ mLevel,
+ mCategoryName,
+ mNDC,
+ mThreadName,
+ mMessage,
+ mThrowableStrRep,
+ mLocationDetails));
+ mNumEvents++;
+ }
+
+ /** Reset the data for an event **/
+ private void resetData() {
+ mTimeStamp = 0;
+ mLevel = null;
+ mCategoryName = null;
+ mNDC = null;
+ mThreadName = null;
+ mMessage = null;
+ mThrowableStrRep = null;
+ mLocationDetails = null;
+ }
+}
diff --git a/java/src/org/apache/log4j/chainsaw/doc-files/screen_01.png b/java/src/org/apache/log4j/chainsaw/doc-files/screen_01.png
new file mode 100644
index 0000000..1a9c26d
Binary files /dev/null and b/java/src/org/apache/log4j/chainsaw/doc-files/screen_01.png differ
diff --git a/java/src/org/apache/log4j/chainsaw/package.html b/java/src/org/apache/log4j/chainsaw/package.html
new file mode 100644
index 0000000..5b01480
--- /dev/null
+++ b/java/src/org/apache/log4j/chainsaw/package.html
@@ -0,0 +1,118 @@
+
+
+
+
+
+ Chainsaw Tool
+
+
+
+
+ Chainsaw is a GUI log viewer and filter for the log4j
+package. By default it listens for LoggingEvent objects sent using
+the SocketAppender and
+displays them in a table. The events can be filtered based on:
+
+
+ - Level
+
+ - Thread name
+
+ - Logger
+ - Message
+
+ - NDC
+
+
+ All the details for each event can be displayed by selecting
+ the event in the table.
+
+ Chainsaw also supports loading a events logged to a file using
+ the XMLLayout format. This
+ is great for analysing log files, and means you do not need to
+ keep Chainsaw running continously. It is easy to add support
+ for loading events from other sources like JDBC.
+
+ A picture is worth a thousand words:
+
+
.
+
+ Finally, why is it called chainsaw?
+ Because it cuts your log (file) down to size. :-)
+
+
+
+ Requirements
+
+ Chainsaw is based on the Swing API which requires JDK 1.2 or later.
+
+
+ Running chainsaw
+
+ Setup
+ You need to include the log4j.jar in the classpath.
+
+
Usage
+
+ The command line usage is:
+
+ java -D<property>=<value> org.apache.log4j.chainsaw.Main
+
+ The default behaviour of chainsaw can be changed by setting system properties
+ using the -D<property>=<value> arguments to java. The
+ following table describes what properties can be set:
+
+
+
+
+ | Property |
+ Description |
+
+ | chainsaw.port |
+ Indicates which port to listen for connections on. Defaults
+ to "4445".
+ |
+
+
+
+
+ Configuring Log4J
+
+ You will need to configure log4j to send logging events to
+ Chainsaw. Here is a sample log4j.properties file
+ for sending logging events to Chainsaw.
+
+
+log4j.rootLogger=DEBUG, CHAINSAW_CLIENT
+
+log4j.appender.CHAINSAW_CLIENT=org.apache.log4j.net.SocketAppender
+log4j.appender.CHAINSAW_CLIENT.RemoteHost=localhost
+log4j.appender.CHAINSAW_CLIENT.Port=4445
+log4j.appender.CHAINSAW_CLIENT.LocationInfo=true
+
+
+
+
+
+
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/config/PropertyGetter.java b/java/src/org/apache/log4j/config/PropertyGetter.java
new file mode 100644
index 0000000..5233699
--- /dev/null
+++ b/java/src/org/apache/log4j/config/PropertyGetter.java
@@ -0,0 +1,108 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.Priority;
+import org.apache.log4j.helpers.LogLog;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.InterruptedIOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+
+/**
+ Used for inferring configuration information for a log4j's component.
+
+ @author Anders Kristensen
+ */
+public class PropertyGetter {
+ protected static final Object[] NULL_ARG = new Object[] {};
+ protected Object obj;
+ protected PropertyDescriptor[] props;
+
+ public interface PropertyCallback {
+ void foundProperty(Object obj, String prefix, String name, Object value);
+ }
+
+ /**
+ Create a new PropertyGetter for the specified Object. This is done
+ in prepartion for invoking {@link
+ #getProperties(PropertyGetter.PropertyCallback, String)} one or
+ more times.
+
+ @param obj the object for which to set properties */
+ public
+ PropertyGetter(Object obj) throws IntrospectionException {
+ BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
+ props = bi.getPropertyDescriptors();
+ this.obj = obj;
+ }
+
+ public
+ static
+ void getProperties(Object obj, PropertyCallback callback, String prefix) {
+ try {
+ new PropertyGetter(obj).getProperties(callback, prefix);
+ } catch (IntrospectionException ex) {
+ LogLog.error("Failed to introspect object " + obj, ex);
+ }
+ }
+
+ public
+ void getProperties(PropertyCallback callback, String prefix) {
+ for (int i = 0; i < props.length; i++) {
+ Method getter = props[i].getReadMethod();
+ if (getter == null) continue;
+ if (!isHandledType(getter.getReturnType())) {
+ //System.err.println("Ignoring " + props[i].getName() +" " + getter.getReturnType());
+ continue;
+ }
+ String name = props[i].getName();
+ try {
+ Object result = getter.invoke(obj, NULL_ARG);
+ //System.err.println("PROP " + name +": " + result);
+ if (result != null) {
+ callback.foundProperty(obj, prefix, name, result);
+ }
+ } catch (IllegalAccessException ex) {
+ LogLog.warn("Failed to get value of property " + name);
+ } catch (InvocationTargetException ex) {
+ if (ex.getTargetException() instanceof InterruptedException
+ || ex.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.warn("Failed to get value of property " + name);
+ } catch (RuntimeException ex) {
+ LogLog.warn("Failed to get value of property " + name);
+ }
+ }
+ }
+
+ protected
+ boolean isHandledType(Class type) {
+ return String.class.isAssignableFrom(type) ||
+ Integer.TYPE.isAssignableFrom(type) ||
+ Long.TYPE.isAssignableFrom(type) ||
+ Boolean.TYPE.isAssignableFrom(type) ||
+ Priority.class.isAssignableFrom(type);
+ }
+}
diff --git a/java/src/org/apache/log4j/config/PropertyPrinter.java b/java/src/org/apache/log4j/config/PropertyPrinter.java
new file mode 100644
index 0000000..517079f
--- /dev/null
+++ b/java/src/org/apache/log4j/config/PropertyPrinter.java
@@ -0,0 +1,168 @@
+/*
+ * 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.log4j.config;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+
+import java.io.PrintWriter;
+import java.util.Enumeration;
+import java.util.Hashtable;
+
+/**
+ Prints the configuration of the log4j default hierarchy
+ (which needs to be auto-initialized) as a propoperties file
+ on a {@link PrintWriter}.
+
+ @author Anders Kristensen
+ */
+public class PropertyPrinter implements PropertyGetter.PropertyCallback {
+ protected int numAppenders = 0;
+ protected Hashtable appenderNames = new Hashtable();
+ protected Hashtable layoutNames = new Hashtable();
+ protected PrintWriter out;
+ protected boolean doCapitalize;
+
+ public
+ PropertyPrinter(PrintWriter out) {
+ this(out, false);
+ }
+
+ public
+ PropertyPrinter(PrintWriter out, boolean doCapitalize) {
+ this.out = out;
+ this.doCapitalize = doCapitalize;
+
+ print(out);
+ out.flush();
+ }
+
+ protected
+ String genAppName() {
+ return "A" + numAppenders++;
+ }
+
+ /**
+ * Returns true if the specified appender name is considered to have
+ * been generated, that is, if it is of the form A[0-9]+.
+ */
+ protected
+ boolean isGenAppName(String name) {
+ if (name.length() < 2 || name.charAt(0) != 'A') return false;
+
+ for (int i = 0; i < name.length(); i++) {
+ if (name.charAt(i) < '0' || name.charAt(i) > '9') return false;
+ }
+ return true;
+ }
+
+ /**
+ * Prints the configuration of the default log4j hierarchy as a Java
+ * properties file on the specified Writer.
+ *
+ * N.B. print() can be invoked only once!
+ */
+ public
+ void print(PrintWriter out) {
+ printOptions(out, Logger.getRootLogger());
+
+ Enumeration cats = LogManager.getCurrentLoggers();
+ while (cats.hasMoreElements()) {
+ printOptions(out, (Logger) cats.nextElement());
+ }
+ }
+
+ /**
+ * @since 1.2.15
+ */
+ protected
+ void printOptions(PrintWriter out, Category cat) {
+ Enumeration appenders = cat.getAllAppenders();
+ Level prio = cat.getLevel();
+ String appenderString = (prio == null ? "" : prio.toString());
+
+ while (appenders.hasMoreElements()) {
+ Appender app = (Appender) appenders.nextElement();
+ String name;
+
+ if ((name = (String) appenderNames.get(app)) == null) {
+
+ // first assign name to the appender
+ if ((name = app.getName()) == null || isGenAppName(name)) {
+ name = genAppName();
+ }
+ appenderNames.put(app, name);
+
+ printOptions(out, app, "log4j.appender."+name);
+ if (app.getLayout() != null) {
+ printOptions(out, app.getLayout(), "log4j.appender."+name+".layout");
+ }
+ }
+ appenderString += ", " + name;
+ }
+ String catKey = (cat == Logger.getRootLogger())
+ ? "log4j.rootLogger"
+ : "log4j.logger." + cat.getName();
+ if (appenderString != "") {
+ out.println(catKey + "=" + appenderString);
+ }
+ if (!cat.getAdditivity() && cat != Logger.getRootLogger()) {
+ out.println("log4j.additivity." + cat.getName() + "=false");
+ }
+ }
+
+ protected void printOptions(PrintWriter out, Logger cat) {
+ printOptions(out, (Category) cat);
+ }
+
+ protected
+ void printOptions(PrintWriter out, Object obj, String fullname) {
+ out.println(fullname + "=" + obj.getClass().getName());
+ PropertyGetter.getProperties(obj, this, fullname + ".");
+ }
+
+ public void foundProperty(Object obj, String prefix, String name, Object value) {
+ // XXX: Properties encode value.toString()
+ if (obj instanceof Appender && "name".equals(name)) {
+ return;
+ }
+ if (doCapitalize) {
+ name = capitalize(name);
+ }
+ out.println(prefix + name + "=" + value.toString());
+ }
+
+ public static String capitalize(String name) {
+ if (Character.isLowerCase(name.charAt(0))) {
+ if (name.length() == 1 || Character.isLowerCase(name.charAt(1))) {
+ StringBuffer newname = new StringBuffer(name);
+ newname.setCharAt(0, Character.toUpperCase(name.charAt(0)));
+ return newname.toString();
+ }
+ }
+ return name;
+ }
+
+ // for testing
+ public static void main(String[] args) {
+ new PropertyPrinter(new PrintWriter(System.out));
+ }
+}
diff --git a/java/src/org/apache/log4j/config/PropertySetter.java b/java/src/org/apache/log4j/config/PropertySetter.java
new file mode 100644
index 0000000..61d36ef
--- /dev/null
+++ b/java/src/org/apache/log4j/config/PropertySetter.java
@@ -0,0 +1,310 @@
+/*
+ * 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.
+ */
+
+// Contributors: Georg Lundesgaard
+
+package org.apache.log4j.config;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Priority;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.ErrorHandler;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.io.InterruptedIOException;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Enumeration;
+import java.util.Properties;
+
+/**
+ General purpose Object property setter. Clients repeatedly invokes
+ {@link #setProperty setProperty(name,value)} in order to invoke setters
+ on the Object specified in the constructor. This class relies on the
+ JavaBeans {@link Introspector} to analyze the given Object Class using
+ reflection.
+
+
Usage:
+
+ PropertySetter ps = new PropertySetter(anObject);
+ ps.set("name", "Joe");
+ ps.set("age", "32");
+ ps.set("isMale", "true");
+
+ will cause the invocations anObject.setName("Joe"), anObject.setAge(32),
+ and setMale(true) if such methods exist with those signatures.
+ Otherwise an {@link IntrospectionException} are thrown.
+
+ @author Anders Kristensen
+ @since 1.1
+ */
+public class PropertySetter {
+ protected Object obj;
+ protected PropertyDescriptor[] props;
+
+ /**
+ Create a new PropertySetter for the specified Object. This is done
+ in prepartion for invoking {@link #setProperty} one or more times.
+
+ @param obj the object for which to set properties
+ */
+ public
+ PropertySetter(Object obj) {
+ this.obj = obj;
+ }
+
+ /**
+ Uses JavaBeans {@link Introspector} to computer setters of object to be
+ configured.
+ */
+ protected
+ void introspect() {
+ try {
+ BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
+ props = bi.getPropertyDescriptors();
+ } catch (IntrospectionException ex) {
+ LogLog.error("Failed to introspect "+obj+": " + ex.getMessage());
+ props = new PropertyDescriptor[0];
+ }
+ }
+
+
+ /**
+ Set the properties of an object passed as a parameter in one
+ go. The properties are parsed relative to a
+ prefix.
+
+ @param obj The object to configure.
+ @param properties A java.util.Properties containing keys and values.
+ @param prefix Only keys having the specified prefix will be set.
+ */
+ public
+ static
+ void setProperties(Object obj, Properties properties, String prefix) {
+ new PropertySetter(obj).setProperties(properties, prefix);
+ }
+
+
+ /**
+ Set the properites for the object that match the
+ prefix passed as parameter.
+
+
+ */
+ public
+ void setProperties(Properties properties, String prefix) {
+ int len = prefix.length();
+
+ for (Enumeration e = properties.propertyNames(); e.hasMoreElements(); ) {
+ String key = (String) e.nextElement();
+
+ // handle only properties that start with the desired frefix.
+ if (key.startsWith(prefix)) {
+
+
+ // ignore key if it contains dots after the prefix
+ if (key.indexOf('.', len + 1) > 0) {
+ //System.err.println("----------Ignoring---["+key
+ // +"], prefix=["+prefix+"].");
+ continue;
+ }
+
+ String value = OptionConverter.findAndSubst(key, properties);
+ key = key.substring(len);
+ if (("layout".equals(key) || "errorhandler".equals(key)) && obj instanceof Appender) {
+ continue;
+ }
+ //
+ // if the property type is an OptionHandler
+ // (for example, triggeringPolicy of org.apache.log4j.rolling.RollingFileAppender)
+ PropertyDescriptor prop = getPropertyDescriptor(Introspector.decapitalize(key));
+ if (prop != null
+ && OptionHandler.class.isAssignableFrom(prop.getPropertyType())
+ && prop.getWriteMethod() != null) {
+ OptionHandler opt = (OptionHandler)
+ OptionConverter.instantiateByKey(properties, prefix + key,
+ prop.getPropertyType(),
+ null);
+ PropertySetter setter = new PropertySetter(opt);
+ setter.setProperties(properties, prefix + key + ".");
+ try {
+ prop.getWriteMethod().invoke(this.obj, new Object[] { opt });
+ } catch(IllegalAccessException ex) {
+ LogLog.warn("Failed to set property [" + key +
+ "] to value \"" + value + "\". ", ex);
+ } catch(InvocationTargetException ex) {
+ if (ex.getTargetException() instanceof InterruptedException
+ || ex.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.warn("Failed to set property [" + key +
+ "] to value \"" + value + "\". ", ex);
+ } catch(RuntimeException ex) {
+ LogLog.warn("Failed to set property [" + key +
+ "] to value \"" + value + "\". ", ex);
+ }
+ continue;
+ }
+
+ setProperty(key, value);
+ }
+ }
+ activate();
+ }
+
+ /**
+ Set a property on this PropertySetter's Object. If successful, this
+ method will invoke a setter method on the underlying Object. The
+ setter is the one for the specified property name and the value is
+ determined partly from the setter argument type and partly from the
+ value specified in the call to this method.
+
+ If the setter expects a String no conversion is necessary.
+ If it expects an int, then an attempt is made to convert 'value'
+ to an int using new Integer(value). If the setter expects a boolean,
+ the conversion is by new Boolean(value).
+
+ @param name name of the property
+ @param value String value of the property
+ */
+ public
+ void setProperty(String name, String value) {
+ if (value == null) return;
+
+ name = Introspector.decapitalize(name);
+ PropertyDescriptor prop = getPropertyDescriptor(name);
+
+ //LogLog.debug("---------Key: "+name+", type="+prop.getPropertyType());
+
+ if (prop == null) {
+ LogLog.warn("No such property [" + name + "] in "+
+ obj.getClass().getName()+"." );
+ } else {
+ try {
+ setProperty(prop, name, value);
+ } catch (PropertySetterException ex) {
+ LogLog.warn("Failed to set property [" + name +
+ "] to value \"" + value + "\". ", ex.rootCause);
+ }
+ }
+ }
+
+ /**
+ Set the named property given a {@link PropertyDescriptor}.
+
+ @param prop A PropertyDescriptor describing the characteristics
+ of the property to set.
+ @param name The named of the property to set.
+ @param value The value of the property.
+ */
+ public
+ void setProperty(PropertyDescriptor prop, String name, String value)
+ throws PropertySetterException {
+ Method setter = prop.getWriteMethod();
+ if (setter == null) {
+ throw new PropertySetterException("No setter for property ["+name+"].");
+ }
+ Class[] paramTypes = setter.getParameterTypes();
+ if (paramTypes.length != 1) {
+ throw new PropertySetterException("#params for setter != 1");
+ }
+
+ Object arg;
+ try {
+ arg = convertArg(value, paramTypes[0]);
+ } catch (Throwable t) {
+ throw new PropertySetterException("Conversion to type ["+paramTypes[0]+
+ "] failed. Reason: "+t);
+ }
+ if (arg == null) {
+ throw new PropertySetterException(
+ "Conversion to type ["+paramTypes[0]+"] failed.");
+ }
+ LogLog.debug("Setting property [" + name + "] to [" +arg+"].");
+ try {
+ setter.invoke(obj, new Object[] { arg });
+ } catch (IllegalAccessException ex) {
+ throw new PropertySetterException(ex);
+ } catch (InvocationTargetException ex) {
+ if (ex.getTargetException() instanceof InterruptedException
+ || ex.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ throw new PropertySetterException(ex);
+ } catch (RuntimeException ex) {
+ throw new PropertySetterException(ex);
+ }
+ }
+
+
+ /**
+ Convert val a String parameter to an object of a
+ given type.
+ */
+ protected
+ Object convertArg(String val, Class type) {
+ if(val == null)
+ return null;
+
+ String v = val.trim();
+ if (String.class.isAssignableFrom(type)) {
+ return val;
+ } else if (Integer.TYPE.isAssignableFrom(type)) {
+ return new Integer(v);
+ } else if (Long.TYPE.isAssignableFrom(type)) {
+ return new Long(v);
+ } else if (Boolean.TYPE.isAssignableFrom(type)) {
+ if ("true".equalsIgnoreCase(v)) {
+ return Boolean.TRUE;
+ } else if ("false".equalsIgnoreCase(v)) {
+ return Boolean.FALSE;
+ }
+ } else if (Priority.class.isAssignableFrom(type)) {
+ return OptionConverter.toLevel(v, (Level) Level.DEBUG);
+ } else if (ErrorHandler.class.isAssignableFrom(type)) {
+ return OptionConverter.instantiateByClassName(v,
+ ErrorHandler.class, null);
+ }
+ return null;
+ }
+
+
+ protected
+ PropertyDescriptor getPropertyDescriptor(String name) {
+ if (props == null) introspect();
+
+ for (int i = 0; i < props.length; i++) {
+ if (name.equals(props[i].getName())) {
+ return props[i];
+ }
+ }
+ return null;
+ }
+
+ public
+ void activate() {
+ if (obj instanceof OptionHandler) {
+ ((OptionHandler) obj).activateOptions();
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/config/PropertySetterException.java b/java/src/org/apache/log4j/config/PropertySetterException.java
new file mode 100644
index 0000000..c6314cc
--- /dev/null
+++ b/java/src/org/apache/log4j/config/PropertySetterException.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.log4j.config;
+
+/**
+ * Thrown when an error is encountered whilst attempting to set a property
+ * using the {@link PropertySetter} utility class.
+ *
+ * @author Anders Kristensen
+ * @since 1.1
+ */
+public class PropertySetterException extends Exception {
+ private static final long serialVersionUID = -1352613734254235861L;
+ protected Throwable rootCause;
+
+ public
+ PropertySetterException(String msg) {
+ super(msg);
+ }
+
+ public
+ PropertySetterException(Throwable rootCause)
+ {
+ super();
+ this.rootCause = rootCause;
+ }
+
+ /**
+ Returns descriptive text on the cause of this exception.
+ */
+ public
+ String getMessage() {
+ String msg = super.getMessage();
+ if (msg == null && rootCause != null) {
+ msg = rootCause.getMessage();
+ }
+ return msg;
+ }
+}
diff --git a/java/src/org/apache/log4j/config/package.html b/java/src/org/apache/log4j/config/package.html
new file mode 100644
index 0000000..973dce4
--- /dev/null
+++ b/java/src/org/apache/log4j/config/package.html
@@ -0,0 +1,23 @@
+
+
+
+
+Package used in getting/setting component properties.
+
+
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java b/java/src/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java
new file mode 100644
index 0000000..737ee85
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/AbsoluteTimeDateFormat.java
@@ -0,0 +1,145 @@
+/*
+ * 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.log4j.helpers;
+
+import java.util.Date;
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.text.DateFormat;
+
+
+/**
+ Formats a {@link Date} in the format "HH:mm:ss,SSS" for example,
+ "15:49:37,459".
+
+ @author Ceki Gülcü
+ @author Andrew Vajoczki
+
+ @since 0.7.5
+*/
+public class AbsoluteTimeDateFormat extends DateFormat {
+ private static final long serialVersionUID = -388856345976723342L;
+
+ /**
+ String constant used to specify {@link
+ org.apache.log4j.helpers.AbsoluteTimeDateFormat} in layouts. Current
+ value is ABSOLUTE. */
+ public final static String ABS_TIME_DATE_FORMAT = "ABSOLUTE";
+
+ /**
+ String constant used to specify {@link
+ org.apache.log4j.helpers.DateTimeDateFormat} in layouts. Current
+ value is DATE.
+ */
+ public final static String DATE_AND_TIME_DATE_FORMAT = "DATE";
+
+ /**
+ String constant used to specify {@link
+ org.apache.log4j.helpers.ISO8601DateFormat} in layouts. Current
+ value is ISO8601.
+ */
+ public final static String ISO8601_DATE_FORMAT = "ISO8601";
+
+ public
+ AbsoluteTimeDateFormat() {
+ setCalendar(Calendar.getInstance());
+ }
+
+ public
+ AbsoluteTimeDateFormat(TimeZone timeZone) {
+ setCalendar(Calendar.getInstance(timeZone));
+ }
+
+ private static long previousTime;
+ private static char[] previousTimeWithoutMillis = new char[9]; // "HH:mm:ss."
+
+ /**
+ Appends to sbuf the time in the format
+ "HH:mm:ss,SSS" for example, "15:49:37,459"
+
+ @param date the date to format
+ @param sbuf the string buffer to write to
+ @param fieldPosition remains untouched
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+
+ long now = date.getTime();
+ int millis = (int)(now % 1000);
+
+ if ((now - millis) != previousTime || previousTimeWithoutMillis[0] == 0) {
+ // We reach this point at most once per second
+ // across all threads instead of each time format()
+ // is called. This saves considerable CPU time.
+
+ calendar.setTime(date);
+
+ int start = sbuf.length();
+
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ if(hour < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(hour);
+ sbuf.append(':');
+
+ int mins = calendar.get(Calendar.MINUTE);
+ if(mins < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(mins);
+ sbuf.append(':');
+
+ int secs = calendar.get(Calendar.SECOND);
+ if(secs < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(secs);
+ sbuf.append(',');
+
+ // store the time string for next time to avoid recomputation
+ sbuf.getChars(start, sbuf.length(), previousTimeWithoutMillis, 0);
+
+ previousTime = now - millis;
+ }
+ else {
+ sbuf.append(previousTimeWithoutMillis);
+ }
+
+
+
+ if(millis < 100)
+ sbuf.append('0');
+ if(millis < 10)
+ sbuf.append('0');
+
+ sbuf.append(millis);
+ return sbuf;
+ }
+
+ /**
+ This method does not do anything but return null.
+ */
+ public
+ Date parse(String s, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/AppenderAttachableImpl.java b/java/src/org/apache/log4j/helpers/AppenderAttachableImpl.java
new file mode 100644
index 0000000..0e8cd38
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/AppenderAttachableImpl.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.log4j.helpers;
+
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.LoggingEvent;
+
+import org.apache.log4j.Appender;
+import java.util.Vector;
+import java.util.Enumeration;
+
+/**
+ A straightforward implementation of the {@link AppenderAttachable}
+ interface.
+
+ @author Ceki Gülcü
+ @since version 0.9.1 */
+public class AppenderAttachableImpl implements AppenderAttachable {
+
+ /** Array of appenders. */
+ protected Vector appenderList;
+
+ /**
+ Attach an appender. If the appender is already in the list in
+ won't be added again.
+ */
+ public
+ void addAppender(Appender newAppender) {
+ // Null values for newAppender parameter are strictly forbidden.
+ if(newAppender == null)
+ return;
+
+ if(appenderList == null) {
+ appenderList = new Vector(1);
+ }
+ if(!appenderList.contains(newAppender))
+ appenderList.addElement(newAppender);
+ }
+
+ /**
+ Call the doAppend method on all attached appenders. */
+ public
+ int appendLoopOnAppenders(LoggingEvent event) {
+ int size = 0;
+ Appender appender;
+
+ if(appenderList != null) {
+ size = appenderList.size();
+ for(int i = 0; i < size; i++) {
+ appender = (Appender) appenderList.elementAt(i);
+ appender.doAppend(event);
+ }
+ }
+ return size;
+ }
+
+
+ /**
+ Get all attached appenders as an Enumeration. If there are no
+ attached appenders null is returned.
+
+ @return Enumeration An enumeration of attached appenders.
+ */
+ public
+ Enumeration getAllAppenders() {
+ if(appenderList == null)
+ return null;
+ else
+ return appenderList.elements();
+ }
+
+ /**
+ Look for an attached appender named as name.
+
+ Return the appender with that name if in the list. Return null
+ otherwise.
+
+ */
+ public
+ Appender getAppender(String name) {
+ if(appenderList == null || name == null)
+ return null;
+
+ int size = appenderList.size();
+ Appender appender;
+ for(int i = 0; i < size; i++) {
+ appender = (Appender) appenderList.elementAt(i);
+ if(name.equals(appender.getName()))
+ return appender;
+ }
+ return null;
+ }
+
+
+ /**
+ Returns true if the specified appender is in the
+ list of attached appenders, false otherwise.
+
+ @since 1.2 */
+ public
+ boolean isAttached(Appender appender) {
+ if(appenderList == null || appender == null)
+ return false;
+
+ int size = appenderList.size();
+ Appender a;
+ for(int i = 0; i < size; i++) {
+ a = (Appender) appenderList.elementAt(i);
+ if(a == appender)
+ return true;
+ }
+ return false;
+ }
+
+
+
+ /**
+ * Remove and close all previously attached appenders.
+ * */
+ public
+ void removeAllAppenders() {
+ if(appenderList != null) {
+ int len = appenderList.size();
+ for(int i = 0; i < len; i++) {
+ Appender a = (Appender) appenderList.elementAt(i);
+ a.close();
+ }
+ appenderList.removeAllElements();
+ appenderList = null;
+ }
+ }
+
+
+ /**
+ Remove the appender passed as parameter form the list of attached
+ appenders. */
+ public
+ void removeAppender(Appender appender) {
+ if(appender == null || appenderList == null)
+ return;
+ appenderList.removeElement(appender);
+ }
+
+
+ /**
+ Remove the appender with the name passed as parameter form the
+ list of appenders.
+ */
+ public
+ void removeAppender(String name) {
+ if(name == null || appenderList == null) return;
+ int size = appenderList.size();
+ for(int i = 0; i < size; i++) {
+ if(name.equals(((Appender)appenderList.elementAt(i)).getName())) {
+ appenderList.removeElementAt(i);
+ break;
+ }
+ }
+ }
+
+}
diff --git a/java/src/org/apache/log4j/helpers/BoundedFIFO.java b/java/src/org/apache/log4j/helpers/BoundedFIFO.java
new file mode 100644
index 0000000..e5ce96c
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/BoundedFIFO.java
@@ -0,0 +1,181 @@
+/*
+ * 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.
+ */
+
+// Contributors: Mathias Bogaert
+// joelr@viair.com
+
+package org.apache.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ BoundedFIFO serves as the bounded first-in-first-out
+ buffer heavily used by the {@link org.apache.log4j.AsyncAppender}.
+
+ @author Ceki Gülcü
+ @since version 0.9.1 */
+public class BoundedFIFO {
+
+ LoggingEvent[] buf;
+ int numElements = 0;
+ int first = 0;
+ int next = 0;
+ int maxSize;
+
+ /**
+ Instantiate a new BoundedFIFO with a maximum size passed as argument.
+ */
+ public
+ BoundedFIFO(int maxSize) {
+ if(maxSize < 1) {
+ throw new IllegalArgumentException("The maxSize argument ("+maxSize+
+ ") is not a positive integer.");
+ }
+ this.maxSize = maxSize;
+ buf = new LoggingEvent[maxSize];
+ }
+
+ /**
+ Get the first element in the buffer. Returns null if
+ there are no elements in the buffer. */
+ public
+ LoggingEvent get() {
+ if(numElements == 0)
+ return null;
+
+ LoggingEvent r = buf[first];
+ buf[first] = null; // help garbage collection
+
+ if(++first == maxSize) {
+ first = 0;
+ }
+ numElements--;
+ return r;
+ }
+
+ /**
+ Place a {@link LoggingEvent} in the buffer. If the buffer is full
+ then the event is silently dropped. It is the caller's
+ responsability to make sure that the buffer has free space. */
+ public
+ void put(LoggingEvent o) {
+ if(numElements != maxSize) {
+ buf[next] = o;
+ if(++next == maxSize) {
+ next = 0;
+ }
+ numElements++;
+ }
+ }
+
+ /**
+ Get the maximum size of the buffer.
+ */
+ public
+ int getMaxSize() {
+ return maxSize;
+ }
+
+ /**
+ Return true if the buffer is full, that is, whether
+ the number of elements in the buffer equals the buffer size. */
+ public
+ boolean isFull() {
+ return numElements == maxSize;
+ }
+
+ /**
+ Get the number of elements in the buffer. This number is
+ guaranteed to be in the range 0 to maxSize
+ (inclusive).
+ */
+ public
+ int length() {
+ return numElements;
+ }
+
+
+ int min(int a, int b) {
+ return a < b ? a : b;
+ }
+
+
+ /**
+ Resize the buffer to a new size. If the new size is smaller than
+ the old size events might be lost.
+
+ @since 1.1
+ */
+ synchronized
+ public
+ void resize(int newSize) {
+ if(newSize == maxSize)
+ return;
+
+
+ LoggingEvent[] tmp = new LoggingEvent[newSize];
+
+ // we should not copy beyond the buf array
+ int len1 = maxSize - first;
+
+ // we should not copy beyond the tmp array
+ len1 = min(len1, newSize);
+
+ // er.. how much do we actually need to copy?
+ // We should not copy more than the actual number of elements.
+ len1 = min(len1, numElements);
+
+ // Copy from buf starting a first, to tmp, starting at position 0, len1 elements.
+ System.arraycopy(buf, first, tmp, 0, len1);
+
+ // Are there any uncopied elements and is there still space in the new array?
+ int len2 = 0;
+ if((len1 < numElements) && (len1 < newSize)) {
+ len2 = numElements - len1;
+ len2 = min(len2, newSize - len1);
+ System.arraycopy(buf, 0, tmp, len1, len2);
+ }
+
+ this.buf = tmp;
+ this.maxSize = newSize;
+ this.first=0;
+ this.numElements = len1+len2;
+ this.next = this.numElements;
+ if(this.next == this.maxSize) // this should never happen, but again, it just might.
+ this.next = 0;
+ }
+
+
+ /**
+ Returns true if there is just one element in the
+ buffer. In other words, if there were no elements before the last
+ {@link #put} operation completed. */
+ public
+ boolean wasEmpty() {
+ return numElements == 1;
+ }
+
+ /**
+ Returns true if the number of elements in the
+ buffer plus 1 equals the maximum buffer size, returns
+ false otherwise. */
+ public
+ boolean wasFull() {
+ return (numElements+1 == maxSize);
+ }
+
+}
diff --git a/java/src/org/apache/log4j/helpers/CountingQuietWriter.java b/java/src/org/apache/log4j/helpers/CountingQuietWriter.java
new file mode 100644
index 0000000..55199e4
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/CountingQuietWriter.java
@@ -0,0 +1,63 @@
+/*
+ * 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.log4j.helpers;
+
+import java.io.Writer;
+import java.io.IOException;
+
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.ErrorCode;
+
+/**
+ Counts the number of bytes written.
+
+ @author Heinz Richter, heinz.richter@frogdot.com
+ @since 0.8.1
+
+ */
+public class CountingQuietWriter extends QuietWriter {
+
+ protected long count;
+
+ public
+ CountingQuietWriter(Writer writer, ErrorHandler eh) {
+ super(writer, eh);
+ }
+
+ public
+ void write(String string) {
+ try {
+ out.write(string);
+ count += string.length();
+ }
+ catch(IOException e) {
+ errorHandler.error("Write failure.", e, ErrorCode.WRITE_FAILURE);
+ }
+ }
+
+ public
+ long getCount() {
+ return count;
+ }
+
+ public
+ void setCount(long count) {
+ this.count = count;
+ }
+
+}
diff --git a/java/src/org/apache/log4j/helpers/CyclicBuffer.java b/java/src/org/apache/log4j/helpers/CyclicBuffer.java
new file mode 100644
index 0000000..9077847
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/CyclicBuffer.java
@@ -0,0 +1,159 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+
+ CyclicBuffer is used by other appenders to hold {@link LoggingEvent
+ LoggingEvents} for immediate or differed display.
+
+
This buffer gives read access to any element in the buffer not
+ just the first or last element.
+
+ @author Ceki Gülcü
+ @since 0.9.0
+
+ */
+public class CyclicBuffer {
+
+ LoggingEvent[] ea;
+ int first;
+ int last;
+ int numElems;
+ int maxSize;
+
+ /**
+ Instantiate a new CyclicBuffer of at most maxSize events.
+
+ The maxSize argument must a positive integer.
+
+ @param maxSize The maximum number of elements in the buffer.
+ */
+ public CyclicBuffer(int maxSize) throws IllegalArgumentException {
+ if(maxSize < 1) {
+ throw new IllegalArgumentException("The maxSize argument ("+maxSize+
+ ") is not a positive integer.");
+ }
+ this.maxSize = maxSize;
+ ea = new LoggingEvent[maxSize];
+ first = 0;
+ last = 0;
+ numElems = 0;
+ }
+
+ /**
+ Add an event as the last event in the buffer.
+
+ */
+ public
+ void add(LoggingEvent event) {
+ ea[last] = event;
+ if(++last == maxSize)
+ last = 0;
+
+ if(numElems < maxSize)
+ numElems++;
+ else if(++first == maxSize)
+ first = 0;
+ }
+
+
+ /**
+ Get the ith oldest event currently in the buffer. If
+ i is outside the range 0 to the number of elements
+ currently in the buffer, then null is returned.
+
+
+ */
+ public
+ LoggingEvent get(int i) {
+ if(i < 0 || i >= numElems)
+ return null;
+
+ return ea[(first + i) % maxSize];
+ }
+
+ public
+ int getMaxSize() {
+ return maxSize;
+ }
+
+ /**
+ Get the oldest (first) element in the buffer. The oldest element
+ is removed from the buffer.
+ */
+ public
+ LoggingEvent get() {
+ LoggingEvent r = null;
+ if(numElems > 0) {
+ numElems--;
+ r = ea[first];
+ ea[first] = null;
+ if(++first == maxSize)
+ first = 0;
+ }
+ return r;
+ }
+
+ /**
+ Get the number of elements in the buffer. This number is
+ guaranteed to be in the range 0 to maxSize
+ (inclusive).
+ */
+ public
+ int length() {
+ return numElems;
+ }
+
+ /**
+ Resize the cyclic buffer to newSize.
+
+ @throws IllegalArgumentException if newSize is negative.
+ */
+ public
+ void resize(int newSize) {
+ if(newSize < 0) {
+ throw new IllegalArgumentException("Negative array size ["+newSize+
+ "] not allowed.");
+ }
+ if(newSize == numElems)
+ return; // nothing to do
+
+ LoggingEvent[] temp = new LoggingEvent[newSize];
+
+ int loopLen = newSize < numElems ? newSize : numElems;
+
+ for(int i = 0; i < loopLen; i++) {
+ temp[i] = ea[first];
+ ea[first] = null;
+ if(++first == numElems)
+ first = 0;
+ }
+ ea = temp;
+ first = 0;
+ numElems = loopLen;
+ maxSize = newSize;
+ if (loopLen == newSize) {
+ last = 0;
+ } else {
+ last = loopLen;
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/DateLayout.java b/java/src/org/apache/log4j/helpers/DateLayout.java
new file mode 100644
index 0000000..383ef38
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/DateLayout.java
@@ -0,0 +1,200 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+import java.text.DateFormat;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+import java.text.FieldPosition;
+
+
+/**
+ This abstract layout takes care of all the date related options and
+ formatting work.
+
+
+ @author Ceki Gülcü
+ */
+abstract public class DateLayout extends Layout {
+
+ /**
+ String constant designating no time information. Current value of
+ this constant is NULL.
+
+ */
+ public final static String NULL_DATE_FORMAT = "NULL";
+
+ /**
+ String constant designating relative time. Current value of
+ this constant is RELATIVE.
+ */
+ public final static String RELATIVE_TIME_DATE_FORMAT = "RELATIVE";
+
+ protected FieldPosition pos = new FieldPosition(0);
+
+ /**
+ @deprecated Options are now handled using the JavaBeans paradigm.
+ This constant is not longer needed and will be removed in the
+ near term.
+ */
+ final static public String DATE_FORMAT_OPTION = "DateFormat";
+
+ /**
+ @deprecated Options are now handled using the JavaBeans paradigm.
+ This constant is not longer needed and will be removed in the
+ near term.
+ */
+ final static public String TIMEZONE_OPTION = "TimeZone";
+
+ private String timeZoneID;
+ private String dateFormatOption;
+
+ protected DateFormat dateFormat;
+ protected Date date = new Date();
+
+ /**
+ @deprecated Use the setter method for the option directly instead
+ of the generic setOption method.
+ */
+ public
+ String[] getOptionStrings() {
+ return new String[] {DATE_FORMAT_OPTION, TIMEZONE_OPTION};
+ }
+
+ /**
+ @deprecated Use the setter method for the option directly instead
+ of the generic setOption method.
+ */
+ public
+ void setOption(String option, String value) {
+ if(option.equalsIgnoreCase(DATE_FORMAT_OPTION)) {
+ dateFormatOption = value.toUpperCase();
+ } else if(option.equalsIgnoreCase(TIMEZONE_OPTION)) {
+ timeZoneID = value;
+ }
+ }
+
+
+ /**
+ The value of the DateFormat option should be either an
+ argument to the constructor of {@link SimpleDateFormat} or one of
+ the srings "NULL", "RELATIVE", "ABSOLUTE", "DATE" or "ISO8601.
+ */
+ public
+ void setDateFormat(String dateFormat) {
+ if (dateFormat != null) {
+ dateFormatOption = dateFormat;
+ }
+ setDateFormat(dateFormatOption, TimeZone.getDefault());
+ }
+
+ /**
+ Returns value of the DateFormat option.
+ */
+ public
+ String getDateFormat() {
+ return dateFormatOption;
+ }
+
+ /**
+ The TimeZoneID option is a time zone ID string in the format
+ expected by the {@link TimeZone#getTimeZone} method.
+ */
+ public
+ void setTimeZone(String timeZone) {
+ this.timeZoneID = timeZone;
+ }
+
+ /**
+ Returns value of the TimeZone option.
+ */
+ public
+ String getTimeZone() {
+ return timeZoneID;
+ }
+
+ public
+ void activateOptions() {
+ setDateFormat(dateFormatOption);
+ if(timeZoneID != null && dateFormat != null) {
+ dateFormat.setTimeZone(TimeZone.getTimeZone(timeZoneID));
+ }
+ }
+
+ public
+ void dateFormat(StringBuffer buf, LoggingEvent event) {
+ if(dateFormat != null) {
+ date.setTime(event.timeStamp);
+ dateFormat.format(date, buf, this.pos);
+ buf.append(' ');
+ }
+ }
+
+ /**
+ Sets the {@link DateFormat} used to format time and date in the
+ zone determined by timeZone.
+ */
+ public
+ void setDateFormat(DateFormat dateFormat, TimeZone timeZone) {
+ this.dateFormat = dateFormat;
+ this.dateFormat.setTimeZone(timeZone);
+ }
+
+ /**
+ Sets the DateFormat used to format date and time in the time zone
+ determined by timeZone parameter. The {@link DateFormat} used
+ will depend on the dateFormatType.
+
+
The recognized types are {@link #NULL_DATE_FORMAT}, {@link
+ #RELATIVE_TIME_DATE_FORMAT} {@link
+ AbsoluteTimeDateFormat#ABS_TIME_DATE_FORMAT}, {@link
+ AbsoluteTimeDateFormat#DATE_AND_TIME_DATE_FORMAT} and {@link
+ AbsoluteTimeDateFormat#ISO8601_DATE_FORMAT}. If the
+ dateFormatType is not one of the above, then the
+ argument is assumed to be a date pattern for {@link
+ SimpleDateFormat}.
+ */
+ public
+ void setDateFormat(String dateFormatType, TimeZone timeZone) {
+ if(dateFormatType == null) {
+ this.dateFormat = null;
+ return;
+ }
+
+ if(dateFormatType.equalsIgnoreCase(NULL_DATE_FORMAT)) {
+ this.dateFormat = null;
+ } else if (dateFormatType.equalsIgnoreCase(RELATIVE_TIME_DATE_FORMAT)) {
+ this.dateFormat = new RelativeTimeDateFormat();
+ } else if(dateFormatType.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT)) {
+ this.dateFormat = new AbsoluteTimeDateFormat(timeZone);
+ } else if(dateFormatType.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT)) {
+ this.dateFormat = new DateTimeDateFormat(timeZone);
+ } else if(dateFormatType.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT)) {
+ this.dateFormat = new ISO8601DateFormat(timeZone);
+ } else {
+ this.dateFormat = new SimpleDateFormat(dateFormatType);
+ this.dateFormat.setTimeZone(timeZone);
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/DateTimeDateFormat.java b/java/src/org/apache/log4j/helpers/DateTimeDateFormat.java
new file mode 100644
index 0000000..559f731
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/DateTimeDateFormat.java
@@ -0,0 +1,85 @@
+/*
+ * 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.log4j.helpers;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.util.Date;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.text.DateFormatSymbols;
+
+/**
+ Formats a {@link Date} in the format "dd MMM yyyy HH:mm:ss,SSS" for example,
+ "06 Nov 1994 15:49:37,459".
+
+ @author Ceki Gülcü
+ @since 0.7.5
+*/
+public class DateTimeDateFormat extends AbsoluteTimeDateFormat {
+ private static final long serialVersionUID = 5547637772208514971L;
+
+ String[] shortMonths;
+
+ public
+ DateTimeDateFormat() {
+ super();
+ shortMonths = new DateFormatSymbols().getShortMonths();
+ }
+
+ public
+ DateTimeDateFormat(TimeZone timeZone) {
+ this();
+ setCalendar(Calendar.getInstance(timeZone));
+ }
+
+ /**
+ Appends to sbuf the date in the format "dd MMM yyyy
+ HH:mm:ss,SSS" for example, "06 Nov 1994 08:49:37,459".
+
+ @param sbuf the string buffer to write to
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+
+ calendar.setTime(date);
+
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+ if(day < 10)
+ sbuf.append('0');
+ sbuf.append(day);
+ sbuf.append(' ');
+ sbuf.append(shortMonths[calendar.get(Calendar.MONTH)]);
+ sbuf.append(' ');
+
+ int year = calendar.get(Calendar.YEAR);
+ sbuf.append(year);
+ sbuf.append(' ');
+
+ return super.format(date, sbuf, fieldPosition);
+ }
+
+ /**
+ This method does not do anything but return null.
+ */
+ public
+ Date parse(java.lang.String s, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/FileWatchdog.java b/java/src/org/apache/log4j/helpers/FileWatchdog.java
new file mode 100644
index 0000000..b78a4af
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/FileWatchdog.java
@@ -0,0 +1,111 @@
+/*
+ * 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.
+ */
+
+// Contributors: Mathias Bogaert
+
+package org.apache.log4j.helpers;
+
+import java.io.File;
+
+/**
+ Check every now and then that a certain file has not changed. If it
+ has, then call the {@link #doOnChange} method.
+
+
+ @author Ceki Gülcü
+ @since version 0.9.1 */
+public abstract class FileWatchdog extends Thread {
+
+ /**
+ The default delay between every file modification check, set to 60
+ seconds. */
+ static final public long DEFAULT_DELAY = 60000;
+ /**
+ The name of the file to observe for changes.
+ */
+ protected String filename;
+
+ /**
+ The delay to observe between every check. By default set {@link
+ #DEFAULT_DELAY}. */
+ protected long delay = DEFAULT_DELAY;
+
+ File file;
+ long lastModif = 0;
+ boolean warnedAlready = false;
+ boolean interrupted = false;
+
+ protected
+ FileWatchdog(String filename) {
+ super("FileWatchdog");
+ this.filename = filename;
+ file = new File(filename);
+ setDaemon(true);
+ checkAndConfigure();
+ }
+
+ /**
+ Set the delay to observe between each check of the file changes.
+ */
+ public
+ void setDelay(long delay) {
+ this.delay = delay;
+ }
+
+ abstract
+ protected
+ void doOnChange();
+
+ protected
+ void checkAndConfigure() {
+ boolean fileExists;
+ try {
+ fileExists = file.exists();
+ } catch(SecurityException e) {
+ LogLog.warn("Was not allowed to read check file existance, file:["+
+ filename+"].");
+ interrupted = true; // there is no point in continuing
+ return;
+ }
+
+ if(fileExists) {
+ long l = file.lastModified(); // this can also throw a SecurityException
+ if(l > lastModif) { // however, if we reached this point this
+ lastModif = l; // is very unlikely.
+ doOnChange();
+ warnedAlready = false;
+ }
+ } else {
+ if(!warnedAlready) {
+ LogLog.debug("["+filename+"] does not exist.");
+ warnedAlready = true;
+ }
+ }
+ }
+
+ public
+ void run() {
+ while(!interrupted) {
+ try {
+ Thread.sleep(delay);
+ } catch(InterruptedException e) {
+ // no interruption expected
+ }
+ checkAndConfigure();
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/FormattingInfo.java b/java/src/org/apache/log4j/helpers/FormattingInfo.java
new file mode 100644
index 0000000..e158243
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/FormattingInfo.java
@@ -0,0 +1,45 @@
+/*
+ * 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.log4j.helpers;
+
+
+/**
+ FormattingInfo instances contain the information obtained when parsing
+ formatting modifiers in conversion modifiers.
+
+ @author Jim Cakalic
+ @author Ceki Gülcü
+
+ @since 0.8.2
+ */
+public class FormattingInfo {
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ void reset() {
+ min = -1;
+ max = 0x7FFFFFFF;
+ leftAlign = false;
+ }
+
+ void dump() {
+ LogLog.debug("min="+min+", max="+max+", leftAlign="+leftAlign);
+ }
+}
+
diff --git a/java/src/org/apache/log4j/helpers/ISO8601DateFormat.java b/java/src/org/apache/log4j/helpers/ISO8601DateFormat.java
new file mode 100644
index 0000000..47b17d9
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/ISO8601DateFormat.java
@@ -0,0 +1,155 @@
+/*
+ * 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.log4j.helpers;
+
+import java.util.Calendar;
+import java.util.TimeZone;
+import java.util.Date;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+
+// Contributors: Arndt Schoenewald
+
+/**
+ Formats a {@link Date} in the format "yyyy-MM-dd HH:mm:ss,SSS" for example
+ "1999-11-27 15:49:37,459".
+
+ Refer to the summary of the
+ International Standard Date and Time Notation for more
+ information on this format.
+
+ @author Ceki Gülcü
+ @author Andrew Vajoczki
+
+ @since 0.7.5
+*/
+public class ISO8601DateFormat extends AbsoluteTimeDateFormat {
+ private static final long serialVersionUID = -759840745298755296L;
+
+ public
+ ISO8601DateFormat() {
+ }
+
+ public
+ ISO8601DateFormat(TimeZone timeZone) {
+ super(timeZone);
+ }
+
+ static private long lastTime;
+ static private char[] lastTimeString = new char[20];
+
+ /**
+ Appends a date in the format "YYYY-mm-dd HH:mm:ss,SSS"
+ to sbuf. For example: "1999-11-27 15:49:37,459".
+
+ @param sbuf the StringBuffer to write to
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+
+ long now = date.getTime();
+ int millis = (int)(now % 1000);
+
+ if ((now - millis) != lastTime || lastTimeString[0] == 0) {
+ // We reach this point at most once per second
+ // across all threads instead of each time format()
+ // is called. This saves considerable CPU time.
+
+ calendar.setTime(date);
+
+ int start = sbuf.length();
+
+ int year = calendar.get(Calendar.YEAR);
+ sbuf.append(year);
+
+ String month;
+ switch(calendar.get(Calendar.MONTH)) {
+ case Calendar.JANUARY: month = "-01-"; break;
+ case Calendar.FEBRUARY: month = "-02-"; break;
+ case Calendar.MARCH: month = "-03-"; break;
+ case Calendar.APRIL: month = "-04-"; break;
+ case Calendar.MAY: month = "-05-"; break;
+ case Calendar.JUNE: month = "-06-"; break;
+ case Calendar.JULY: month = "-07-"; break;
+ case Calendar.AUGUST: month = "-08-"; break;
+ case Calendar.SEPTEMBER: month = "-09-"; break;
+ case Calendar.OCTOBER: month = "-10-"; break;
+ case Calendar.NOVEMBER: month = "-11-"; break;
+ case Calendar.DECEMBER: month = "-12-"; break;
+ default: month = "-NA-"; break;
+ }
+ sbuf.append(month);
+
+ int day = calendar.get(Calendar.DAY_OF_MONTH);
+ if(day < 10)
+ sbuf.append('0');
+ sbuf.append(day);
+
+ sbuf.append(' ');
+
+ int hour = calendar.get(Calendar.HOUR_OF_DAY);
+ if(hour < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(hour);
+ sbuf.append(':');
+
+ int mins = calendar.get(Calendar.MINUTE);
+ if(mins < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(mins);
+ sbuf.append(':');
+
+ int secs = calendar.get(Calendar.SECOND);
+ if(secs < 10) {
+ sbuf.append('0');
+ }
+ sbuf.append(secs);
+
+ sbuf.append(',');
+
+ // store the time string for next time to avoid recomputation
+ sbuf.getChars(start, sbuf.length(), lastTimeString, 0);
+ lastTime = now - millis;
+ }
+ else {
+ sbuf.append(lastTimeString);
+ }
+
+
+ if (millis < 100)
+ sbuf.append('0');
+ if (millis < 10)
+ sbuf.append('0');
+
+ sbuf.append(millis);
+ return sbuf;
+ }
+
+ /**
+ This method does not do anything but return null.
+ */
+ public
+ Date parse(java.lang.String s, ParsePosition pos) {
+ return null;
+ }
+}
+
diff --git a/java/src/org/apache/log4j/helpers/Loader.java b/java/src/org/apache/log4j/helpers/Loader.java
new file mode 100644
index 0000000..207bfdf
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/Loader.java
@@ -0,0 +1,200 @@
+/*
+ * 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.log4j.helpers;
+
+import java.net.URL;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.io.InterruptedIOException;
+
+
+/**
+ Load resources (or images) from various sources.
+
+ @author Ceki Gülcü
+ */
+
+public class Loader {
+
+ static final String TSTR = "Caught Exception while in Loader.getResource. This may be innocuous.";
+
+ // We conservatively assume that we are running under Java 1.x
+ static private boolean java1 = true;
+
+ static private boolean ignoreTCL = false;
+
+ static {
+ String prop = OptionConverter.getSystemProperty("java.version", null);
+
+ if(prop != null) {
+ int i = prop.indexOf('.');
+ if(i != -1) {
+ if(prop.charAt(i+1) != '1')
+ java1 = false;
+ }
+ }
+ String ignoreTCLProp = OptionConverter.getSystemProperty("log4j.ignoreTCL", null);
+ if(ignoreTCLProp != null) {
+ ignoreTCL = OptionConverter.toBoolean(ignoreTCLProp, true);
+ }
+ }
+
+ /**
+ * Get a resource by delegating to getResource(String).
+ * @param resource resource name
+ * @param clazz class, ignored.
+ * @return URL to resource or null.
+ * @deprecated as of 1.2.
+ */
+ public static URL getResource(String resource, Class clazz) {
+ return getResource(resource);
+ }
+
+ /**
+ This method will search for resource in different
+ places. The search order is as follows:
+
+
+
+ - Search for
resource using the thread context
+ class loader under Java2. If that fails, search for
+ resource using the class loader that loaded this
+ class (Loader). Under JDK 1.1, only the the class
+ loader that loaded this class (Loader) is used.
+
+ - Try one last time with
+
ClassLoader.getSystemResource(resource), that is is
+ using the system class loader in JDK 1.2 and virtual machine's
+ built-in class loader in JDK 1.1.
+
+
+ */
+ static public URL getResource(String resource) {
+ ClassLoader classLoader = null;
+ URL url = null;
+
+ try {
+ if(!java1 && !ignoreTCL) {
+ classLoader = getTCL();
+ if(classLoader != null) {
+ LogLog.debug("Trying to find ["+resource+"] using context classloader "
+ +classLoader+".");
+ url = classLoader.getResource(resource);
+ if(url != null) {
+ return url;
+ }
+ }
+ }
+
+ // We could not find resource. Ler us now try with the
+ // classloader that loaded this class.
+ classLoader = Loader.class.getClassLoader();
+ if(classLoader != null) {
+ LogLog.debug("Trying to find ["+resource+"] using "+classLoader
+ +" class loader.");
+ url = classLoader.getResource(resource);
+ if(url != null) {
+ return url;
+ }
+ }
+ } catch(IllegalAccessException t) {
+ LogLog.warn(TSTR, t);
+ } catch(InvocationTargetException t) {
+ if (t.getTargetException() instanceof InterruptedException
+ || t.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.warn(TSTR, t);
+ } catch(Throwable t) {
+ //
+ // can't be InterruptedException or InterruptedIOException
+ // since not declared, must be error or RuntimeError.
+ LogLog.warn(TSTR, t);
+ }
+
+ // Last ditch attempt: get the resource from the class path. It
+ // may be the case that clazz was loaded by the Extentsion class
+ // loader which the parent of the system class loader. Hence the
+ // code below.
+ LogLog.debug("Trying to find ["+resource+
+ "] using ClassLoader.getSystemResource().");
+ return ClassLoader.getSystemResource(resource);
+ }
+
+ /**
+ Are we running under JDK 1.x?
+ */
+ public
+ static
+ boolean isJava1() {
+ return java1;
+ }
+
+ /**
+ * Get the Thread Context Loader which is a JDK 1.2 feature. If we
+ * are running under JDK 1.1 or anything else goes wrong the method
+ * returns null.
+ *
+ * */
+ private static ClassLoader getTCL() throws IllegalAccessException,
+ InvocationTargetException {
+
+ // Are we running on a JDK 1.2 or later system?
+ Method method = null;
+ try {
+ method = Thread.class.getMethod("getContextClassLoader", null);
+ } catch (NoSuchMethodException e) {
+ // We are running on JDK 1.1
+ return null;
+ }
+
+ return (ClassLoader) method.invoke(Thread.currentThread(), null);
+ }
+
+
+
+ /**
+ * If running under JDK 1.2 load the specified class using the
+ * Thread contextClassLoader if that
+ * fails try Class.forname. Under JDK 1.1 only Class.forName is
+ * used.
+ *
+ */
+ static public Class loadClass (String clazz) throws ClassNotFoundException {
+ // Just call Class.forName(clazz) if we are running under JDK 1.1
+ // or if we are instructed to ignore the TCL.
+ if(java1 || ignoreTCL) {
+ return Class.forName(clazz);
+ } else {
+ try {
+ return getTCL().loadClass(clazz);
+ }
+ // we reached here because tcl was null or because of a
+ // security exception, or because clazz could not be loaded...
+ // In any case we now try one more time
+ catch(InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ } catch(Throwable t) {
+ }
+ }
+ return Class.forName(clazz);
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/LogLog.java b/java/src/org/apache/log4j/helpers/LogLog.java
new file mode 100644
index 0000000..a7bd588
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/LogLog.java
@@ -0,0 +1,189 @@
+/*
+ * 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.log4j.helpers;
+
+/**
+ This class used to output log statements from within the log4j package.
+
+ Log4j components cannot make log4j logging calls. However, it is
+ sometimes useful for the user to learn about what log4j is
+ doing. You can enable log4j internal logging by defining the
+ log4j.configDebug variable.
+
+
All log4j internal debug calls go to System.out
+ where as internal error messages are sent to
+ System.err. All internal messages are prepended with
+ the string "log4j: ".
+
+ @since 0.8.2
+ @author Ceki Gülcü
+*/
+public class LogLog {
+
+ /**
+ Defining this value makes log4j print log4j-internal debug
+ statements to System.out.
+
+
The value of this string is log4j.debug.
+
+
Note that the search for all option names is case sensitive. */
+ public static final String DEBUG_KEY="log4j.debug";
+
+
+ /**
+ Defining this value makes log4j components print log4j-internal
+ debug statements to System.out.
+
+
The value of this string is log4j.configDebug.
+
+
Note that the search for all option names is case sensitive.
+
+ @deprecated Use {@link #DEBUG_KEY} instead.
+ */
+ public static final String CONFIG_DEBUG_KEY="log4j.configDebug";
+
+ protected static boolean debugEnabled = false;
+
+ /**
+ In quietMode not even errors generate any output.
+ */
+ private static boolean quietMode = false;
+
+ private static final String PREFIX = "log4j: ";
+ private static final String ERR_PREFIX = "log4j:ERROR ";
+ private static final String WARN_PREFIX = "log4j:WARN ";
+
+ static {
+ String key = OptionConverter.getSystemProperty(DEBUG_KEY, null);
+
+ if(key == null) {
+ key = OptionConverter.getSystemProperty(CONFIG_DEBUG_KEY, null);
+ }
+
+ if(key != null) {
+ debugEnabled = OptionConverter.toBoolean(key, true);
+ }
+ }
+
+ /**
+ Allows to enable/disable log4j internal logging.
+ */
+ static
+ public
+ void setInternalDebugging(boolean enabled) {
+ debugEnabled = enabled;
+ }
+
+ /**
+ This method is used to output log4j internal debug
+ statements. Output goes to System.out.
+ */
+ public
+ static
+ void debug(String msg) {
+ if(debugEnabled && !quietMode) {
+ System.out.println(PREFIX+msg);
+ }
+ }
+
+ /**
+ This method is used to output log4j internal debug
+ statements. Output goes to System.out.
+ */
+ public
+ static
+ void debug(String msg, Throwable t) {
+ if(debugEnabled && !quietMode) {
+ System.out.println(PREFIX+msg);
+ if(t != null)
+ t.printStackTrace(System.out);
+ }
+ }
+
+
+ /**
+ This method is used to output log4j internal error
+ statements. There is no way to disable error statements.
+ Output goes to System.err.
+ */
+ public
+ static
+ void error(String msg) {
+ if(quietMode)
+ return;
+ System.err.println(ERR_PREFIX+msg);
+ }
+
+ /**
+ This method is used to output log4j internal error
+ statements. There is no way to disable error statements.
+ Output goes to System.err.
+ */
+ public
+ static
+ void error(String msg, Throwable t) {
+ if(quietMode)
+ return;
+
+ System.err.println(ERR_PREFIX+msg);
+ if(t != null) {
+ t.printStackTrace();
+ }
+ }
+
+ /**
+ In quite mode no LogLog generates strictly no output, not even
+ for errors.
+
+ @param quietMode A true for not
+ */
+ public
+ static
+ void setQuietMode(boolean quietMode) {
+ LogLog.quietMode = quietMode;
+ }
+
+ /**
+ This method is used to output log4j internal warning
+ statements. There is no way to disable warning statements.
+ Output goes to System.err. */
+ public
+ static
+ void warn(String msg) {
+ if(quietMode)
+ return;
+
+ System.err.println(WARN_PREFIX+msg);
+ }
+
+ /**
+ This method is used to output log4j internal warnings. There is
+ no way to disable warning statements. Output goes to
+ System.err. */
+ public
+ static
+ void warn(String msg, Throwable t) {
+ if(quietMode)
+ return;
+
+ System.err.println(WARN_PREFIX+msg);
+ if(t != null) {
+ t.printStackTrace();
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/MDCKeySetExtractor.java b/java/src/org/apache/log4j/helpers/MDCKeySetExtractor.java
new file mode 100644
index 0000000..2d2a539
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/MDCKeySetExtractor.java
@@ -0,0 +1,92 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.pattern.LogEvent;
+
+import java.lang.reflect.Method;
+import java.util.Set;
+import java.io.ByteArrayOutputStream;
+import java.io.ObjectOutputStream;
+import java.io.ByteArrayInputStream;
+import java.io.ObjectInputStream;
+
+
+public final class MDCKeySetExtractor {
+ private final Method getKeySetMethod;
+ public static final MDCKeySetExtractor INSTANCE =
+ new MDCKeySetExtractor();
+
+
+ private MDCKeySetExtractor() {
+ //
+ // log4j 1.2.15 and later will have method to get names
+ // of all keys in MDC
+ //
+ Method getMethod = null;
+
+ try {
+ getMethod = LoggingEvent.class.getMethod(
+ "getPropertyKeySet", null);
+ } catch(Exception ex) {
+ getMethod = null;
+ }
+ getKeySetMethod = getMethod;
+
+ }
+
+ public Set getPropertyKeySet(final LoggingEvent event) throws Exception {
+ //
+ // MDC keys are not visible prior to log4j 1.2.15
+ //
+ Set keySet = null;
+ if (getKeySetMethod != null) {
+ keySet = (Set) getKeySetMethod.invoke(event, null);
+ } else {
+ //
+ // for 1.2.14 and earlier could serialize and
+ // extract MDC content
+ ByteArrayOutputStream outBytes = new ByteArrayOutputStream();
+ ObjectOutputStream os = new ObjectOutputStream(outBytes);
+ os.writeObject(event);
+ os.close();
+
+ byte[] raw = outBytes.toByteArray();
+ //
+ // bytes 6 and 7 should be the length of the original classname
+ // should be the same as our substitute class name
+ final String subClassName = LogEvent.class.getName();
+ if (raw[6] == 0 || raw[7] == subClassName.length()) {
+ //
+ // manipulate stream to use our class name
+ //
+ for (int i = 0; i < subClassName.length(); i++) {
+ raw[8 + i] = (byte) subClassName.charAt(i);
+ }
+ ByteArrayInputStream inBytes = new ByteArrayInputStream(raw);
+ ObjectInputStream is = new ObjectInputStream(inBytes);
+ Object cracked = is.readObject();
+ if (cracked instanceof LogEvent) {
+ keySet = ((LogEvent) cracked).getPropertyKeySet();
+ }
+ is.close();
+ }
+ }
+ return keySet;
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/NullEnumeration.java b/java/src/org/apache/log4j/helpers/NullEnumeration.java
new file mode 100644
index 0000000..0f4310d
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/NullEnumeration.java
@@ -0,0 +1,50 @@
+/*
+ * 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.log4j.helpers;
+
+import java.util.Enumeration;
+import java.util.NoSuchElementException;
+
+/**
+
+ An always-empty Enumerator.
+
+ @author Anders Kristensen
+ @since version 1.0
+ */
+public class NullEnumeration implements Enumeration {
+ private static final NullEnumeration instance = new NullEnumeration();
+
+ private
+ NullEnumeration() {
+ }
+
+ public static NullEnumeration getInstance() {
+ return instance;
+ }
+
+ public
+ boolean hasMoreElements() {
+ return false;
+ }
+
+ public
+ Object nextElement() {
+ throw new NoSuchElementException();
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/OnlyOnceErrorHandler.java b/java/src/org/apache/log4j/helpers/OnlyOnceErrorHandler.java
new file mode 100644
index 0000000..950778d
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/OnlyOnceErrorHandler.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.log4j.helpers;
+
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Appender;
+
+import java.io.InterruptedIOException;
+
+/**
+
+ The OnlyOnceErrorHandler implements log4j's default
+ error handling policy which consists of emitting a message for the
+ first error in an appender and ignoring all following errors.
+
+
The error message is printed on System.err.
+
+
This policy aims at protecting an otherwise working application
+ from being flooded with error messages when logging fails.
+
+ @author Ceki Gülcü
+ @since 0.9.0 */
+public class OnlyOnceErrorHandler implements ErrorHandler {
+
+
+ final String WARN_PREFIX = "log4j warning: ";
+ final String ERROR_PREFIX = "log4j error: ";
+
+ boolean firstTime = true;
+
+
+ /**
+ Does not do anything.
+ */
+ public
+ void setLogger(Logger logger) {
+ }
+
+
+ /**
+ No options to activate.
+ */
+ public
+ void activateOptions() {
+ }
+
+
+ /**
+ Prints the message and the stack trace of the exception on
+ System.err. */
+ public
+ void error(String message, Exception e, int errorCode) {
+ error(message, e, errorCode, null);
+ }
+
+ /**
+ Prints the message and the stack trace of the exception on
+ System.err.
+ */
+ public
+ void error(String message, Exception e, int errorCode, LoggingEvent event) {
+ if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ if(firstTime) {
+ LogLog.error(message, e);
+ firstTime = false;
+ }
+ }
+
+
+ /**
+ Print a the error message passed as parameter on
+ System.err.
+ */
+ public
+ void error(String message) {
+ if(firstTime) {
+ LogLog.error(message);
+ firstTime = false;
+ }
+ }
+
+ /**
+ Does not do anything.
+ */
+ public
+ void setAppender(Appender appender) {
+ }
+
+ /**
+ Does not do anything.
+ */
+ public
+ void setBackupAppender(Appender appender) {
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/OptionConverter.java b/java/src/org/apache/log4j/helpers/OptionConverter.java
new file mode 100644
index 0000000..9291774
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/OptionConverter.java
@@ -0,0 +1,485 @@
+/*
+ * 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.log4j.helpers;
+
+import java.util.Properties;
+import java.net.URL;
+import java.io.InterruptedIOException;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.PropertyConfigurator;
+
+// Contributors: Avy Sharell (sharell@online.fr)
+// Matthieu Verbert (mve@zurich.ibm.com)
+// Colin Sampaleanu
+
+/**
+ A convenience class to convert property values to specific types.
+
+ @author Ceki Gülcü
+ @author Simon Kitching;
+ @author Anders Kristensen
+*/
+public class OptionConverter {
+
+ static String DELIM_START = "${";
+ static char DELIM_STOP = '}';
+ static int DELIM_START_LEN = 2;
+ static int DELIM_STOP_LEN = 1;
+
+ /** OptionConverter is a static class. */
+ private OptionConverter() {}
+
+ public
+ static
+ String[] concatanateArrays(String[] l, String[] r) {
+ int len = l.length + r.length;
+ String[] a = new String[len];
+
+ System.arraycopy(l, 0, a, 0, l.length);
+ System.arraycopy(r, 0, a, l.length, r.length);
+
+ return a;
+ }
+
+ public
+ static
+ String convertSpecialChars(String s) {
+ char c;
+ int len = s.length();
+ StringBuffer sbuf = new StringBuffer(len);
+
+ int i = 0;
+ while(i < len) {
+ c = s.charAt(i++);
+ if (c == '\\') {
+ c = s.charAt(i++);
+ if(c == 'n') c = '\n';
+ else if(c == 'r') c = '\r';
+ else if(c == 't') c = '\t';
+ else if(c == 'f') c = '\f';
+ else if(c == '\b') c = '\b';
+ else if(c == '\"') c = '\"';
+ else if(c == '\'') c = '\'';
+ else if(c == '\\') c = '\\';
+ }
+ sbuf.append(c);
+ }
+ return sbuf.toString();
+ }
+
+
+ /**
+ Very similar to System.getProperty except
+ that the {@link SecurityException} is hidden.
+
+ @param key The key to search for.
+ @param def The default value to return.
+ @return the string value of the system property, or the default
+ value if there is no property with that key.
+
+ @since 1.1 */
+ public
+ static
+ String getSystemProperty(String key, String def) {
+ try {
+ return System.getProperty(key, def);
+ } catch(Throwable e) { // MS-Java throws com.ms.security.SecurityExceptionEx
+ LogLog.debug("Was not allowed to read system property \""+key+"\".");
+ return def;
+ }
+ }
+
+
+ public
+ static
+ Object instantiateByKey(Properties props, String key, Class superClass,
+ Object defaultValue) {
+
+ // Get the value of the property in string form
+ String className = findAndSubst(key, props);
+ if(className == null) {
+ LogLog.error("Could not find value for key " + key);
+ return defaultValue;
+ }
+ // Trim className to avoid trailing spaces that cause problems.
+ return OptionConverter.instantiateByClassName(className.trim(), superClass,
+ defaultValue);
+ }
+
+ /**
+ If value is "true", then true is
+ returned. If value is "false", then
+ true is returned. Otherwise, default is
+ returned.
+
+
Case of value is unimportant. */
+ public
+ static
+ boolean toBoolean(String value, boolean dEfault) {
+ if(value == null)
+ return dEfault;
+ String trimmedVal = value.trim();
+ if("true".equalsIgnoreCase(trimmedVal))
+ return true;
+ if("false".equalsIgnoreCase(trimmedVal))
+ return false;
+ return dEfault;
+ }
+
+ public
+ static
+ int toInt(String value, int dEfault) {
+ if(value != null) {
+ String s = value.trim();
+ try {
+ return Integer.valueOf(s).intValue();
+ }
+ catch (NumberFormatException e) {
+ LogLog.error("[" + s + "] is not in proper int form.");
+ e.printStackTrace();
+ }
+ }
+ return dEfault;
+ }
+
+ /**
+ Converts a standard or custom priority level to a Level
+ object.
If value is of form
+ "level#classname", then the specified class' toLevel method
+ is called to process the specified level string; if no '#'
+ character is present, then the default {@link org.apache.log4j.Level}
+ class is used to process the level value.
+
+
As a special case, if the value parameter is
+ equal to the string "NULL", then the value null will
+ be returned.
+
+
If any error occurs while converting the value to a level,
+ the defaultValue parameter, which may be
+ null, is returned.
+
+
Case of value is insignificant for the level level, but is
+ significant for the class name part, if present.
+
+ @since 1.1 */
+ public
+ static
+ Level toLevel(String value, Level defaultValue) {
+ if(value == null)
+ return defaultValue;
+
+ value = value.trim();
+
+ int hashIndex = value.indexOf('#');
+ if (hashIndex == -1) {
+ if("NULL".equalsIgnoreCase(value)) {
+ return null;
+ } else {
+ // no class name specified : use standard Level class
+ return(Level) Level.toLevel(value, defaultValue);
+ }
+ }
+
+ Level result = defaultValue;
+
+ String clazz = value.substring(hashIndex+1);
+ String levelName = value.substring(0, hashIndex);
+
+ // This is degenerate case but you never know.
+ if("NULL".equalsIgnoreCase(levelName)) {
+ return null;
+ }
+
+ LogLog.debug("toLevel" + ":class=[" + clazz + "]"
+ + ":pri=[" + levelName + "]");
+
+ try {
+ Class customLevel = Loader.loadClass(clazz);
+
+ // get a ref to the specified class' static method
+ // toLevel(String, org.apache.log4j.Level)
+ Class[] paramTypes = new Class[] { String.class,
+ org.apache.log4j.Level.class
+ };
+ java.lang.reflect.Method toLevelMethod =
+ customLevel.getMethod("toLevel", paramTypes);
+
+ // now call the toLevel method, passing level string + default
+ Object[] params = new Object[] {levelName, defaultValue};
+ Object o = toLevelMethod.invoke(null, params);
+
+ result = (Level) o;
+ } catch(ClassNotFoundException e) {
+ LogLog.warn("custom level class [" + clazz + "] not found.");
+ } catch(NoSuchMethodException e) {
+ LogLog.warn("custom level class [" + clazz + "]"
+ + " does not have a class function toLevel(String, Level)", e);
+ } catch(java.lang.reflect.InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.warn("custom level class [" + clazz + "]"
+ + " could not be instantiated", e);
+ } catch(ClassCastException e) {
+ LogLog.warn("class [" + clazz
+ + "] is not a subclass of org.apache.log4j.Level", e);
+ } catch(IllegalAccessException e) {
+ LogLog.warn("class ["+clazz+
+ "] cannot be instantiated due to access restrictions", e);
+ } catch(RuntimeException e) {
+ LogLog.warn("class ["+clazz+"], level ["+levelName+
+ "] conversion failed.", e);
+ }
+ return result;
+ }
+
+ public
+ static
+ long toFileSize(String value, long dEfault) {
+ if(value == null)
+ return dEfault;
+
+ String s = value.trim().toUpperCase();
+ long multiplier = 1;
+ int index;
+
+ if((index = s.indexOf("KB")) != -1) {
+ multiplier = 1024;
+ s = s.substring(0, index);
+ }
+ else if((index = s.indexOf("MB")) != -1) {
+ multiplier = 1024*1024;
+ s = s.substring(0, index);
+ }
+ else if((index = s.indexOf("GB")) != -1) {
+ multiplier = 1024*1024*1024;
+ s = s.substring(0, index);
+ }
+ if(s != null) {
+ try {
+ return Long.valueOf(s).longValue() * multiplier;
+ }
+ catch (NumberFormatException e) {
+ LogLog.error("[" + s + "] is not in proper int form.");
+ LogLog.error("[" + value + "] not in expected format.", e);
+ }
+ }
+ return dEfault;
+ }
+
+ /**
+ Find the value corresponding to key in
+ props. Then perform variable substitution on the
+ found value.
+
+ */
+ public
+ static
+ String findAndSubst(String key, Properties props) {
+ String value = props.getProperty(key);
+ if(value == null)
+ return null;
+
+ try {
+ return substVars(value, props);
+ } catch(IllegalArgumentException e) {
+ LogLog.error("Bad option value ["+value+"].", e);
+ return value;
+ }
+ }
+
+ /**
+ Instantiate an object given a class name. Check that the
+ className is a subclass of
+ superClass. If that test fails or the object could
+ not be instantiated, then defaultValue is returned.
+
+ @param className The fully qualified class name of the object to instantiate.
+ @param superClass The class to which the new object should belong.
+ @param defaultValue The object to return in case of non-fulfillment
+ */
+ public
+ static
+ Object instantiateByClassName(String className, Class superClass,
+ Object defaultValue) {
+ if(className != null) {
+ try {
+ Class classObj = Loader.loadClass(className);
+ if(!superClass.isAssignableFrom(classObj)) {
+ LogLog.error("A \""+className+"\" object is not assignable to a \""+
+ superClass.getName() + "\" variable.");
+ LogLog.error("The class \""+ superClass.getName()+"\" was loaded by ");
+ LogLog.error("["+superClass.getClassLoader()+"] whereas object of type ");
+ LogLog.error("\"" +classObj.getName()+"\" was loaded by ["
+ +classObj.getClassLoader()+"].");
+ return defaultValue;
+ }
+ return classObj.newInstance();
+ } catch (ClassNotFoundException e) {
+ LogLog.error("Could not instantiate class [" + className + "].", e);
+ } catch (IllegalAccessException e) {
+ LogLog.error("Could not instantiate class [" + className + "].", e);
+ } catch (InstantiationException e) {
+ LogLog.error("Could not instantiate class [" + className + "].", e);
+ } catch (RuntimeException e) {
+ LogLog.error("Could not instantiate class [" + className + "].", e);
+ }
+ }
+ return defaultValue;
+ }
+
+
+ /**
+ Perform variable substitution in string val from the
+ values of keys found in the system propeties.
+
+
The variable substitution delimeters are ${ and }.
+
+
For example, if the System properties contains "key=value", then
+ the call
+
+ String s = OptionConverter.substituteVars("Value of key is ${key}.");
+
+
+ will set the variable s to "Value of key is value.".
+
+ If no value could be found for the specified key, then the
+ props parameter is searched, if the value could not
+ be found there, then substitution defaults to the empty string.
+
+
For example, if system propeties contains no value for the key
+ "inexistentKey", then the call
+
+
+ String s = OptionConverter.subsVars("Value of inexistentKey is [${inexistentKey}]");
+
+ will set s to "Value of inexistentKey is []"
+
+ An {@link java.lang.IllegalArgumentException} is thrown if
+ val contains a start delimeter "${" which is not
+ balanced by a stop delimeter "}".
+
+ Author Avy Sharell
+
+ @param val The string on which variable substitution is performed.
+ @throws IllegalArgumentException if val is malformed.
+
+ */
+ public static
+ String substVars(String val, Properties props) throws
+ IllegalArgumentException {
+
+ StringBuffer sbuf = new StringBuffer();
+
+ int i = 0;
+ int j, k;
+
+ while(true) {
+ j=val.indexOf(DELIM_START, i);
+ if(j == -1) {
+ // no more variables
+ if(i==0) { // this is a simple string
+ return val;
+ } else { // add the tail string which contails no variables and return the result.
+ sbuf.append(val.substring(i, val.length()));
+ return sbuf.toString();
+ }
+ } else {
+ sbuf.append(val.substring(i, j));
+ k = val.indexOf(DELIM_STOP, j);
+ if(k == -1) {
+ throw new IllegalArgumentException('"'+val+
+ "\" has no closing brace. Opening brace at position " + j
+ + '.');
+ } else {
+ j += DELIM_START_LEN;
+ String key = val.substring(j, k);
+ // first try in System properties
+ String replacement = getSystemProperty(key, null);
+ // then try props parameter
+ if(replacement == null && props != null) {
+ replacement = props.getProperty(key);
+ }
+
+ if(replacement != null) {
+ // Do variable substitution on the replacement string
+ // such that we can solve "Hello ${x2}" as "Hello p1"
+ // the where the properties are
+ // x1=p1
+ // x2=${x1}
+ String recursiveReplacement = substVars(replacement, props);
+ sbuf.append(recursiveReplacement);
+ }
+ i = k + DELIM_STOP_LEN;
+ }
+ }
+ }
+ }
+
+
+ /**
+ Configure log4j given a URL.
+
+ The url must point to a file or resource which will be interpreted by
+ a new instance of a log4j configurator.
+
+
All configurations steps are taken on the
+ hierarchy passed as a parameter.
+
+
+ @param url The location of the configuration file or resource.
+ @param clazz The classname, of the log4j configurator which will parse
+ the file or resource at url. This must be a subclass of
+ {@link Configurator}, or null. If this value is null then a default
+ configurator of {@link PropertyConfigurator} is used, unless the
+ filename pointed to by url ends in '.xml', in which case
+ {@link org.apache.log4j.xml.DOMConfigurator} is used.
+ @param hierarchy The {@link org.apache.log4j.Hierarchy} to act on.
+
+ @since 1.1.4 */
+
+ static
+ public
+ void selectAndConfigure(URL url, String clazz, LoggerRepository hierarchy) {
+ Configurator configurator = null;
+ String filename = url.getFile();
+
+ if(clazz == null && filename != null && filename.endsWith(".xml")) {
+ clazz = "org.apache.log4j.xml.DOMConfigurator";
+ }
+
+ if(clazz != null) {
+ LogLog.debug("Preferred configurator class: " + clazz);
+ configurator = (Configurator) instantiateByClassName(clazz,
+ Configurator.class,
+ null);
+ if(configurator == null) {
+ LogLog.error("Could not instantiate configurator ["+clazz+"].");
+ return;
+ }
+ } else {
+ configurator = new PropertyConfigurator();
+ }
+
+ configurator.doConfigure(url, hierarchy);
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/PatternConverter.java b/java/src/org/apache/log4j/helpers/PatternConverter.java
new file mode 100644
index 0000000..2b46db7
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/PatternConverter.java
@@ -0,0 +1,111 @@
+/*
+ * 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.log4j.helpers;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+
+
PatternConverter is an abtract class that provides the
+ formatting functionality that derived classes need.
+
+
Conversion specifiers in a conversion patterns are parsed to
+ individual PatternConverters. Each of which is responsible for
+ converting a logging event in a converter specific manner.
+
+ @author James P. Cakalic
+ @author Ceki Gülcü
+
+ @since 0.8.2
+ */
+public abstract class PatternConverter {
+ public PatternConverter next;
+ int min = -1;
+ int max = 0x7FFFFFFF;
+ boolean leftAlign = false;
+
+ protected
+ PatternConverter() { }
+
+ protected
+ PatternConverter(FormattingInfo fi) {
+ min = fi.min;
+ max = fi.max;
+ leftAlign = fi.leftAlign;
+ }
+
+ /**
+ Derived pattern converters must override this method in order to
+ convert conversion specifiers in the correct way.
+ */
+ abstract
+ protected
+ String convert(LoggingEvent event);
+
+ /**
+ A template method for formatting in a converter specific way.
+ */
+ public
+ void format(StringBuffer sbuf, LoggingEvent e) {
+ String s = convert(e);
+
+ if(s == null) {
+ if(0 < min)
+ spacePad(sbuf, min);
+ return;
+ }
+
+ int len = s.length();
+
+ if(len > max)
+ sbuf.append(s.substring(len-max));
+ else if(len < min) {
+ if(leftAlign) {
+ sbuf.append(s);
+ spacePad(sbuf, min-len);
+ }
+ else {
+ spacePad(sbuf, min-len);
+ sbuf.append(s);
+ }
+ }
+ else
+ sbuf.append(s);
+ }
+
+ static String[] SPACES = {" ", " ", " ", " ", //1,2,4,8 spaces
+ " ", // 16 spaces
+ " " }; // 32 spaces
+
+ /**
+ Fast space padding method.
+ */
+ public
+ void spacePad(StringBuffer sbuf, int length) {
+ while(length >= 32) {
+ sbuf.append(SPACES[5]);
+ length -= 32;
+ }
+
+ for(int i = 4; i >= 0; i--) {
+ if((length & (1<
+// Igor E. Poteryaev
+// Reinhard Deschler
+
+/**
+ Most of the work of the {@link org.apache.log4j.PatternLayout} class
+ is delegated to the PatternParser class.
+
+ It is this class that parses conversion patterns and creates
+ a chained list of {@link OptionConverter OptionConverters}.
+
+ @author James P. Cakalic
+ @author Ceki Gülcü
+ @author Anders Kristensen
+
+ @since 0.8.2
+*/
+public class PatternParser {
+
+ private static final char ESCAPE_CHAR = '%';
+
+ private static final int LITERAL_STATE = 0;
+ private static final int CONVERTER_STATE = 1;
+ private static final int DOT_STATE = 3;
+ private static final int MIN_STATE = 4;
+ private static final int MAX_STATE = 5;
+
+ static final int FULL_LOCATION_CONVERTER = 1000;
+ static final int METHOD_LOCATION_CONVERTER = 1001;
+ static final int CLASS_LOCATION_CONVERTER = 1002;
+ static final int LINE_LOCATION_CONVERTER = 1003;
+ static final int FILE_LOCATION_CONVERTER = 1004;
+
+ static final int RELATIVE_TIME_CONVERTER = 2000;
+ static final int THREAD_CONVERTER = 2001;
+ static final int LEVEL_CONVERTER = 2002;
+ static final int NDC_CONVERTER = 2003;
+ static final int MESSAGE_CONVERTER = 2004;
+
+ int state;
+ protected StringBuffer currentLiteral = new StringBuffer(32);
+ protected int patternLength;
+ protected int i;
+ PatternConverter head;
+ PatternConverter tail;
+ protected FormattingInfo formattingInfo = new FormattingInfo();
+ protected String pattern;
+
+ public
+ PatternParser(String pattern) {
+ this.pattern = pattern;
+ patternLength = pattern.length();
+ state = LITERAL_STATE;
+ }
+
+ private
+ void addToList(PatternConverter pc) {
+ if(head == null) {
+ head = tail = pc;
+ } else {
+ tail.next = pc;
+ tail = pc;
+ }
+ }
+
+ protected
+ String extractOption() {
+ if((i < patternLength) && (pattern.charAt(i) == '{')) {
+ int end = pattern.indexOf('}', i);
+ if (end > i) {
+ String r = pattern.substring(i + 1, end);
+ i = end+1;
+ return r;
+ }
+ }
+ return null;
+ }
+
+
+ /**
+ The option is expected to be in decimal and positive. In case of
+ error, zero is returned. */
+ protected
+ int extractPrecisionOption() {
+ String opt = extractOption();
+ int r = 0;
+ if(opt != null) {
+ try {
+ r = Integer.parseInt(opt);
+ if(r <= 0) {
+ LogLog.error(
+ "Precision option (" + opt + ") isn't a positive integer.");
+ r = 0;
+ }
+ }
+ catch (NumberFormatException e) {
+ LogLog.error("Category option \""+opt+"\" not a decimal integer.", e);
+ }
+ }
+ return r;
+ }
+
+ public
+ PatternConverter parse() {
+ char c;
+ i = 0;
+ while(i < patternLength) {
+ c = pattern.charAt(i++);
+ switch(state) {
+ case LITERAL_STATE:
+ // In literal state, the last char is always a literal.
+ if(i == patternLength) {
+ currentLiteral.append(c);
+ continue;
+ }
+ if(c == ESCAPE_CHAR) {
+ // peek at the next char.
+ switch(pattern.charAt(i)) {
+ case ESCAPE_CHAR:
+ currentLiteral.append(c);
+ i++; // move pointer
+ break;
+ case 'n':
+ currentLiteral.append(Layout.LINE_SEP);
+ i++; // move pointer
+ break;
+ default:
+ if(currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(
+ currentLiteral.toString()));
+ //LogLog.debug("Parsed LITERAL converter: \""
+ // +currentLiteral+"\".");
+ }
+ currentLiteral.setLength(0);
+ currentLiteral.append(c); // append %
+ state = CONVERTER_STATE;
+ formattingInfo.reset();
+ }
+ }
+ else {
+ currentLiteral.append(c);
+ }
+ break;
+ case CONVERTER_STATE:
+ currentLiteral.append(c);
+ switch(c) {
+ case '-':
+ formattingInfo.leftAlign = true;
+ break;
+ case '.':
+ state = DOT_STATE;
+ break;
+ default:
+ if(c >= '0' && c <= '9') {
+ formattingInfo.min = c - '0';
+ state = MIN_STATE;
+ }
+ else
+ finalizeConverter(c);
+ } // switch
+ break;
+ case MIN_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9')
+ formattingInfo.min = formattingInfo.min*10 + (c - '0');
+ else if(c == '.')
+ state = DOT_STATE;
+ else {
+ finalizeConverter(c);
+ }
+ break;
+ case DOT_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9') {
+ formattingInfo.max = c - '0';
+ state = MAX_STATE;
+ }
+ else {
+ LogLog.error("Error occured in position "+i
+ +".\n Was expecting digit, instead got char \""+c+"\".");
+ state = LITERAL_STATE;
+ }
+ break;
+ case MAX_STATE:
+ currentLiteral.append(c);
+ if(c >= '0' && c <= '9')
+ formattingInfo.max = formattingInfo.max*10 + (c - '0');
+ else {
+ finalizeConverter(c);
+ state = LITERAL_STATE;
+ }
+ break;
+ } // switch
+ } // while
+ if(currentLiteral.length() != 0) {
+ addToList(new LiteralPatternConverter(currentLiteral.toString()));
+ //LogLog.debug("Parsed LITERAL converter: \""+currentLiteral+"\".");
+ }
+ return head;
+ }
+
+ protected
+ void finalizeConverter(char c) {
+ PatternConverter pc = null;
+ switch(c) {
+ case 'c':
+ pc = new CategoryPatternConverter(formattingInfo,
+ extractPrecisionOption());
+ //LogLog.debug("CATEGORY converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'C':
+ pc = new ClassNamePatternConverter(formattingInfo,
+ extractPrecisionOption());
+ //LogLog.debug("CLASS_NAME converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'd':
+ String dateFormatStr = AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT;
+ DateFormat df;
+ String dOpt = extractOption();
+ if(dOpt != null)
+ dateFormatStr = dOpt;
+
+ if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ISO8601_DATE_FORMAT))
+ df = new ISO8601DateFormat();
+ else if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.ABS_TIME_DATE_FORMAT))
+ df = new AbsoluteTimeDateFormat();
+ else if(dateFormatStr.equalsIgnoreCase(
+ AbsoluteTimeDateFormat.DATE_AND_TIME_DATE_FORMAT))
+ df = new DateTimeDateFormat();
+ else {
+ try {
+ df = new SimpleDateFormat(dateFormatStr);
+ }
+ catch (IllegalArgumentException e) {
+ LogLog.error("Could not instantiate SimpleDateFormat with " +
+ dateFormatStr, e);
+ df = (DateFormat) OptionConverter.instantiateByClassName(
+ "org.apache.log4j.helpers.ISO8601DateFormat",
+ DateFormat.class, null);
+ }
+ }
+ pc = new DatePatternConverter(formattingInfo, df);
+ //LogLog.debug("DATE converter {"+dateFormatStr+"}.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'F':
+ pc = new LocationPatternConverter(formattingInfo,
+ FILE_LOCATION_CONVERTER);
+ //LogLog.debug("File name converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'l':
+ pc = new LocationPatternConverter(formattingInfo,
+ FULL_LOCATION_CONVERTER);
+ //LogLog.debug("Location converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'L':
+ pc = new LocationPatternConverter(formattingInfo,
+ LINE_LOCATION_CONVERTER);
+ //LogLog.debug("LINE NUMBER converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'm':
+ pc = new BasicPatternConverter(formattingInfo, MESSAGE_CONVERTER);
+ //LogLog.debug("MESSAGE converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'M':
+ pc = new LocationPatternConverter(formattingInfo,
+ METHOD_LOCATION_CONVERTER);
+ //LogLog.debug("METHOD converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'p':
+ pc = new BasicPatternConverter(formattingInfo, LEVEL_CONVERTER);
+ //LogLog.debug("LEVEL converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 'r':
+ pc = new BasicPatternConverter(formattingInfo,
+ RELATIVE_TIME_CONVERTER);
+ //LogLog.debug("RELATIVE time converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ case 't':
+ pc = new BasicPatternConverter(formattingInfo, THREAD_CONVERTER);
+ //LogLog.debug("THREAD converter.");
+ //formattingInfo.dump();
+ currentLiteral.setLength(0);
+ break;
+ /*case 'u':
+ if(i < patternLength) {
+ char cNext = pattern.charAt(i);
+ if(cNext >= '0' && cNext <= '9') {
+ pc = new UserFieldPatternConverter(formattingInfo, cNext - '0');
+ LogLog.debug("USER converter ["+cNext+"].");
+ formattingInfo.dump();
+ currentLiteral.setLength(0);
+ i++;
+ }
+ else
+ LogLog.error("Unexpected char" +cNext+" at position "+i);
+ }
+ break;*/
+ case 'x':
+ pc = new BasicPatternConverter(formattingInfo, NDC_CONVERTER);
+ //LogLog.debug("NDC converter.");
+ currentLiteral.setLength(0);
+ break;
+ case 'X':
+ String xOpt = extractOption();
+ pc = new MDCPatternConverter(formattingInfo, xOpt);
+ currentLiteral.setLength(0);
+ break;
+ default:
+ LogLog.error("Unexpected char [" +c+"] at position "+i
+ +" in conversion patterrn.");
+ pc = new LiteralPatternConverter(currentLiteral.toString());
+ currentLiteral.setLength(0);
+ }
+
+ addConverter(pc);
+ }
+
+ protected
+ void addConverter(PatternConverter pc) {
+ currentLiteral.setLength(0);
+ // Add the pattern converter to the list.
+ addToList(pc);
+ // Next pattern is assumed to be a literal.
+ state = LITERAL_STATE;
+ // Reset formatting info
+ formattingInfo.reset();
+ }
+
+ // ---------------------------------------------------------------------
+ // PatternConverters
+ // ---------------------------------------------------------------------
+
+ private static class BasicPatternConverter extends PatternConverter {
+ int type;
+
+ BasicPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ switch(type) {
+ case RELATIVE_TIME_CONVERTER:
+ return (Long.toString(event.timeStamp - LoggingEvent.getStartTime()));
+ case THREAD_CONVERTER:
+ return event.getThreadName();
+ case LEVEL_CONVERTER:
+ return event.getLevel().toString();
+ case NDC_CONVERTER:
+ return event.getNDC();
+ case MESSAGE_CONVERTER: {
+ return event.getRenderedMessage();
+ }
+ default: return null;
+ }
+ }
+ }
+
+ private static class LiteralPatternConverter extends PatternConverter {
+ private String literal;
+
+ LiteralPatternConverter(String value) {
+ literal = value;
+ }
+
+ public
+ final
+ void format(StringBuffer sbuf, LoggingEvent event) {
+ sbuf.append(literal);
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ return literal;
+ }
+ }
+
+ private static class DatePatternConverter extends PatternConverter {
+ private DateFormat df;
+ private Date date;
+
+ DatePatternConverter(FormattingInfo formattingInfo, DateFormat df) {
+ super(formattingInfo);
+ date = new Date();
+ this.df = df;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ date.setTime(event.timeStamp);
+ String converted = null;
+ try {
+ converted = df.format(date);
+ }
+ catch (Exception ex) {
+ LogLog.error("Error occured while converting date.", ex);
+ }
+ return converted;
+ }
+ }
+
+ private static class MDCPatternConverter extends PatternConverter {
+ private String key;
+
+ MDCPatternConverter(FormattingInfo formattingInfo, String key) {
+ super(formattingInfo);
+ this.key = key;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ if (key == null) {
+ StringBuffer buf = new StringBuffer("{");
+ Map properties = event.getProperties();
+ if (properties.size() > 0) {
+ Object[] keys = properties.keySet().toArray();
+ Arrays.sort(keys);
+ for (int i = 0; i < keys.length; i++) {
+ buf.append('{');
+ buf.append(keys[i]);
+ buf.append(',');
+ buf.append(properties.get(keys[i]));
+ buf.append('}');
+ }
+ }
+ buf.append('}');
+ return buf.toString();
+ } else {
+ Object val = event.getMDC(key);
+ if(val == null) {
+ return null;
+ } else {
+ return val.toString();
+ }
+ }
+ }
+ }
+
+
+ private class LocationPatternConverter extends PatternConverter {
+ int type;
+
+ LocationPatternConverter(FormattingInfo formattingInfo, int type) {
+ super(formattingInfo);
+ this.type = type;
+ }
+
+ public
+ String convert(LoggingEvent event) {
+ LocationInfo locationInfo = event.getLocationInformation();
+ switch(type) {
+ case FULL_LOCATION_CONVERTER:
+ return locationInfo.fullInfo;
+ case METHOD_LOCATION_CONVERTER:
+ return locationInfo.getMethodName();
+ case LINE_LOCATION_CONVERTER:
+ return locationInfo.getLineNumber();
+ case FILE_LOCATION_CONVERTER:
+ return locationInfo.getFileName();
+ default: return null;
+ }
+ }
+ }
+
+ private static abstract class NamedPatternConverter extends PatternConverter {
+ int precision;
+
+ NamedPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo);
+ this.precision = precision;
+ }
+
+ abstract
+ String getFullyQualifiedName(LoggingEvent event);
+
+ public
+ String convert(LoggingEvent event) {
+ String n = getFullyQualifiedName(event);
+ if(precision <= 0)
+ return n;
+ else {
+ int len = n.length();
+
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = len -1 ;
+ for(int i = precision; i > 0; i--) {
+ end = n.lastIndexOf('.', end-1);
+ if(end == -1)
+ return n;
+ }
+ return n.substring(end+1, len);
+ }
+ }
+ }
+
+ private class ClassNamePatternConverter extends NamedPatternConverter {
+
+ ClassNamePatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(LoggingEvent event) {
+ return event.getLocationInformation().getClassName();
+ }
+ }
+
+ private class CategoryPatternConverter extends NamedPatternConverter {
+
+ CategoryPatternConverter(FormattingInfo formattingInfo, int precision) {
+ super(formattingInfo, precision);
+ }
+
+ String getFullyQualifiedName(LoggingEvent event) {
+ return event.getLoggerName();
+ }
+ }
+}
+
diff --git a/java/src/org/apache/log4j/helpers/QuietWriter.java b/java/src/org/apache/log4j/helpers/QuietWriter.java
new file mode 100644
index 0000000..778f091
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/QuietWriter.java
@@ -0,0 +1,76 @@
+/*
+ * 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.log4j.helpers;
+
+import java.io.Writer;
+import java.io.FilterWriter;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.ErrorCode;
+
+
+/**
+ QuietWriter does not throw exceptions when things go
+ wrong. Instead, it delegates error handling to its {@link ErrorHandler}.
+
+ @author Ceki Gülcü
+
+ @since 0.7.3
+*/
+public class QuietWriter extends FilterWriter {
+
+ protected ErrorHandler errorHandler;
+
+ public
+ QuietWriter(Writer writer, ErrorHandler errorHandler) {
+ super(writer);
+ setErrorHandler(errorHandler);
+ }
+
+ public
+ void write(String string) {
+ if (string != null) {
+ try {
+ out.write(string);
+ } catch(Exception e) {
+ errorHandler.error("Failed to write ["+string+"].", e,
+ ErrorCode.WRITE_FAILURE);
+ }
+ }
+ }
+
+ public
+ void flush() {
+ try {
+ out.flush();
+ } catch(Exception e) {
+ errorHandler.error("Failed to flush writer,", e,
+ ErrorCode.FLUSH_FAILURE);
+ }
+ }
+
+
+ public
+ void setErrorHandler(ErrorHandler eh) {
+ if(eh == null) {
+ // This is a programming error on the part of the enclosing appender.
+ throw new IllegalArgumentException("Attempted to set null ErrorHandler.");
+ } else {
+ this.errorHandler = eh;
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/RelativeTimeDateFormat.java b/java/src/org/apache/log4j/helpers/RelativeTimeDateFormat.java
new file mode 100644
index 0000000..ab81a34
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/RelativeTimeDateFormat.java
@@ -0,0 +1,65 @@
+/*
+ * 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.log4j.helpers;
+
+import java.util.Date;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.text.DateFormat;
+
+/**
+ Formats a {@link Date} by printing the number of milliseconds
+ elapsed since construction of the format. This is the fastest
+ printing DateFormat in the package.
+
+ @author Ceki Gülcü
+
+ @since 0.7.5
+*/
+public class RelativeTimeDateFormat extends DateFormat {
+ private static final long serialVersionUID = 7055751607085611984L;
+
+
+ protected final long startTime;
+
+ public
+ RelativeTimeDateFormat() {
+ this.startTime = System.currentTimeMillis();
+ }
+
+ /**
+ Appends to sbuf the number of milliseconds elapsed
+ since the start of the application.
+
+ @since 0.7.5
+ */
+ public
+ StringBuffer format(Date date, StringBuffer sbuf,
+ FieldPosition fieldPosition) {
+ //System.err.println(":"+ date.getTime() + " - " + startTime);
+ return sbuf.append((date.getTime() - startTime));
+ }
+
+ /**
+ This method does not do anything but return null.
+ */
+ public
+ Date parse(java.lang.String s, ParsePosition pos) {
+ return null;
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/SyslogQuietWriter.java b/java/src/org/apache/log4j/helpers/SyslogQuietWriter.java
new file mode 100644
index 0000000..62e933e
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/SyslogQuietWriter.java
@@ -0,0 +1,56 @@
+/*
+ * 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.log4j.helpers;
+
+
+
+import java.io.Writer;
+import org.apache.log4j.spi.ErrorHandler;
+
+/**
+ SyslogQuietWriter extends QuietWriter by prepending the syslog
+ level code before each printed String.
+
+ @since 0.7.3
+*/
+public class SyslogQuietWriter extends QuietWriter {
+
+ int syslogFacility;
+ int level;
+
+ public
+ SyslogQuietWriter(Writer writer, int syslogFacility, ErrorHandler eh) {
+ super(writer, eh);
+ this.syslogFacility = syslogFacility;
+ }
+
+ public
+ void setLevel(int level) {
+ this.level = level;
+ }
+
+ public
+ void setSyslogFacility(int syslogFacility) {
+ this.syslogFacility = syslogFacility;
+ }
+
+ public
+ void write(String string) {
+ super.write("<"+(syslogFacility | level)+">" + string);
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/SyslogWriter.java b/java/src/org/apache/log4j/helpers/SyslogWriter.java
new file mode 100644
index 0000000..d6ce4bf
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/SyslogWriter.java
@@ -0,0 +1,145 @@
+/*
+ * 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.log4j.helpers;
+
+
+import java.io.Writer;
+import java.net.DatagramSocket;
+import java.net.InetAddress;
+import java.net.DatagramPacket;
+import java.net.UnknownHostException;
+import java.net.SocketException;
+import java.io.IOException;
+import java.net.URL;
+import java.net.MalformedURLException;
+
+/**
+ SyslogWriter is a wrapper around the java.net.DatagramSocket class
+ so that it behaves like a java.io.Writer.
+
+ @since 0.7.3
+*/
+public class SyslogWriter extends Writer {
+
+ final int SYSLOG_PORT = 514;
+ /**
+ * Host string from last constructed SyslogWriter.
+ * @deprecated
+ */
+ static String syslogHost;
+
+ private InetAddress address;
+ private final int port;
+ private DatagramSocket ds;
+
+ /**
+ * Constructs a new instance of SyslogWriter.
+ * @param syslogHost host name, may not be null. A port
+ * may be specified by following the name or IPv4 literal address with
+ * a colon and a decimal port number. To specify a port with an IPv6
+ * address, enclose the IPv6 address in square brackets before appending
+ * the colon and decimal port number.
+ */
+ public
+ SyslogWriter(final String syslogHost) {
+ SyslogWriter.syslogHost = syslogHost;
+ if (syslogHost == null) {
+ throw new NullPointerException("syslogHost");
+ }
+
+ String host = syslogHost;
+ int urlPort = -1;
+
+ //
+ // If not an unbracketed IPv6 address then
+ // parse as a URL
+ //
+ if (host.indexOf("[") != -1 || host.indexOf(':') == host.lastIndexOf(':')) {
+ try {
+ URL url = new URL("http://" + host);
+ if (url.getHost() != null) {
+ host = url.getHost();
+ // if host is a IPv6 literal, strip off the brackets
+ if(host.startsWith("[") && host.charAt(host.length() - 1) == ']') {
+ host = host.substring(1, host.length() - 1);
+ }
+ urlPort = url.getPort();
+ }
+ } catch(MalformedURLException e) {
+ LogLog.error("Malformed URL: will attempt to interpret as InetAddress.", e);
+ }
+ }
+
+ if (urlPort == -1) {
+ urlPort = SYSLOG_PORT;
+ }
+ port = urlPort;
+
+ try {
+ this.address = InetAddress.getByName(host);
+ }
+ catch (UnknownHostException e) {
+ LogLog.error("Could not find " + host +
+ ". All logging will FAIL.", e);
+ }
+
+ try {
+ this.ds = new DatagramSocket();
+ }
+ catch (SocketException e) {
+ e.printStackTrace();
+ LogLog.error("Could not instantiate DatagramSocket to " + host +
+ ". All logging will FAIL.", e);
+ }
+
+ }
+
+
+ public
+ void write(char[] buf, int off, int len) throws IOException {
+ this.write(new String(buf, off, len));
+ }
+
+ public
+ void write(final String string) throws IOException {
+
+ if(this.ds != null && this.address != null) {
+ byte[] bytes = string.getBytes();
+ //
+ // syslog packets must be less than 1024 bytes
+ //
+ int bytesLength = bytes.length;
+ if (bytesLength >= 1024) {
+ bytesLength = 1024;
+ }
+ DatagramPacket packet = new DatagramPacket(bytes, bytesLength,
+ address, port);
+ ds.send(packet);
+ }
+
+ }
+
+ public
+ void flush() {}
+
+ public void close() {
+ if (ds != null) {
+ ds.close();
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/ThreadLocalMap.java b/java/src/org/apache/log4j/helpers/ThreadLocalMap.java
new file mode 100644
index 0000000..da60c86
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/ThreadLocalMap.java
@@ -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.
+ */
+
+package org.apache.log4j.helpers;
+
+import java.util.Hashtable;
+
+/**
+ ThreadLocalMap extends {@link InheritableThreadLocal}
+ to bequeath a copy of the hashtable of the MDC of the parent
+ thread.
+
+ @author Ceki Gülcü
+ @since 1.2
+*/
+final public class ThreadLocalMap extends InheritableThreadLocal {
+
+ public
+ final
+ Object childValue(Object parentValue) {
+ Hashtable ht = (Hashtable) parentValue;
+ if(ht != null) {
+ return ht.clone();
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/Transform.java b/java/src/org/apache/log4j/helpers/Transform.java
new file mode 100644
index 0000000..7626e71
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/Transform.java
@@ -0,0 +1,113 @@
+/*
+ * 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.log4j.helpers;
+
+/**
+ Utility class for transforming strings.
+
+ @author Ceki Gülcü
+ @author Michael A. McAngus
+ */
+public class Transform {
+
+ private static final String CDATA_START = "";
+ private static final String CDATA_PSEUDO_END = "]]>";
+ private static final String CDATA_EMBEDED_END = CDATA_END + CDATA_PSEUDO_END + CDATA_START;
+ private static final int CDATA_END_LEN = CDATA_END.length();
+
+ /**
+ * This method takes a string which may contain HTML tags (ie,
+ * <b>, <table>, etc) and replaces any
+ * '<', '>' , '&' or '"'
+ * characters with respective predefined entity references.
+ *
+ * @param input The text to be converted.
+ * @return The input string with the special characters replaced.
+ * */
+ static public String escapeTags(final String input) {
+ //Check if the string is null, zero length or devoid of special characters
+ // if so, return what was sent in.
+
+ if(input == null
+ || input.length() == 0
+ || (input.indexOf('"') == -1 &&
+ input.indexOf('&') == -1 &&
+ input.indexOf('<') == -1 &&
+ input.indexOf('>') == -1)) {
+ return input;
+ }
+
+ //Use a StringBuffer in lieu of String concatenation -- it is
+ //much more efficient this way.
+
+ StringBuffer buf = new StringBuffer(input.length() + 6);
+ char ch = ' ';
+
+ int len = input.length();
+ for(int i=0; i < len; i++) {
+ ch = input.charAt(i);
+ if (ch > '>') {
+ buf.append(ch);
+ } else if(ch == '<') {
+ buf.append("<");
+ } else if(ch == '>') {
+ buf.append(">");
+ } else if(ch == '&') {
+ buf.append("&");
+ } else if(ch == '"') {
+ buf.append(""");
+ } else {
+ buf.append(ch);
+ }
+ }
+ return buf.toString();
+ }
+
+ /**
+ * Ensures that embeded CDEnd strings (]]>) are handled properly
+ * within message, NDC and throwable tag text.
+ *
+ * @param buf StringBuffer holding the XML data to this point. The
+ * initial CDStart () of the CDATA
+ * section are the responsibility of the calling method.
+ * @param str The String that is inserted into an existing CDATA Section within buf.
+ * */
+ static public void appendEscapingCDATA(final StringBuffer buf,
+ final String str) {
+ if (str != null) {
+ int end = str.indexOf(CDATA_END);
+ if (end < 0) {
+ buf.append(str);
+ } else {
+ int start = 0;
+ while (end > -1) {
+ buf.append(str.substring(start, end));
+ buf.append(CDATA_EMBEDED_END);
+ start = end + CDATA_END_LEN;
+ if (start < str.length()) {
+ end = str.indexOf(CDATA_END, start);
+ } else {
+ return;
+ }
+ }
+ buf.append(str.substring(start));
+ }
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/helpers/package.html b/java/src/org/apache/log4j/helpers/package.html
new file mode 100644
index 0000000..ab75bf3
--- /dev/null
+++ b/java/src/org/apache/log4j/helpers/package.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+This package is used internally.
+
+
+
+
+
+Last modified: Sat Jul 3 15:12:58 MDT 1999
+
+
diff --git a/java/src/org/apache/log4j/jdbc/JDBCAppender.java b/java/src/org/apache/log4j/jdbc/JDBCAppender.java
new file mode 100644
index 0000000..f18974a
--- /dev/null
+++ b/java/src/org/apache/log4j/jdbc/JDBCAppender.java
@@ -0,0 +1,398 @@
+/*
+ * 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.log4j.jdbc;
+
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.SQLException;
+import java.sql.Statement;
+import java.util.ArrayList;
+import java.util.Iterator;
+
+import org.apache.log4j.PatternLayout;
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ The JDBCAppender provides for sending log events to a database.
+
+ WARNING: This version of JDBCAppender
+ is very likely to be completely replaced in the future. Moreoever,
+ it does not log exceptions.
+
+
Each append call adds to an ArrayList buffer. When
+ the buffer is filled each log event is placed in a sql statement
+ (configurable) and executed.
+
+ BufferSize, db URL, User, & Password are
+ configurable options in the standard log4j ways.
+
+
The setSql(String sql) sets the SQL statement to be
+ used for logging -- this statement is sent to a
+ PatternLayout (either created automaticly by the
+ appender or added by the user). Therefore by default all the
+ conversion patterns in PatternLayout can be used
+ inside of the statement. (see the test cases for examples)
+
+
Overriding the {@link #getLogStatement} method allows more
+ explicit control of the statement used for logging.
+
+
For use as a base class:
+
+
+
+ - Override
getConnection() to pass any connection
+ you want. Typically this is used to enable application wide
+ connection pooling.
+
+ - Override
closeConnection(Connection con) -- if
+ you override getConnection make sure to implement
+ closeConnection to handle the connection you
+ generated. Typically this would return the connection to the
+ pool it came from.
+
+ - Override
getLogStatement(LoggingEvent event) to
+ produce specialized or dynamic statements. The default uses the
+ sql option value.
+
+
+
+ @author Kevin Steppe (ksteppe@pacbell.net)
+
+*/
+public class JDBCAppender extends org.apache.log4j.AppenderSkeleton
+ implements org.apache.log4j.Appender {
+
+ /**
+ * URL of the DB for default connection handling
+ */
+ protected String databaseURL = "jdbc:odbc:myDB";
+
+ /**
+ * User to connect as for default connection handling
+ */
+ protected String databaseUser = "me";
+
+ /**
+ * User to use for default connection handling
+ */
+ protected String databasePassword = "mypassword";
+
+ /**
+ * Connection used by default. The connection is opened the first time it
+ * is needed and then held open until the appender is closed (usually at
+ * garbage collection). This behavior is best modified by creating a
+ * sub-class and overriding the getConnection and
+ * closeConnection methods.
+ */
+ protected Connection connection = null;
+
+ /**
+ * Stores the string given to the pattern layout for conversion into a SQL
+ * statement, eg: insert into LogTable (Thread, Class, Message) values
+ * ("%t", "%c", "%m").
+ *
+ * Be careful of quotes in your messages!
+ *
+ * Also see PatternLayout.
+ */
+ protected String sqlStatement = "";
+
+ /**
+ * size of LoggingEvent buffer before writting to the database.
+ * Default is 1.
+ */
+ protected int bufferSize = 1;
+
+ /**
+ * ArrayList holding the buffer of Logging Events.
+ */
+ protected ArrayList buffer;
+
+ /**
+ * Helper object for clearing out the buffer
+ */
+ protected ArrayList removes;
+
+ private boolean locationInfo = false;
+
+ public JDBCAppender() {
+ super();
+ buffer = new ArrayList(bufferSize);
+ removes = new ArrayList(bufferSize);
+ }
+
+ /**
+ * Gets whether the location of the logging request call
+ * should be captured.
+ *
+ * @since 1.2.16
+ * @return the current value of the LocationInfo option.
+ */
+ public boolean getLocationInfo() {
+ return locationInfo;
+ }
+
+ /**
+ * The LocationInfo option takes a boolean value. By default, it is
+ * set to false which means there will be no effort to extract the location
+ * information related to the event. As a result, the event that will be
+ * ultimately logged will likely to contain the wrong location information
+ * (if present in the log format).
+ *
+ *
+ * Location information extraction is comparatively very slow and should be
+ * avoided unless performance is not a concern.
+ *
+ * @since 1.2.16
+ * @param flag true if location information should be extracted.
+ */
+ public void setLocationInfo(final boolean flag) {
+ locationInfo = flag;
+ }
+
+
+ /**
+ * Adds the event to the buffer. When full the buffer is flushed.
+ */
+ public void append(LoggingEvent event) {
+ event.getNDC();
+ event.getThreadName();
+ // Get a copy of this thread's MDC.
+ event.getMDCCopy();
+ if (locationInfo) {
+ event.getLocationInformation();
+ }
+ event.getRenderedMessage();
+ event.getThrowableStrRep();
+ buffer.add(event);
+
+ if (buffer.size() >= bufferSize)
+ flushBuffer();
+ }
+
+ /**
+ * By default getLogStatement sends the event to the required Layout object.
+ * The layout will format the given pattern into a workable SQL string.
+ *
+ * Overriding this provides direct access to the LoggingEvent
+ * when constructing the logging statement.
+ *
+ */
+ protected String getLogStatement(LoggingEvent event) {
+ return getLayout().format(event);
+ }
+
+ /**
+ *
+ * Override this to provide an alertnate method of getting
+ * connections (such as caching). One method to fix this is to open
+ * connections at the start of flushBuffer() and close them at the
+ * end. I use a connection pool outside of JDBCAppender which is
+ * accessed in an override of this method.
+ * */
+ protected void execute(String sql) throws SQLException {
+
+ Connection con = null;
+ Statement stmt = null;
+
+ try {
+ con = getConnection();
+
+ stmt = con.createStatement();
+ stmt.executeUpdate(sql);
+ } catch (SQLException e) {
+ if (stmt != null)
+ stmt.close();
+ throw e;
+ }
+ stmt.close();
+ closeConnection(con);
+
+ //System.out.println("Execute: " + sql);
+ }
+
+
+ /**
+ * Override this to return the connection to a pool, or to clean up the
+ * resource.
+ *
+ * The default behavior holds a single connection open until the appender
+ * is closed (typically when garbage collected).
+ */
+ protected void closeConnection(Connection con) {
+ }
+
+ /**
+ * Override this to link with your connection pooling system.
+ *
+ * By default this creates a single connection which is held open
+ * until the object is garbage collected.
+ */
+ protected Connection getConnection() throws SQLException {
+ if (!DriverManager.getDrivers().hasMoreElements())
+ setDriver("sun.jdbc.odbc.JdbcOdbcDriver");
+
+ if (connection == null) {
+ connection = DriverManager.getConnection(databaseURL, databaseUser,
+ databasePassword);
+ }
+
+ return connection;
+ }
+
+ /**
+ * Closes the appender, flushing the buffer first then closing the default
+ * connection if it is open.
+ */
+ public void close()
+ {
+ flushBuffer();
+
+ try {
+ if (connection != null && !connection.isClosed())
+ connection.close();
+ } catch (SQLException e) {
+ errorHandler.error("Error closing connection", e, ErrorCode.GENERIC_FAILURE);
+ }
+ this.closed = true;
+ }
+
+ /**
+ * loops through the buffer of LoggingEvents, gets a
+ * sql string from getLogStatement() and sends it to execute().
+ * Errors are sent to the errorHandler.
+ *
+ * If a statement fails the LoggingEvent stays in the buffer!
+ */
+ public void flushBuffer() {
+ //Do the actual logging
+ removes.ensureCapacity(buffer.size());
+ for (Iterator i = buffer.iterator(); i.hasNext();) {
+ try {
+ LoggingEvent logEvent = (LoggingEvent)i.next();
+ String sql = getLogStatement(logEvent);
+ execute(sql);
+ removes.add(logEvent);
+ }
+ catch (SQLException e) {
+ errorHandler.error("Failed to excute sql", e,
+ ErrorCode.FLUSH_FAILURE);
+ }
+ }
+
+ // remove from the buffer any events that were reported
+ buffer.removeAll(removes);
+
+ // clear the buffer of reported events
+ removes.clear();
+ }
+
+
+ /** closes the appender before disposal */
+ public void finalize() {
+ close();
+ }
+
+
+ /**
+ * JDBCAppender requires a layout.
+ * */
+ public boolean requiresLayout() {
+ return true;
+ }
+
+
+ /**
+ *
+ */
+ public void setSql(String s) {
+ sqlStatement = s;
+ if (getLayout() == null) {
+ this.setLayout(new PatternLayout(s));
+ }
+ else {
+ ((PatternLayout)getLayout()).setConversionPattern(s);
+ }
+ }
+
+
+ /**
+ * Returns pre-formated statement eg: insert into LogTable (msg) values ("%m")
+ */
+ public String getSql() {
+ return sqlStatement;
+ }
+
+
+ public void setUser(String user) {
+ databaseUser = user;
+ }
+
+
+ public void setURL(String url) {
+ databaseURL = url;
+ }
+
+
+ public void setPassword(String password) {
+ databasePassword = password;
+ }
+
+
+ public void setBufferSize(int newBufferSize) {
+ bufferSize = newBufferSize;
+ buffer.ensureCapacity(bufferSize);
+ removes.ensureCapacity(bufferSize);
+ }
+
+
+ public String getUser() {
+ return databaseUser;
+ }
+
+
+ public String getURL() {
+ return databaseURL;
+ }
+
+
+ public String getPassword() {
+ return databasePassword;
+ }
+
+
+ public int getBufferSize() {
+ return bufferSize;
+ }
+
+
+ /**
+ * Ensures that the given driver class has been loaded for sql connection
+ * creation.
+ */
+ public void setDriver(String driverClass) {
+ try {
+ Class.forName(driverClass);
+ } catch (Exception e) {
+ errorHandler.error("Failed to load driver", e,
+ ErrorCode.GENERIC_FAILURE);
+ }
+ }
+}
+
diff --git a/java/src/org/apache/log4j/jdbc/package.html b/java/src/org/apache/log4j/jdbc/package.html
new file mode 100644
index 0000000..e57e3a5
--- /dev/null
+++ b/java/src/org/apache/log4j/jdbc/package.html
@@ -0,0 +1,23 @@
+
+
+
+
+ The JDBCAppender provides for sending log events to a database.
+
+
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/jmx/AbstractDynamicMBean.java b/java/src/org/apache/log4j/jmx/AbstractDynamicMBean.java
new file mode 100644
index 0000000..a5a6574
--- /dev/null
+++ b/java/src/org/apache/log4j/jmx/AbstractDynamicMBean.java
@@ -0,0 +1,187 @@
+/*
+ * 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.log4j.jmx;
+
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Vector;
+
+import javax.management.Attribute;
+import javax.management.AttributeList;
+import javax.management.DynamicMBean;
+import javax.management.InstanceAlreadyExistsException;
+import javax.management.InstanceNotFoundException;
+import javax.management.JMException;
+import javax.management.MBeanRegistration;
+import javax.management.MBeanRegistrationException;
+import javax.management.MBeanServer;
+import javax.management.NotCompliantMBeanException;
+import javax.management.ObjectName;
+import javax.management.RuntimeOperationsException;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.Appender;
+
+public abstract class AbstractDynamicMBean implements DynamicMBean,
+ MBeanRegistration {
+
+ String dClassName;
+ MBeanServer server;
+ private final Vector mbeanList = new Vector();
+
+ /**
+ * Get MBean name.
+ * @param appender appender, may not be null.
+ * @return name.
+ * @since 1.2.16
+ */
+ static protected String getAppenderName(final Appender appender){
+ String name = appender.getName();
+ if (name == null || name.trim().length() == 0) {
+ // try to get some form of a name, because null is not allowed (exception), and empty string certainly isn't useful in JMX..
+ name = appender.toString();
+ }
+ return name;
+ }
+
+
+ /**
+ * Enables the to get the values of several attributes of the Dynamic MBean.
+ */
+ public
+ AttributeList getAttributes(String[] attributeNames) {
+
+ // Check attributeNames is not null to avoid NullPointerException later on
+ if (attributeNames == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("attributeNames[] cannot be null"),
+ "Cannot invoke a getter of " + dClassName);
+ }
+
+ AttributeList resultList = new AttributeList();
+
+ // if attributeNames is empty, return an empty result list
+ if (attributeNames.length == 0)
+ return resultList;
+
+ // build the result attribute list
+ for (int i=0 ; i 0) {
+ val = attributeName.substring(0, k)+'='+ attributeName.substring(k+3);
+ }
+ try {
+ return new ObjectName("log4j:"+val);
+ } catch(JMException e) {
+ log.error("Could not create ObjectName" + val);
+ } catch(RuntimeException e) {
+ log.error("Could not create ObjectName" + val);
+ }
+ }
+
+
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw(new AttributeNotFoundException("Cannot find " + attributeName +
+ " attribute in " + dClassName));
+
+ }
+
+
+ public
+ void addAppenderEvent(Category logger, Appender appender) {
+ log.debug("addAppenderEvent called: logger="+logger.getName()+
+ ", appender="+appender.getName());
+ Notification n = new Notification(ADD_APPENDER+logger.getName(), this, 0);
+ n.setUserData(appender);
+ log.debug("sending notification.");
+ nbs.sendNotification(n);
+ }
+
+ public
+ void removeAppenderEvent(Category cat, Appender appender) {
+ log.debug("removeAppenderCalled: logger="+cat.getName()+
+ ", appender="+appender.getName());
+ }
+
+ public
+ void postRegister(java.lang.Boolean registrationDone) {
+ log.debug("postRegister is called.");
+ hierarchy.addHierarchyEventListener(this);
+ Logger root = hierarchy.getRootLogger();
+ addLoggerMBean(root);
+ }
+
+ public
+ void removeNotificationListener(NotificationListener listener)
+ throws ListenerNotFoundException {
+ nbs.removeNotificationListener(listener);
+ }
+
+ public
+ void setAttribute(Attribute attribute) throws AttributeNotFoundException,
+ InvalidAttributeValueException,
+ MBeanException,
+ ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of "+dClassName+" with null attribute");
+ }
+ String name = attribute.getName();
+ Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of "+dClassName+
+ " with null attribute name");
+ }
+
+ if(name.equals(THRESHOLD)) {
+ Level l = OptionConverter.toLevel((String) value,
+ hierarchy.getThreshold());
+ hierarchy.setThreshold(l);
+ }
+
+
+ }
+}
diff --git a/java/src/org/apache/log4j/jmx/LayoutDynamicMBean.java b/java/src/org/apache/log4j/jmx/LayoutDynamicMBean.java
new file mode 100644
index 0000000..4be6b63
--- /dev/null
+++ b/java/src/org/apache/log4j/jmx/LayoutDynamicMBean.java
@@ -0,0 +1,274 @@
+/*
+ * 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.log4j.jmx;
+
+import java.lang.reflect.Constructor;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Level;
+import org.apache.log4j.Layout;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.OptionHandler;
+
+import java.util.Vector;
+import java.util.Hashtable;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanInfo;
+import javax.management.Attribute;
+
+import javax.management.MBeanException;
+import javax.management.AttributeNotFoundException;
+import javax.management.RuntimeOperationsException;
+import javax.management.ReflectionException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+
+import java.beans.Introspector;
+import java.beans.BeanInfo;
+import java.beans.PropertyDescriptor;
+import java.beans.IntrospectionException;
+import java.io.InterruptedIOException;
+
+public class LayoutDynamicMBean extends AbstractDynamicMBean {
+
+ private MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
+ private Vector dAttributes = new Vector();
+ private String dClassName = this.getClass().getName();
+
+ private Hashtable dynamicProps = new Hashtable(5);
+ private MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];
+ private String dDescription =
+ "This MBean acts as a management facade for log4j layouts.";
+
+ // This category instance is for logging.
+ private static Logger cat = Logger.getLogger(LayoutDynamicMBean.class);
+
+ // We wrap this layout instance.
+ private Layout layout;
+
+ public LayoutDynamicMBean(Layout layout) throws IntrospectionException {
+ this.layout = layout;
+ buildDynamicMBeanInfo();
+ }
+
+ private
+ void buildDynamicMBeanInfo() throws IntrospectionException {
+ Constructor[] constructors = this.getClass().getConstructors();
+ dConstructors[0] = new MBeanConstructorInfo(
+ "LayoutDynamicMBean(): Constructs a LayoutDynamicMBean instance",
+ constructors[0]);
+
+
+ BeanInfo bi = Introspector.getBeanInfo(layout.getClass());
+ PropertyDescriptor[] pd = bi.getPropertyDescriptors();
+
+ int size = pd.length;
+
+ for(int i = 0; i < size; i++) {
+ String name = pd[i].getName();
+ Method readMethod = pd[i].getReadMethod();
+ Method writeMethod = pd[i].getWriteMethod();
+ if(readMethod != null) {
+ Class returnClass = readMethod.getReturnType();
+ if(isSupportedType(returnClass)) {
+ String returnClassName;
+ if(returnClass.isAssignableFrom(Level.class)) {
+ returnClassName = "java.lang.String";
+ } else {
+ returnClassName = returnClass.getName();
+ }
+
+ dAttributes.add(new MBeanAttributeInfo(name,
+ returnClassName,
+ "Dynamic",
+ true,
+ writeMethod != null,
+ false));
+ dynamicProps.put(name, new MethodUnion(readMethod, writeMethod));
+ }
+ }
+ }
+
+ MBeanParameterInfo[] params = new MBeanParameterInfo[0];
+
+ dOperations[0] = new MBeanOperationInfo("activateOptions",
+ "activateOptions(): add an layout",
+ params,
+ "void",
+ MBeanOperationInfo.ACTION);
+ }
+
+ private
+ boolean isSupportedType(Class clazz) {
+ if(clazz.isPrimitive()) {
+ return true;
+ }
+
+ if(clazz == String.class) {
+ return true;
+ }
+ if(clazz.isAssignableFrom(Level.class)) {
+ return true;
+ }
+
+ return false;
+ }
+
+
+
+ public
+ MBeanInfo getMBeanInfo() {
+ cat.debug("getMBeanInfo called.");
+
+ MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()];
+ dAttributes.toArray(attribs);
+
+ return new MBeanInfo(dClassName,
+ dDescription,
+ attribs,
+ dConstructors,
+ dOperations,
+ new MBeanNotificationInfo[0]);
+ }
+
+ public
+ Object invoke(String operationName, Object params[], String signature[])
+ throws MBeanException,
+ ReflectionException {
+
+ if(operationName.equals("activateOptions") &&
+ layout instanceof OptionHandler) {
+ OptionHandler oh = (OptionHandler) layout;
+ oh.activateOptions();
+ return "Options activated.";
+ }
+ return null;
+ }
+
+ protected
+ Logger getLogger() {
+ return cat;
+ }
+
+
+ public
+ Object getAttribute(String attributeName) throws AttributeNotFoundException,
+ MBeanException,
+ ReflectionException {
+
+ // Check attributeName is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException(
+ "Attribute name cannot be null"),
+ "Cannot invoke a getter of " + dClassName + " with null attribute name");
+ }
+
+
+ MethodUnion mu = (MethodUnion) dynamicProps.get(attributeName);
+
+ cat.debug("----name="+attributeName+", mu="+mu);
+
+ if(mu != null && mu.readMethod != null) {
+ try {
+ return mu.readMethod.invoke(layout, null);
+ } catch(InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ return null;
+ } catch(IllegalAccessException e) {
+ return null;
+ } catch(RuntimeException e) {
+ return null;
+ }
+ }
+
+
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw(new AttributeNotFoundException("Cannot find " + attributeName +
+ " attribute in " + dClassName));
+
+ }
+
+
+ public
+ void setAttribute(Attribute attribute) throws AttributeNotFoundException,
+ InvalidAttributeValueException,
+ MBeanException,
+ ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + dClassName +
+ " with null attribute");
+ }
+ String name = attribute.getName();
+ Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of "+dClassName+
+ " with null attribute name");
+ }
+
+
+
+ MethodUnion mu = (MethodUnion) dynamicProps.get(name);
+
+ if(mu != null && mu.writeMethod != null) {
+ Object[] o = new Object[1];
+
+ Class[] params = mu.writeMethod.getParameterTypes();
+ if(params[0] == org.apache.log4j.Priority.class) {
+ value = OptionConverter.toLevel((String) value,
+ (Level) getAttribute(name));
+ }
+ o[0] = value;
+
+ try {
+ mu.writeMethod.invoke(layout, o);
+
+ } catch(InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ cat.error("FIXME", e);
+ } catch(IllegalAccessException e) {
+ cat.error("FIXME", e);
+ } catch(RuntimeException e) {
+ cat.error("FIXME", e);
+ }
+ } else {
+ throw(new AttributeNotFoundException("Attribute " + name +
+ " not found in " +
+ this.getClass().getName()));
+ }
+ }
+}
+
+
diff --git a/java/src/org/apache/log4j/jmx/LoggerDynamicMBean.java b/java/src/org/apache/log4j/jmx/LoggerDynamicMBean.java
new file mode 100644
index 0000000..b226319
--- /dev/null
+++ b/java/src/org/apache/log4j/jmx/LoggerDynamicMBean.java
@@ -0,0 +1,282 @@
+/*
+ * 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.log4j.jmx;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.OptionConverter;
+
+import javax.management.Attribute;
+import javax.management.AttributeNotFoundException;
+import javax.management.InvalidAttributeValueException;
+import javax.management.JMException;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanException;
+import javax.management.MBeanInfo;
+import javax.management.MBeanNotificationInfo;
+import javax.management.MBeanOperationInfo;
+import javax.management.MBeanParameterInfo;
+import javax.management.MalformedObjectNameException;
+import javax.management.Notification;
+import javax.management.NotificationListener;
+import javax.management.ObjectName;
+import javax.management.ReflectionException;
+import javax.management.RuntimeOperationsException;
+import java.lang.reflect.Constructor;
+import java.util.Enumeration;
+import java.util.Vector;
+
+public class LoggerDynamicMBean extends AbstractDynamicMBean
+ implements NotificationListener {
+
+ private MBeanConstructorInfo[] dConstructors = new MBeanConstructorInfo[1];
+ private MBeanOperationInfo[] dOperations = new MBeanOperationInfo[1];
+
+ private Vector dAttributes = new Vector();
+ private String dClassName = this.getClass().getName();
+
+ private String dDescription =
+ "This MBean acts as a management facade for a org.apache.log4j.Logger instance.";
+
+ // This Logger instance is for logging.
+ private static Logger cat = Logger.getLogger(LoggerDynamicMBean.class);
+
+ // We wrap this Logger instance.
+ private Logger logger;
+
+ public LoggerDynamicMBean(Logger logger) {
+ this.logger = logger;
+ buildDynamicMBeanInfo();
+ }
+
+ public
+ void handleNotification(Notification notification, Object handback) {
+ cat.debug("Received notification: "+notification.getType());
+ registerAppenderMBean((Appender) notification.getUserData() );
+
+
+ }
+
+ private
+ void buildDynamicMBeanInfo() {
+ Constructor[] constructors = this.getClass().getConstructors();
+ dConstructors[0] = new MBeanConstructorInfo(
+ "HierarchyDynamicMBean(): Constructs a HierarchyDynamicMBean instance",
+ constructors[0]);
+
+ dAttributes.add(new MBeanAttributeInfo("name",
+ "java.lang.String",
+ "The name of this Logger.",
+ true,
+ false,
+ false));
+
+ dAttributes.add(new MBeanAttributeInfo("priority",
+ "java.lang.String",
+ "The priority of this logger.",
+ true,
+ true,
+ false));
+
+
+
+
+
+ MBeanParameterInfo[] params = new MBeanParameterInfo[2];
+ params[0] = new MBeanParameterInfo("class name", "java.lang.String",
+ "add an appender to this logger");
+ params[1] = new MBeanParameterInfo("appender name", "java.lang.String",
+ "name of the appender");
+
+ dOperations[0] = new MBeanOperationInfo("addAppender",
+ "addAppender(): add an appender",
+ params,
+ "void",
+ MBeanOperationInfo.ACTION);
+ }
+
+ protected
+ Logger getLogger() {
+ return logger;
+ }
+
+
+ public
+ MBeanInfo getMBeanInfo() {
+ //cat.debug("getMBeanInfo called.");
+
+ MBeanAttributeInfo[] attribs = new MBeanAttributeInfo[dAttributes.size()];
+ dAttributes.toArray(attribs);
+
+ MBeanInfo mb = new MBeanInfo(dClassName,
+ dDescription,
+ attribs,
+ dConstructors,
+ dOperations,
+ new MBeanNotificationInfo[0]);
+ //cat.debug("getMBeanInfo exit.");
+ return mb;
+ }
+
+ public
+ Object invoke(String operationName, Object params[], String signature[])
+ throws MBeanException,
+ ReflectionException {
+
+ if(operationName.equals("addAppender")) {
+ addAppender((String) params[0], (String) params[1]);
+ return "Hello world.";
+ }
+
+ return null;
+ }
+
+
+ public
+ Object getAttribute(String attributeName) throws AttributeNotFoundException,
+ MBeanException,
+ ReflectionException {
+
+ // Check attributeName is not null to avoid NullPointerException later on
+ if (attributeName == null) {
+ throw new RuntimeOperationsException(new IllegalArgumentException(
+ "Attribute name cannot be null"),
+ "Cannot invoke a getter of " + dClassName + " with null attribute name");
+ }
+
+ // Check for a recognized attributeName and call the corresponding getter
+ if (attributeName.equals("name")) {
+ return logger.getName();
+ } else if(attributeName.equals("priority")) {
+ Level l = logger.getLevel();
+ if(l == null) {
+ return null;
+ } else {
+ return l.toString();
+ }
+ } else if(attributeName.startsWith("appender=")) {
+ try {
+ return new ObjectName("log4j:"+attributeName );
+ } catch(MalformedObjectNameException e) {
+ cat.error("Could not create ObjectName" + attributeName);
+ } catch(RuntimeException e) {
+ cat.error("Could not create ObjectName" + attributeName);
+ }
+ }
+
+
+ // If attributeName has not been recognized throw an AttributeNotFoundException
+ throw(new AttributeNotFoundException("Cannot find " + attributeName +
+ " attribute in " + dClassName));
+
+ }
+
+
+ void addAppender(String appenderClass, String appenderName) {
+ cat.debug("addAppender called with "+appenderClass+", "+appenderName);
+ Appender appender = (Appender)
+ OptionConverter.instantiateByClassName(appenderClass,
+ org.apache.log4j.Appender.class,
+ null);
+ appender.setName(appenderName);
+ logger.addAppender(appender);
+
+ //appenderMBeanRegistration();
+
+ }
+
+
+ public
+ void setAttribute(Attribute attribute) throws AttributeNotFoundException,
+ InvalidAttributeValueException,
+ MBeanException,
+ ReflectionException {
+
+ // Check attribute is not null to avoid NullPointerException later on
+ if (attribute == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute cannot be null"),
+ "Cannot invoke a setter of " + dClassName +
+ " with null attribute");
+ }
+ String name = attribute.getName();
+ Object value = attribute.getValue();
+
+ if (name == null) {
+ throw new RuntimeOperationsException(
+ new IllegalArgumentException("Attribute name cannot be null"),
+ "Cannot invoke the setter of "+dClassName+
+ " with null attribute name");
+ }
+
+
+ if(name.equals("priority")) {
+ if (value instanceof String) {
+ String s = (String) value;
+ Level p = logger.getLevel();
+ if(s.equalsIgnoreCase("NULL")) {
+ p = null;
+ } else {
+ p = OptionConverter.toLevel(s, p);
+ }
+ logger.setLevel(p);
+ }
+ } else {
+ throw(new AttributeNotFoundException("Attribute " + name +
+ " not found in " +
+ this.getClass().getName()));
+ }
+ }
+
+ void appenderMBeanRegistration() {
+ Enumeration enumeration = logger.getAllAppenders();
+ while(enumeration.hasMoreElements()) {
+ Appender appender = (Appender) enumeration.nextElement();
+ registerAppenderMBean(appender);
+ }
+ }
+
+ void registerAppenderMBean(Appender appender) {
+ String name = getAppenderName(appender);
+ cat.debug("Adding AppenderMBean for appender named "+name);
+ ObjectName objectName = null;
+ try {
+ AppenderDynamicMBean appenderMBean = new AppenderDynamicMBean(appender);
+ objectName = new ObjectName("log4j", "appender", name);
+ if (!server.isRegistered(objectName)) {
+ registerMBean(appenderMBean, objectName);
+ dAttributes.add(new MBeanAttributeInfo("appender=" + name, "javax.management.ObjectName",
+ "The " + name + " appender.", true, true, false));
+ }
+
+ } catch(JMException e) {
+ cat.error("Could not add appenderMBean for ["+name+"].", e);
+ } catch(java.beans.IntrospectionException e) {
+ cat.error("Could not add appenderMBean for ["+name+"].", e);
+ } catch(RuntimeException e) {
+ cat.error("Could not add appenderMBean for ["+name+"].", e);
+ }
+ }
+
+ public
+ void postRegister(java.lang.Boolean registrationDone) {
+ appenderMBeanRegistration();
+ }
+}
diff --git a/java/src/org/apache/log4j/jmx/MethodUnion.java b/java/src/org/apache/log4j/jmx/MethodUnion.java
new file mode 100644
index 0000000..e9f2fb2
--- /dev/null
+++ b/java/src/org/apache/log4j/jmx/MethodUnion.java
@@ -0,0 +1,32 @@
+/*
+ * 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.log4j.jmx;
+
+import java.lang.reflect.Method;
+
+class MethodUnion {
+
+ Method readMethod;
+ Method writeMethod;
+
+ MethodUnion( Method readMethod, Method writeMethod) {
+ this.readMethod = readMethod;
+ this.writeMethod = writeMethod;
+ }
+
+}
diff --git a/java/src/org/apache/log4j/jmx/package.html b/java/src/org/apache/log4j/jmx/package.html
new file mode 100644
index 0000000..6d1583a
--- /dev/null
+++ b/java/src/org/apache/log4j/jmx/package.html
@@ -0,0 +1,24 @@
+
+
+
+
+ This package lets you manage log4j settings using JMX. It is
+ unfortunately not of production quality.
+
+
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/lf5/AppenderFinalizer.java b/java/src/org/apache/log4j/lf5/AppenderFinalizer.java
new file mode 100644
index 0000000..a2a7019
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/AppenderFinalizer.java
@@ -0,0 +1,78 @@
+/*
+ * 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.log4j.lf5;
+
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+
+/**
+ * AppenderFinalizer has a single method that will finalize
+ * resources associated with a LogBrokerMonitor in the event
+ * that the LF5Appender class is destroyed, and the class loader
+ * is garbage collected.
+ *
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class AppenderFinalizer {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ protected LogBrokerMonitor _defaultMonitor = null;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public AppenderFinalizer(LogBrokerMonitor defaultMonitor) {
+ _defaultMonitor = defaultMonitor;
+ }
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * @throws java.lang.Throwable
+ */
+ protected void finalize() throws Throwable {
+ System.out.println("Disposing of the default LogBrokerMonitor instance");
+ _defaultMonitor.dispose();
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/lf5/DefaultLF5Configurator.java b/java/src/org/apache/log4j/lf5/DefaultLF5Configurator.java
new file mode 100644
index 0000000..31d7f5b
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/DefaultLF5Configurator.java
@@ -0,0 +1,113 @@
+/*
+ * 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.log4j.lf5;
+
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.LoggerRepository;
+
+import java.io.IOException;
+import java.net.URL;
+
+/**
+ * The DefaultLF5Configurator provides a default
+ * configuration for the LF5Appender.
+ *
+ * Note: The preferred method for configuring a LF5Appender
+ * is to use the LF5Manager class. This class ensures
+ * that configuration does not occur multiple times, and improves system
+ * performance. Reconfiguring the monitor multiple times can result in
+ * unexpected behavior.
+ *
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class DefaultLF5Configurator implements Configurator {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ /**
+ * This class should never be instantiated! It implements the
+ * Configurator
+ * interface, but does not provide the same functionality as full
+ * configurator class.
+ */
+ private DefaultLF5Configurator() {
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+ /**
+ * This method configures the LF5Appender using a
+ * default configuration file. The default configuration file is
+ * defaultconfig.properties.
+ * @throws java.io.IOException
+ */
+ public static void configure() throws IOException {
+ String resource =
+ "/org/apache/log4j/lf5/config/defaultconfig.properties";
+ URL configFileResource =
+ DefaultLF5Configurator.class.getResource(resource);
+
+ if (configFileResource != null) {
+ PropertyConfigurator.configure(configFileResource);
+ } else {
+ throw new IOException("Error: Unable to open the resource" +
+ resource);
+ }
+
+ }
+
+ /**
+ * This is a dummy method that will throw an
+ * IllegalStateException if used.
+ */
+ public void doConfigure(URL configURL, LoggerRepository repository) {
+ throw new IllegalStateException("This class should NOT be" +
+ " instantiated!");
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/lf5/LF5Appender.java b/java/src/org/apache/log4j/lf5/LF5Appender.java
new file mode 100644
index 0000000..e98e9bd
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/LF5Appender.java
@@ -0,0 +1,266 @@
+/*
+ * 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.log4j.lf5;
+
+import java.awt.Toolkit;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * LF5Appender logs events to a swing based logging
+ * console. The swing console supports turning categories on and off,
+ * multiple detail level views, as well as full text searching and many
+ * other capabilties.
+ *
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LF5Appender extends AppenderSkeleton {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ protected LogBrokerMonitor _logMonitor;
+ protected static LogBrokerMonitor _defaultLogMonitor;
+ protected static AppenderFinalizer _finalizer;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Constructs a LF5Appender using the default instance of
+ * the LogBrokerMonitor. This constructor should always
+ * be preferred over the
+ * LF5Appender(LogBrokerMonitor monitor)
+ * constructor, unless you need to spawn additional log monitoring
+ * windows.
+ */
+ public LF5Appender() {
+ this(getDefaultInstance());
+ }
+
+ /**
+ * Constructs a LF5Appender using an instance of
+ * a LogBrokerMonitor supplied by the user. This
+ * constructor should only be used when you need to spawn
+ * additional log monitoring windows.
+ *
+ * @param monitor An instance of a LogBrokerMonitor
+ * created by the user.
+ */
+ public LF5Appender(LogBrokerMonitor monitor) {
+
+ if (monitor != null) {
+ _logMonitor = monitor;
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Appends a LoggingEvent record to the
+ * LF5Appender.
+ * @param event The LoggingEvent
+ * to be appended.
+ */
+ public void append(LoggingEvent event) {
+ // Retrieve the information from the log4j LoggingEvent.
+ String category = event.getLoggerName();
+ String logMessage = event.getRenderedMessage();
+ String nestedDiagnosticContext = event.getNDC();
+ String threadDescription = event.getThreadName();
+ String level = event.getLevel().toString();
+ long time = event.timeStamp;
+ LocationInfo locationInfo = event.getLocationInformation();
+
+ // Add the logging event information to a LogRecord
+ Log4JLogRecord record = new Log4JLogRecord();
+
+ record.setCategory(category);
+ record.setMessage(logMessage);
+ record.setLocation(locationInfo.fullInfo);
+ record.setMillis(time);
+ record.setThreadDescription(threadDescription);
+
+ if (nestedDiagnosticContext != null) {
+ record.setNDC(nestedDiagnosticContext);
+ } else {
+ record.setNDC("");
+ }
+
+ if (event.getThrowableInformation() != null) {
+ record.setThrownStackTrace(event.getThrowableInformation());
+ }
+
+ try {
+ record.setLevel(LogLevel.valueOf(level));
+ } catch (LogLevelFormatException e) {
+ // If the priority level doesn't match one of the predefined
+ // log levels, then set the level to warning.
+ record.setLevel(LogLevel.WARN);
+ }
+
+ if (_logMonitor != null) {
+ _logMonitor.addMessage(record);
+ }
+ }
+
+ /**
+ * This method is an empty implementation of the close() method inherited
+ * from the org.apache.log4j.Appender interface.
+ */
+ public void close() {
+ }
+
+ /**
+ * Returns a value that indicates whether this appender requires a
+ * Layout. This method always returns false.
+ * No layout is required for the LF5Appender.
+ */
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ /**
+ * This method is used to set the property that controls whether
+ * the LogBrokerMonitor is hidden or closed when a user
+ * exits
+ * the monitor. By default, the LogBrokerMonitor will hide
+ * itself when the log window is exited, and the swing thread will
+ * continue to run in the background. If this property is
+ * set to true, the LogBrokerMonitor will call System.exit(0)
+ * and will shut down swing thread and the virtual machine.
+ *
+ * @param callSystemExitOnClose A boolean value indicating whether
+ * to call System.exit(0) when closing the log window.
+ */
+ public void setCallSystemExitOnClose(boolean callSystemExitOnClose) {
+ _logMonitor.setCallSystemExitOnClose(callSystemExitOnClose);
+ }
+
+ /**
+ * The equals method compares two LF5Appenders and determines whether
+ * they are equal. Two Appenders will be considered equal
+ * if, and only if, they both contain references to the same
+ * LogBrokerMonitor.
+ *
+ * @param compareTo A boolean value indicating whether
+ * the two LF5Appenders are equal.
+ */
+ public boolean equals(LF5Appender compareTo) {
+ // If both reference the same LogBrokerMonitor, they are equal.
+ return _logMonitor == compareTo.getLogBrokerMonitor();
+ }
+
+ public LogBrokerMonitor getLogBrokerMonitor() {
+ return _logMonitor;
+ }
+
+ public static void main(String[] args) {
+ new LF5Appender();
+ }
+
+ public void setMaxNumberOfRecords(int maxNumberOfRecords) {
+ _defaultLogMonitor.setMaxNumberOfLogRecords(maxNumberOfRecords);
+ }
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * @return The default instance of the LogBrokerMonitor.
+ */
+ protected static synchronized LogBrokerMonitor getDefaultInstance() {
+ if (_defaultLogMonitor == null) {
+ try {
+ _defaultLogMonitor =
+ new LogBrokerMonitor(LogLevel.getLog4JLevels());
+ _finalizer = new AppenderFinalizer(_defaultLogMonitor);
+
+ _defaultLogMonitor.setFrameSize(getDefaultMonitorWidth(),
+ getDefaultMonitorHeight());
+ _defaultLogMonitor.setFontSize(12);
+ _defaultLogMonitor.show();
+
+ } catch (SecurityException e) {
+ _defaultLogMonitor = null;
+ }
+ }
+
+ return _defaultLogMonitor;
+ }
+
+ /**
+ * @return the screen width from Toolkit.getScreenSize()
+ * if possible, otherwise returns 800
+ * @see java.awt.Toolkit
+ */
+ protected static int getScreenWidth() {
+ try {
+ return Toolkit.getDefaultToolkit().getScreenSize().width;
+ } catch (Throwable t) {
+ return 800;
+ }
+ }
+
+ /**
+ * @return the screen height from Toolkit.getScreenSize()
+ * if possible, otherwise returns 600
+ * @see java.awt.Toolkit
+ */
+ protected static int getScreenHeight() {
+ try {
+ return Toolkit.getDefaultToolkit().getScreenSize().height;
+ } catch (Throwable t) {
+ return 600;
+ }
+ }
+
+ protected static int getDefaultMonitorWidth() {
+ return (3 * getScreenWidth()) / 4;
+ }
+
+ protected static int getDefaultMonitorHeight() {
+ return (3 * getScreenHeight()) / 4;
+ }
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
diff --git a/java/src/org/apache/log4j/lf5/Log4JLogRecord.java b/java/src/org/apache/log4j/lf5/Log4JLogRecord.java
new file mode 100644
index 0000000..4393eb5
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/Log4JLogRecord.java
@@ -0,0 +1,116 @@
+/*
+ * 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.log4j.lf5;
+
+import org.apache.log4j.spi.ThrowableInformation;
+
+/**
+ * A Log4JLogRecord encapsulates
+ * the details of your log4j LoggingEvent in a format usable
+ * by the LogBrokerMonitor.
+ *
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class Log4JLogRecord extends LogRecord {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Constructs an instance of a Log4JLogRecord.
+ */
+ public Log4JLogRecord() {
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+ /**
+ * Determines which Priority levels will
+ * be displayed in colored font when the LogMonitorAppender
+ * renders this log message. By default, messages will be colored
+ * red if they are of Priority ERROR or FATAL.
+ *
+ * @return true if the log level is ERROR or FATAL.
+ */
+ public boolean isSevereLevel() {
+ boolean isSevere = false;
+
+ if (LogLevel.ERROR.equals(getLevel()) ||
+ LogLevel.FATAL.equals(getLevel())) {
+ isSevere = true;
+ }
+
+ return isSevere;
+ }
+
+ /**
+ * Set stack trace information associated with this Log4JLogRecord.
+ * When this method is called, the stack trace in a
+ * String-based format is made
+ * available via the getThrownStackTrace() method.
+ *
+ * @param throwableInfo An org.apache.log4j.spi.ThrowableInformation to
+ * associate with this Log4JLogRecord.
+ * @see #getThrownStackTrace()
+ */
+ public void setThrownStackTrace(ThrowableInformation throwableInfo) {
+ String[] stackTraceArray = throwableInfo.getThrowableStrRep();
+
+ StringBuffer stackTrace = new StringBuffer();
+ String nextLine;
+
+ for (int i = 0; i < stackTraceArray.length; i++) {
+ nextLine = stackTraceArray[i] + "\n";
+ stackTrace.append(nextLine);
+ }
+
+ _thrownStackTrace = stackTrace.toString();
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/LogLevel.java b/java/src/org/apache/log4j/lf5/LogLevel.java
new file mode 100644
index 0000000..080ed68
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/LogLevel.java
@@ -0,0 +1,278 @@
+/*
+ * 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.log4j.lf5;
+
+import java.awt.Color;
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * The LogLevel class defines a set of standard logging levels.
+ *
+ * The logging Level objects are ordered and are specified by ordered
+ * integers. Enabling logging at a given level also enables logging at all
+ * higher levels.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brent Sprecher
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogLevel implements java.io.Serializable {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ // log4j log levels.
+ public final static LogLevel FATAL = new LogLevel("FATAL", 0);
+ public final static LogLevel ERROR = new LogLevel("ERROR", 1);
+ public final static LogLevel WARN = new LogLevel("WARN", 2);
+ public final static LogLevel INFO = new LogLevel("INFO", 3);
+ public final static LogLevel DEBUG = new LogLevel("DEBUG", 4);
+
+ // jdk1.4 log levels NOTE: also includes INFO
+ public final static LogLevel SEVERE = new LogLevel("SEVERE", 1);
+ public final static LogLevel WARNING = new LogLevel("WARNING", 2);
+ public final static LogLevel CONFIG = new LogLevel("CONFIG", 4);
+ public final static LogLevel FINE = new LogLevel("FINE", 5);
+ public final static LogLevel FINER = new LogLevel("FINER", 6);
+ public final static LogLevel FINEST = new LogLevel("FINEST", 7);
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected String _label;
+ protected int _precedence;
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+ private static LogLevel[] _log4JLevels;
+ private static LogLevel[] _jdk14Levels;
+ private static LogLevel[] _allDefaultLevels;
+ private static Map _logLevelMap;
+ private static Map _logLevelColorMap;
+ private static Map _registeredLogLevelMap = new HashMap();
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ static {
+ _log4JLevels = new LogLevel[]{FATAL, ERROR, WARN, INFO, DEBUG};
+ _jdk14Levels = new LogLevel[]{SEVERE, WARNING, INFO,
+ CONFIG, FINE, FINER, FINEST};
+ _allDefaultLevels = new LogLevel[]{FATAL, ERROR, WARN, INFO, DEBUG,
+ SEVERE, WARNING, CONFIG, FINE, FINER, FINEST};
+
+ _logLevelMap = new HashMap();
+ for (int i = 0; i < _allDefaultLevels.length; i++) {
+ _logLevelMap.put(_allDefaultLevels[i].getLabel(), _allDefaultLevels[i]);
+ }
+
+ // prepopulate map with levels and text color of black
+ _logLevelColorMap = new HashMap();
+ for (int i = 0; i < _allDefaultLevels.length; i++) {
+ _logLevelColorMap.put(_allDefaultLevels[i], Color.black);
+ }
+ }
+
+ public LogLevel(String label, int precedence) {
+ _label = label;
+ _precedence = precedence;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Return the Label of the LogLevel.
+ */
+ public String getLabel() {
+ return _label;
+ }
+
+ /**
+ * Returns true if the level supplied is encompassed by this level.
+ * For example, LogLevel.SEVERE encompasses no other LogLevels and
+ * LogLevel.FINE encompasses all other LogLevels. By definition,
+ * a LogLevel encompasses itself.
+ */
+ public boolean encompasses(LogLevel level) {
+ if (level.getPrecedence() <= getPrecedence()) {
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Convert a log level label into a LogLevel object.
+ *
+ * @param level The label of a level to be converted into a LogLevel.
+ * @return LogLevel The LogLevel with a label equal to level.
+ * @throws LogLevelFormatException Is thrown when the level can not be
+ * converted into a LogLevel.
+ */
+ public static LogLevel valueOf(String level)
+ throws LogLevelFormatException {
+ LogLevel logLevel = null;
+ if (level != null) {
+ level = level.trim().toUpperCase();
+ logLevel = (LogLevel) _logLevelMap.get(level);
+ }
+
+ // Didn't match, Check for registered LogLevels
+ if (logLevel == null && _registeredLogLevelMap.size() > 0) {
+ logLevel = (LogLevel) _registeredLogLevelMap.get(level);
+ }
+
+ if (logLevel == null) {
+ StringBuffer buf = new StringBuffer();
+ buf.append("Error while trying to parse (" + level + ") into");
+ buf.append(" a LogLevel.");
+ throw new LogLevelFormatException(buf.toString());
+ }
+ return logLevel;
+ }
+
+ /**
+ * Registers a used defined LogLevel.
+ *
+ * @param logLevel The log level to be registered. Cannot be a default LogLevel
+ * @return LogLevel The replaced log level.
+ */
+ public static LogLevel register(LogLevel logLevel) {
+ if (logLevel == null) return null;
+
+ // ensure that this is not a default log level
+ if (_logLevelMap.get(logLevel.getLabel()) == null) {
+ return (LogLevel) _registeredLogLevelMap.put(logLevel.getLabel(), logLevel);
+ }
+
+ return null;
+ }
+
+ public static void register(LogLevel[] logLevels) {
+ if (logLevels != null) {
+ for (int i = 0; i < logLevels.length; i++) {
+ register(logLevels[i]);
+ }
+ }
+ }
+
+ public static void register(List logLevels) {
+ if (logLevels != null) {
+ Iterator it = logLevels.iterator();
+ while (it.hasNext()) {
+ register((LogLevel) it.next());
+ }
+ }
+ }
+
+ public boolean equals(Object o) {
+ boolean equals = false;
+
+ if (o instanceof LogLevel) {
+ if (this.getPrecedence() ==
+ ((LogLevel) o).getPrecedence()) {
+ equals = true;
+ }
+
+ }
+
+ return equals;
+ }
+
+ public int hashCode() {
+ return _label.hashCode();
+ }
+
+ public String toString() {
+ return _label;
+ }
+
+ // set a text color for a specific log level
+ public void setLogLevelColorMap(LogLevel level, Color color) {
+ // remove the old entry
+ _logLevelColorMap.remove(level);
+ // add the new color entry
+ if (color == null) {
+ color = Color.black;
+ }
+ _logLevelColorMap.put(level, color);
+ }
+
+ public static void resetLogLevelColorMap() {
+ // empty the map
+ _logLevelColorMap.clear();
+
+ // repopulate map and reset text color black
+ for (int i = 0; i < _allDefaultLevels.length; i++) {
+ _logLevelColorMap.put(_allDefaultLevels[i], Color.black);
+ }
+ }
+
+ /**
+ * @return A List of LogLevel objects that map
+ * to log4j Priority objects.
+ */
+ public static List getLog4JLevels() {
+ return Arrays.asList(_log4JLevels);
+ }
+
+ public static List getJdk14Levels() {
+ return Arrays.asList(_jdk14Levels);
+ }
+
+ public static List getAllDefaultLevels() {
+ return Arrays.asList(_allDefaultLevels);
+ }
+
+ public static Map getLogLevelColorMap() {
+ return _logLevelColorMap;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected int getPrecedence() {
+ return _precedence;
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/LogLevelFormatException.java b/java/src/org/apache/log4j/lf5/LogLevelFormatException.java
new file mode 100644
index 0000000..1109e23
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/LogLevelFormatException.java
@@ -0,0 +1,73 @@
+/*
+ * 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.log4j.lf5;
+
+/**
+ * Thrown to indicate that the client has attempted to convert a string
+ * to one the LogLevel types, but the string does not have the appropriate
+ * format.
+ *
+ * @author Michael J. Sikorsky<
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogLevelFormatException extends Exception {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public LogLevelFormatException(String message) {
+ super(message);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/LogRecord.java b/java/src/org/apache/log4j/lf5/LogRecord.java
new file mode 100644
index 0000000..4f4097f
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/LogRecord.java
@@ -0,0 +1,395 @@
+/*
+ * 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.log4j.lf5;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * LogRecord. A LogRecord encapsulates the details of your desired log
+ * request.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public abstract class LogRecord implements java.io.Serializable {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected static long _seqCount = 0;
+
+ protected LogLevel _level;
+ protected String _message;
+ protected long _sequenceNumber;
+ protected long _millis;
+ protected String _category;
+ protected String _thread;
+ protected String _thrownStackTrace;
+ protected Throwable _thrown;
+ protected String _ndc;
+ protected String _location;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public LogRecord() {
+ super();
+
+ _millis = System.currentTimeMillis();
+ _category = "Debug";
+ _message = "";
+ _level = LogLevel.INFO;
+ _sequenceNumber = getNextId();
+ _thread = Thread.currentThread().toString();
+ _ndc = "";
+ _location = "";
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Get the level of this LogRecord.
+ *
+ * @return The LogLevel of this record.
+ * @see #setLevel(LogLevel)
+ * @see LogLevel
+ */
+ public LogLevel getLevel() {
+ return (_level);
+ }
+
+ /**
+ * Set the level of this LogRecord.
+ *
+ * @param level The LogLevel for this record.
+ * @see #getLevel()
+ * @see LogLevel
+ */
+ public void setLevel(LogLevel level) {
+ _level = level;
+ }
+
+ /**
+ * Abstract method. Must be overridden to indicate what log level
+ * to show in red.
+ */
+ public abstract boolean isSevereLevel();
+
+ /**
+ * @return true if getThrown().toString() is a non-empty string.
+ */
+ public boolean hasThrown() {
+ Throwable thrown = getThrown();
+ if (thrown == null) {
+ return false;
+ }
+ String thrownString = thrown.toString();
+ return thrownString != null && thrownString.trim().length() != 0;
+ }
+
+ /**
+ * @return true if isSevereLevel() or hasThrown() returns true.
+ */
+ public boolean isFatal() {
+ return isSevereLevel() || hasThrown();
+ }
+
+ /**
+ * Get the category asscociated with this LogRecord. For a more detailed
+ * description of what a category is see setCategory().
+ *
+ * @return The category of this record.
+ * @see #setCategory(String)
+ */
+ public String getCategory() {
+ return (_category);
+ }
+
+ /**
+ * Set the category associated with this LogRecord. A category represents
+ * a hierarchical dot (".") separated namespace for messages.
+ * The definition of a category is application specific, but a common convention
+ * is as follows:
+ *
+ *
+ * When logging messages
+ * for a particluar class you can use its class name:
+ * com.thoughtworks.framework.servlet.ServletServiceBroker.
+ * Futhermore, to log a message for a particular method in a class
+ * add the method name:
+ * com.thoughtworks.framework.servlet.ServletServiceBroker.init().
+ *
+ *
+ * @param category The category for this record.
+ * @see #getCategory()
+ */
+ public void setCategory(String category) {
+ _category = category;
+ }
+
+ /**
+ * Get the message asscociated with this LogRecord.
+ *
+ * @return The message of this record.
+ * @see #setMessage(String)
+ */
+ public String getMessage() {
+ return (_message);
+ }
+
+ /**
+ * Set the message associated with this LogRecord.
+ *
+ * @param message The message for this record.
+ * @see #getMessage()
+ */
+ public void setMessage(String message) {
+ _message = message;
+ }
+
+ /**
+ * Get the sequence number associated with this LogRecord. Sequence numbers
+ * are generally assigned when a LogRecord is constructed. Sequence numbers
+ * start at 0 and increase with each newly constructed LogRocord.
+ *
+ * @return The sequence number of this record.
+ * @see #setSequenceNumber(long)
+ */
+ public long getSequenceNumber() {
+ return (_sequenceNumber);
+ }
+
+ /**
+ * Set the sequence number assocsiated with this LogRecord. A sequence number
+ * will automatically be assigned to evey newly constructed LogRecord, however,
+ * this method can override the value.
+ *
+ * @param number The sequence number.
+ * @see #getSequenceNumber()
+ */
+ public void setSequenceNumber(long number) {
+ _sequenceNumber = number;
+ }
+
+ /**
+ * Get the event time of this record in milliseconds from 1970.
+ * When a LogRecord is constructed the event time is set but may be
+ * overridden by calling setMillis();
+ *
+ * @return The event time of this record in milliseconds from 1970.
+ * @see #setMillis(long)
+ */
+ public long getMillis() {
+ return _millis;
+ }
+
+ /**
+ * Set the event time of this record. When a LogRecord is constructed
+ * the event time is set but may be overridden by calling this method.
+ *
+ * @param millis The time in milliseconds from 1970.
+ * @see #getMillis()
+ */
+ public void setMillis(long millis) {
+ _millis = millis;
+ }
+
+ /**
+ * Get the thread description asscociated with this LogRecord. When a
+ * LogRecord is constructed, the thread description is set by calling:
+ * Thread.currentThread().toString(). You may supply a thread description
+ * of your own by calling the setThreadDescription(String) method.
+ *
+ * @return The thread description of this record.
+ * @see #setThreadDescription(String)
+ */
+ public String getThreadDescription() {
+ return (_thread);
+ }
+
+ /**
+ * Set the thread description associated with this LogRecord. When a
+ * LogRecord is constructed, the thread description is set by calling:
+ * Thread.currentThread().toString(). You may supply a thread description
+ * of your own by calling this method.
+ *
+ * @param threadDescription The description of the thread for this record.
+ * @see #getThreadDescription()
+ */
+ public void setThreadDescription(String threadDescription) {
+ _thread = threadDescription;
+ }
+
+ /**
+ * Get the stack trace in a String-based format for the associated Throwable
+ * of this LogRecord. The stack trace in a String-based format is set
+ * when the setThrown(Throwable) method is called.
+ *
+ *
+ * Why do we need this method considering that we
+ * have the getThrown() and setThrown() methods?
+ * A Throwable object may not be serializable, however, a String representation
+ * of it is. Users of LogRecords should generally call this method over
+ * getThrown() for the reasons of serialization.
+ *
+ *
+ * @return The Stack Trace for the asscoiated Throwable of this LogRecord.
+ * @see #setThrown(Throwable)
+ * @see #getThrown()
+ */
+ public String getThrownStackTrace() {
+ return (_thrownStackTrace);
+ }
+
+ /**
+ * Set the ThrownStackTrace for the log record.
+ *
+ * @param trace A String to associate with this LogRecord
+ * @see #getThrownStackTrace()
+ */
+ public void setThrownStackTrace(String trace) {
+ _thrownStackTrace = trace;
+ }
+
+ /**
+ * Get the Throwable associated with this LogRecord.
+ *
+ * @return The LogLevel of this record.
+ * @see #setThrown(Throwable)
+ * @see #getThrownStackTrace()
+ */
+ public Throwable getThrown() {
+ return (_thrown);
+ }
+
+ /**
+ * Set the Throwable associated with this LogRecord. When this method
+ * is called, the stack trace in a String-based format is made
+ * available via the getThrownStackTrace() method.
+ *
+ * @param thrown A Throwable to associate with this LogRecord.
+ * @see #getThrown()
+ * @see #getThrownStackTrace()
+ */
+ public void setThrown(Throwable thrown) {
+ if (thrown == null) {
+ return;
+ }
+ _thrown = thrown;
+ StringWriter sw = new StringWriter();
+ PrintWriter out = new PrintWriter(sw);
+ thrown.printStackTrace(out);
+ out.flush();
+ _thrownStackTrace = sw.toString();
+ try {
+ out.close();
+ sw.close();
+ } catch (IOException e) {
+ // Do nothing, this should not happen as it is StringWriter.
+ }
+ out = null;
+ sw = null;
+ }
+
+ /**
+ * Return a String representation of this LogRecord.
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer();
+ buf.append("LogRecord: [" + _level + ", " + _message + "]");
+ return (buf.toString());
+ }
+
+ /**
+ * Get the NDC (nested diagnostic context) for this record.
+ *
+ * @return The string representing the NDC.
+ */
+ public String getNDC() {
+ return _ndc;
+ }
+
+ /**
+ * Set the NDC (nested diagnostic context) for this record.
+ *
+ * @param ndc A string representing the NDC.
+ */
+ public void setNDC(String ndc) {
+ _ndc = ndc;
+ }
+
+ /**
+ * Get the location in code where this LogRecord originated.
+ *
+ * @return The string containing the location information.
+ */
+ public String getLocation() {
+ return _location;
+ }
+
+ /**
+ * Set the location in code where this LogRecord originated.
+ *
+ * @param location A string containing location information.
+ */
+ public void setLocation(String location) {
+ _location = location;
+ }
+
+ /**
+ * Resets that sequence number to 0.
+ *
+ */
+ public static synchronized void resetSequenceNumber() {
+ _seqCount = 0;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected static synchronized long getNextId() {
+ _seqCount++;
+ return _seqCount;
+ }
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/LogRecordFilter.java b/java/src/org/apache/log4j/lf5/LogRecordFilter.java
new file mode 100644
index 0000000..25a3e53
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/LogRecordFilter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.log4j.lf5;
+
+
+/**
+ * An interface for classes which filters LogRecords. Implementations
+ * represent a rule or condition which LogRecords may pass or fail.
+ * @see LogRecord
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public interface LogRecordFilter {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * @return true if the specified LogRecord satisfies whatever condition
+ * implementing class tests for.
+ */
+ public boolean passes(LogRecord record);
+
+}
+
diff --git a/java/src/org/apache/log4j/lf5/PassingLogRecordFilter.java b/java/src/org/apache/log4j/lf5/PassingLogRecordFilter.java
new file mode 100644
index 0000000..178f6cb
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/PassingLogRecordFilter.java
@@ -0,0 +1,74 @@
+/*
+ * 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.log4j.lf5;
+
+
+/**
+ * An implementation of LogRecordFilter which always returns true.
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class PassingLogRecordFilter implements LogRecordFilter {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * @return true;
+ */
+ public boolean passes(LogRecord record) {
+ return true;
+ }
+
+ /**
+ * Does nothing.
+ */
+ public void reset() {
+ // do nothing
+ }
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
+
diff --git a/java/src/org/apache/log4j/lf5/StartLogFactor5.java b/java/src/org/apache/log4j/lf5/StartLogFactor5.java
new file mode 100644
index 0000000..1ea28c3
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/StartLogFactor5.java
@@ -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.
+ */
+package org.apache.log4j.lf5;
+
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+
+/**
+ * Starts an instance of the LogFactor5 console for off-line viewing.
+ *
+ * @author Brad Marlborough
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class StartLogFactor5 {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Main - starts a an instance of the LogFactor5 console and configures
+ * the console settings.
+ */
+ public final static void main(String[] args) {
+
+ LogBrokerMonitor monitor = new LogBrokerMonitor(
+ LogLevel.getLog4JLevels());
+
+ monitor.setFrameSize(LF5Appender.getDefaultMonitorWidth(),
+ LF5Appender.getDefaultMonitorHeight());
+ monitor.setFontSize(12);
+ monitor.show();
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+
+}
+
+
diff --git a/java/src/org/apache/log4j/lf5/util/AdapterLogRecord.java b/java/src/org/apache/log4j/lf5/util/AdapterLogRecord.java
new file mode 100644
index 0000000..2e4ee6f
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/util/AdapterLogRecord.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.log4j.lf5.util;
+
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogRecord;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+
+/**
+ * A LogRecord to be used with the LogMonitorAdapter
+ *
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class AdapterLogRecord extends LogRecord {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+ private static LogLevel severeLevel = null;
+
+ private static StringWriter sw = new StringWriter();
+ private static PrintWriter pw = new PrintWriter(sw);
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ public AdapterLogRecord() {
+ super();
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+ public void setCategory(String category) {
+ super.setCategory(category);
+ super.setLocation(getLocationInfo(category));
+ }
+
+ public boolean isSevereLevel() {
+ if (severeLevel == null) return false;
+ return severeLevel.equals(getLevel());
+ }
+
+ public static void setSevereLevel(LogLevel level) {
+ severeLevel = level;
+ }
+
+ public static LogLevel getSevereLevel() {
+ return severeLevel;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+ protected String getLocationInfo(String category) {
+ String stackTrace = stackTraceToString(new Throwable());
+ String line = parseLine(stackTrace, category);
+ return line;
+ }
+
+ protected String stackTraceToString(Throwable t) {
+ String s = null;
+
+ synchronized (sw) {
+ t.printStackTrace(pw);
+ s = sw.toString();
+ sw.getBuffer().setLength(0);
+ }
+
+ return s;
+ }
+
+ protected String parseLine(String trace, String category) {
+ int index = trace.indexOf(category);
+ if (index == -1) return null;
+ trace = trace.substring(index);
+ trace = trace.substring(0, trace.indexOf(")") + 1);
+ return trace;
+ }
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
+
diff --git a/java/src/org/apache/log4j/lf5/util/DateFormatManager.java b/java/src/org/apache/log4j/lf5/util/DateFormatManager.java
new file mode 100644
index 0000000..6bed9cc
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/util/DateFormatManager.java
@@ -0,0 +1,243 @@
+/*
+ * 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.log4j.lf5.util;
+
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.util.TimeZone;
+
+/**
+ * Date format manager.
+ * Utility class to help manage consistent date formatting and parsing.
+ * It may be advantageous to have multiple DateFormatManagers per
+ * application. For example, one for handling the output (formatting) of
+ * dates, and another one for handling the input (parsing) of dates.
+ *
+ * @author Robert Shaw
+ * @author Michael J. Sikorsky
+ */
+
+// Contributed by ThoughtWorks Inc.
+public class DateFormatManager {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+ private TimeZone _timeZone = null;
+ private Locale _locale = null;
+
+ private String _pattern = null;
+ private DateFormat _dateFormat = null;
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ public DateFormatManager() {
+ super();
+ configure();
+ }
+
+ public DateFormatManager(TimeZone timeZone) {
+ super();
+
+ _timeZone = timeZone;
+ configure();
+ }
+
+ public DateFormatManager(Locale locale) {
+ super();
+
+ _locale = locale;
+ configure();
+ }
+
+ public DateFormatManager(String pattern) {
+ super();
+
+ _pattern = pattern;
+ configure();
+ }
+
+ public DateFormatManager(TimeZone timeZone, Locale locale) {
+ super();
+
+ _timeZone = timeZone;
+ _locale = locale;
+ configure();
+ }
+
+ public DateFormatManager(TimeZone timeZone, String pattern) {
+ super();
+
+ _timeZone = timeZone;
+ _pattern = pattern;
+ configure();
+ }
+
+ public DateFormatManager(Locale locale, String pattern) {
+ super();
+
+ _locale = locale;
+ _pattern = pattern;
+ configure();
+ }
+
+ public DateFormatManager(TimeZone timeZone, Locale locale, String pattern) {
+ super();
+
+ _timeZone = timeZone;
+ _locale = locale;
+ _pattern = pattern;
+ configure();
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public synchronized TimeZone getTimeZone() {
+ if (_timeZone == null) {
+ return TimeZone.getDefault();
+ } else {
+ return _timeZone;
+ }
+ }
+
+ public synchronized void setTimeZone(TimeZone timeZone) {
+ _timeZone = timeZone;
+ configure();
+ }
+
+ public synchronized Locale getLocale() {
+ if (_locale == null) {
+ return Locale.getDefault();
+ } else {
+ return _locale;
+ }
+ }
+
+ public synchronized void setLocale(Locale locale) {
+ _locale = locale;
+ configure();
+ }
+
+ public synchronized String getPattern() {
+ return _pattern;
+ }
+
+ /**
+ * Set the pattern. i.e. "EEEEE, MMMMM d, yyyy hh:mm aaa"
+ */
+ public synchronized void setPattern(String pattern) {
+ _pattern = pattern;
+ configure();
+ }
+
+
+ /**
+ * This method has been deprecated in favour of getPattern().
+ * @deprecated Use getPattern().
+ */
+ public synchronized String getOutputFormat() {
+ return _pattern;
+ }
+
+ /**
+ * This method has been deprecated in favour of setPattern().
+ * @deprecated Use setPattern().
+ */
+ public synchronized void setOutputFormat(String pattern) {
+ _pattern = pattern;
+ configure();
+ }
+
+ public synchronized DateFormat getDateFormatInstance() {
+ return _dateFormat;
+ }
+
+ public synchronized void setDateFormatInstance(DateFormat dateFormat) {
+ _dateFormat = dateFormat;
+ // No reconfiguration necessary!
+ }
+
+ public String format(Date date) {
+ return getDateFormatInstance().format(date);
+ }
+
+ public String format(Date date, String pattern) {
+ DateFormat formatter = null;
+ formatter = getDateFormatInstance();
+ if (formatter instanceof SimpleDateFormat) {
+ formatter = (SimpleDateFormat) (formatter.clone());
+ ((SimpleDateFormat) formatter).applyPattern(pattern);
+ }
+ return formatter.format(date);
+ }
+
+ /**
+ * @throws java.text.ParseException
+ */
+ public Date parse(String date) throws ParseException {
+ return getDateFormatInstance().parse(date);
+ }
+
+ /**
+ * @throws java.text.ParseException
+ */
+ public Date parse(String date, String pattern) throws ParseException {
+ DateFormat formatter = null;
+ formatter = getDateFormatInstance();
+ if (formatter instanceof SimpleDateFormat) {
+ formatter = (SimpleDateFormat) (formatter.clone());
+ ((SimpleDateFormat) formatter).applyPattern(pattern);
+ }
+ return formatter.parse(date);
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+ private synchronized void configure() {
+ _dateFormat = SimpleDateFormat.getDateTimeInstance(DateFormat.FULL,
+ DateFormat.FULL,
+ getLocale());
+ _dateFormat.setTimeZone(getTimeZone());
+
+ if (_pattern != null) {
+ ((SimpleDateFormat) _dateFormat).applyPattern(_pattern);
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
diff --git a/java/src/org/apache/log4j/lf5/util/LogFileParser.java b/java/src/org/apache/log4j/lf5/util/LogFileParser.java
new file mode 100644
index 0000000..656d4b6
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/util/LogFileParser.java
@@ -0,0 +1,301 @@
+/*
+ * 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.log4j.lf5.util;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+
+import javax.swing.SwingUtilities;
+
+import org.apache.log4j.lf5.Log4JLogRecord;
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogLevelFormatException;
+import org.apache.log4j.lf5.LogRecord;
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+import org.apache.log4j.lf5.viewer.LogFactor5ErrorDialog;
+import org.apache.log4j.lf5.viewer.LogFactor5LoadingDialog;
+
+/**
+ * Provides utility methods for input and output streams.
+ *
+ * @author Brad Marlborough
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogFileParser implements Runnable {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+ public static final String RECORD_DELIMITER = "[slf5s.start]";
+ public static final String ATTRIBUTE_DELIMITER = "[slf5s.";
+ public static final String DATE_DELIMITER = ATTRIBUTE_DELIMITER + "DATE]";
+ public static final String THREAD_DELIMITER = ATTRIBUTE_DELIMITER + "THREAD]";
+ public static final String CATEGORY_DELIMITER = ATTRIBUTE_DELIMITER + "CATEGORY]";
+ public static final String LOCATION_DELIMITER = ATTRIBUTE_DELIMITER + "LOCATION]";
+ public static final String MESSAGE_DELIMITER = ATTRIBUTE_DELIMITER + "MESSAGE]";
+ public static final String PRIORITY_DELIMITER = ATTRIBUTE_DELIMITER + "PRIORITY]";
+ public static final String NDC_DELIMITER = ATTRIBUTE_DELIMITER + "NDC]";
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+ private static SimpleDateFormat _sdf = new SimpleDateFormat("dd MMM yyyy HH:mm:ss,S");
+ private LogBrokerMonitor _monitor;
+ LogFactor5LoadingDialog _loadDialog;
+ private InputStream _in = null;
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ public LogFileParser(File file) throws IOException,
+ FileNotFoundException {
+ this(new FileInputStream(file));
+ }
+
+ public LogFileParser(InputStream stream) throws IOException {
+ _in = stream;
+ }
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Starts a new thread to parse the log file and create a LogRecord.
+ * See run().
+ * @param monitor LogBrokerMonitor
+ */
+ public void parse(LogBrokerMonitor monitor) throws RuntimeException {
+ _monitor = monitor;
+ Thread t = new Thread(this);
+ t.start();
+ }
+
+ /**
+ * Parses the file and creates new log records and adds the record
+ * to the monitor.
+ */
+ public void run() {
+
+ int index = 0;
+ int counter = 0;
+ LogRecord temp;
+ boolean isLogFile = false;
+
+ _loadDialog = new LogFactor5LoadingDialog(
+ _monitor.getBaseFrame(), "Loading file...");
+
+
+ try {
+ String logRecords = loadLogFile(_in);
+
+ while ((counter = logRecords.indexOf(RECORD_DELIMITER, index)) != -1) {
+ temp = createLogRecord(logRecords.substring(index, counter));
+ isLogFile = true;
+
+ if (temp != null) {
+ _monitor.addMessage(temp);
+ }
+
+ index = counter + RECORD_DELIMITER.length();
+ }
+
+ if (index < logRecords.length() && isLogFile) {
+ temp = createLogRecord(logRecords.substring(index));
+
+ if (temp != null) {
+ _monitor.addMessage(temp);
+ }
+ }
+
+ if (isLogFile == false) {
+ throw new RuntimeException("Invalid log file format");
+ }
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ destroyDialog();
+ }
+ });
+
+ } catch (RuntimeException e) {
+ destroyDialog();
+ displayError("Error - Invalid log file format.\nPlease see documentation"
+ + " on how to load log files.");
+ } catch (IOException e) {
+ destroyDialog();
+ displayError("Error - Unable to load log file!");
+ }
+
+ _in = null;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+ protected void displayError(String message) {
+ LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+ _monitor.getBaseFrame(), message);
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+ private void destroyDialog() {
+ _loadDialog.hide();
+ _loadDialog.dispose();
+ }
+
+ /**
+ * Loads a log file from a web server into the LogFactor5 GUI.
+ */
+ private String loadLogFile(InputStream stream) throws IOException {
+ BufferedInputStream br = new BufferedInputStream(stream);
+
+ int count = 0;
+ int size = br.available();
+
+ StringBuffer sb = null;
+ if (size > 0) {
+ sb = new StringBuffer(size);
+ } else {
+ sb = new StringBuffer(1024);
+ }
+
+ while ((count = br.read()) != -1) {
+ sb.append((char) count);
+ }
+
+ br.close();
+ br = null;
+ return sb.toString();
+
+ }
+
+ private String parseAttribute(String name, String record) {
+
+ int index = record.indexOf(name);
+
+ if (index == -1) {
+ return null;
+ }
+
+ return getAttribute(index, record);
+ }
+
+ private long parseDate(String record) {
+ try {
+ String s = parseAttribute(DATE_DELIMITER, record);
+
+ if (s == null) {
+ return 0;
+ }
+
+ Date d = _sdf.parse(s);
+
+ return d.getTime();
+ } catch (ParseException e) {
+ return 0;
+ }
+ }
+
+ private LogLevel parsePriority(String record) {
+ String temp = parseAttribute(PRIORITY_DELIMITER, record);
+
+ if (temp != null) {
+ try {
+ return LogLevel.valueOf(temp);
+ } catch (LogLevelFormatException e) {
+ return LogLevel.DEBUG;
+ }
+
+ }
+
+ return LogLevel.DEBUG;
+ }
+
+ private String parseThread(String record) {
+ return parseAttribute(THREAD_DELIMITER, record);
+ }
+
+ private String parseCategory(String record) {
+ return parseAttribute(CATEGORY_DELIMITER, record);
+ }
+
+ private String parseLocation(String record) {
+ return parseAttribute(LOCATION_DELIMITER, record);
+ }
+
+ private String parseMessage(String record) {
+ return parseAttribute(MESSAGE_DELIMITER, record);
+ }
+
+ private String parseNDC(String record) {
+ return parseAttribute(NDC_DELIMITER, record);
+ }
+
+ private String parseThrowable(String record) {
+ return getAttribute(record.length(), record);
+ }
+
+ private LogRecord createLogRecord(String record) {
+ if (record == null || record.trim().length() == 0) {
+ return null;
+ }
+
+ LogRecord lr = new Log4JLogRecord();
+ lr.setMillis(parseDate(record));
+ lr.setLevel(parsePriority(record));
+ lr.setCategory(parseCategory(record));
+ lr.setLocation(parseLocation(record));
+ lr.setThreadDescription(parseThread(record));
+ lr.setNDC(parseNDC(record));
+ lr.setMessage(parseMessage(record));
+ lr.setThrownStackTrace(parseThrowable(record));
+
+ return lr;
+ }
+
+
+ private String getAttribute(int index, String record) {
+ int start = record.lastIndexOf(ATTRIBUTE_DELIMITER, index - 1);
+
+ if (start == -1) {
+ return record.substring(0, index);
+ }
+
+ start = record.indexOf("]", start);
+
+ return record.substring(start + 1, index).trim();
+ }
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+
+}
diff --git a/java/src/org/apache/log4j/lf5/util/LogMonitorAdapter.java b/java/src/org/apache/log4j/lf5/util/LogMonitorAdapter.java
new file mode 100644
index 0000000..6ba927c
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/util/LogMonitorAdapter.java
@@ -0,0 +1,289 @@
+/*
+ * 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.log4j.lf5.util;
+
+import java.awt.Toolkit;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogRecord;
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+
+/**
+ * LogMonitorAdapter facilitates the usage of the LogMonitor
+ *
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogMonitorAdapter {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+ public static final int LOG4J_LOG_LEVELS = 0;
+ public static final int JDK14_LOG_LEVELS = 1;
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+ private LogBrokerMonitor _logMonitor;
+ private LogLevel _defaultLevel = null;
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ private LogMonitorAdapter(List userDefinedLevels) {
+ super();
+ // set the default level to be the first entry in the list
+ _defaultLevel = (LogLevel) userDefinedLevels.get(0);
+ _logMonitor = new LogBrokerMonitor(userDefinedLevels);
+
+ _logMonitor.setFrameSize(getDefaultMonitorWidth(),
+ getDefaultMonitorHeight());
+ _logMonitor.setFontSize(12);
+ _logMonitor.show();
+ }
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+ /**
+ * Creates an instance of LogMonitorAdapter using the
+ * log levels inticated by the parameter. Log4J and JDK1.4 both have default
+ * LogLevels which are set but these levels can be overriden.
+ *
+ * @param loglevels An integer representing either Log4J or JDK1.4 logging levels
+ * @return LogMonitorAdapter
+ */
+ public static LogMonitorAdapter newInstance(int loglevels) {
+ LogMonitorAdapter adapter;
+ if (loglevels == JDK14_LOG_LEVELS) {
+ adapter = newInstance(LogLevel.getJdk14Levels());
+ adapter.setDefaultLevel(LogLevel.FINEST);
+ adapter.setSevereLevel(LogLevel.SEVERE);
+ } else {
+ adapter = newInstance(LogLevel.getLog4JLevels());
+ adapter.setDefaultLevel(LogLevel.DEBUG);
+ adapter.setSevereLevel(LogLevel.FATAL);
+ }
+ return adapter;
+ }
+
+ /**
+ *
Creates an instance of LogMonitorAdapter using the specified LogLevels.
+ * The first LogLevel in the array is used as the default LogLevel unless
+ * changed using the setDefaultLevel method.
+ *
+ * @param userDefined An array of user defined LogLevel objects.
+ * @return LogMonitorAdapter
+ */
+ public static LogMonitorAdapter newInstance(LogLevel[] userDefined) {
+ if (userDefined == null) {
+ return null;
+ }
+ return newInstance(Arrays.asList(userDefined));
+ }
+
+ /**
+ *
Creates an instance of LogMonitorAdapter using the specified LogLevels.
+ * The first LogLevel in the List is used as the default LogLevel unless
+ * changed using the setDefaultLevel method.
+ *
+ * @param userDefinedLevels A list of user defined LogLevel objects.
+ * @return LogMonitorAdapter
+ */
+ public static LogMonitorAdapter newInstance(List userDefinedLevels) {
+ return new LogMonitorAdapter(userDefinedLevels);
+ }
+
+ /**
+ *
Adds a LogRecord to the LogMonitor.
+ *
+ * @param record The LogRecord object to be logged in the logging monitor.
+ */
+ public void addMessage(LogRecord record) {
+ _logMonitor.addMessage(record);
+ }
+
+ /**
+ *
Set the maximum number of records to be displayed in the monitor
+ *
+ * @param maxNumberOfRecords
+ */
+ public void setMaxNumberOfRecords(int maxNumberOfRecords) {
+ _logMonitor.setMaxNumberOfLogRecords(maxNumberOfRecords);
+ }
+
+ /**
+ *
Set the default log level to be used when logging messages without
+ * specifying a LogLevel.
+ *
+ * @param level
+ */
+ public void setDefaultLevel(LogLevel level) {
+ _defaultLevel = level;
+ }
+
+ /**
+ *
Gets the default LogLevel for the Adapter.
+ *
+ * @return LogLevel
+ */
+ public LogLevel getDefaultLevel() {
+ return _defaultLevel;
+ }
+
+ /**
+ *
Sets the Severe LogLevel.
+ *
+ * @param level
+ */
+ public void setSevereLevel(LogLevel level) {
+ AdapterLogRecord.setSevereLevel(level);
+ }
+
+ /**
+ * Gets the current Severe LogLevel
+ *
+ * @return LogLevel
+ */
+ public LogLevel getSevereLevel() {
+ return AdapterLogRecord.getSevereLevel();
+ }
+
+ /**
+ *
Log a complete message to the Monitor.
+ *
+ * @param category The category to be used
+ * @param level The log level to apply to the message
+ * @param message The message
+ * @param t The throwable content of the message
+ * @param NDC The NDC really only applies to Log4J and the parameter can
+ * usually be ignored.
+ */
+ public void log(String category, LogLevel level, String message,
+ Throwable t, String NDC) {
+ AdapterLogRecord record = new AdapterLogRecord();
+ record.setCategory(category);
+ record.setMessage(message);
+ record.setNDC(NDC);
+ record.setThrown(t);
+
+ if (level == null) {
+ record.setLevel(getDefaultLevel());
+ } else {
+ record.setLevel(level);
+ }
+
+ addMessage(record);
+ }
+
+ /**
+ *
Log a message to the Monitor and use the default LogLevel.
+ *
+ * @param category The category to be used
+ * @param message The message
+ */
+ public void log(String category, String message) {
+ log(category, null, message);
+ }
+
+ /**
+ *
Log a message to the Monitor.
+ *
+ * @param category The category to be used
+ * @param level The log level to apply to the message
+ * @param message The message
+ * @param NDC
+ */
+ public void log(String category, LogLevel level, String message, String NDC) {
+ log(category, level, message, null, NDC);
+ }
+
+ /**
+ *
Log a message to the Monitor.
+ *
+ * @param category The category to be used
+ * @param level The log level to apply to the message
+ * @param message The message
+ * @param t The throwable content of the message
+ */
+ public void log(String category, LogLevel level, String message,
+ Throwable t) {
+ log(category, level, message, t, null);
+ }
+
+ /**
+ *
Log a message to the Monitor.
+ *
+ * @param category The category to be used
+ * @param level The log level to apply to the message
+ * @param message The message
+ */
+ public void log(String category, LogLevel level, String message) {
+ log(category, level, message, null, null);
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+ /**
+ * @return the screen width from Toolkit.getScreenSize()
+ * if possible, otherwise returns 800
+ * @see java.awt.Toolkit
+ */
+ protected static int getScreenWidth() {
+ try {
+ return Toolkit.getDefaultToolkit().getScreenSize().width;
+ } catch (Throwable t) {
+ return 800;
+ }
+ }
+
+ /**
+ * @return the screen height from Toolkit.getScreenSize()
+ * if possible, otherwise returns 600
+ * @see java.awt.Toolkit
+ */
+ protected static int getScreenHeight() {
+ try {
+ return Toolkit.getDefaultToolkit().getScreenSize().height;
+ } catch (Throwable t) {
+ return 600;
+ }
+ }
+
+ protected static int getDefaultMonitorWidth() {
+ return (3 * getScreenWidth()) / 4;
+ }
+
+ protected static int getDefaultMonitorHeight() {
+ return (3 * getScreenHeight()) / 4;
+ }
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
+
diff --git a/java/src/org/apache/log4j/lf5/util/Resource.java b/java/src/org/apache/log4j/lf5/util/Resource.java
new file mode 100644
index 0000000..66293cf
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/util/Resource.java
@@ -0,0 +1,156 @@
+/*
+ * 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.log4j.lf5.util;
+
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.net.URL;
+
+/**
+ * Resource encapsulates access to Resources via the Classloader.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class Resource {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected String _name;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Default, no argument constructor.
+ */
+ public Resource() {
+ super();
+ }
+
+ /**
+ * Construct a Resource given a name.
+ *
+ * @see #setName(String)
+ */
+ public Resource(String name) {
+ _name = name;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Set the name of the resource.
+ *
+ * A resource is some data (images, audio, text, etc) that can be accessed
+ * by class code in a way that is independent of the location of the code.
+ *
+ *
+ * The name of a resource is a "/"-separated path name that identifies
+ * the resource.
+ *
+ *
+ * @see #getName()
+ */
+ public void setName(String name) {
+ _name = name;
+ }
+
+ /**
+ * Get the name of the resource. Set setName() for a description of
+ * a resource.
+ *
+ * @see #setName
+ */
+ public String getName() {
+ return (_name);
+ }
+
+ /**
+ * Get the InputStream for this Resource. Uses the classloader
+ * from this Resource.
+ *
+ * @see #getInputStreamReader
+ * @see ResourceUtils
+ */
+ public InputStream getInputStream() {
+ InputStream in = ResourceUtils.getResourceAsStream(this, this);
+
+ return (in);
+ }
+
+ /**
+ * Get the InputStreamReader for this Resource. Uses the classloader from
+ * this Resource.
+ *
+ * @see #getInputStream
+ * @see ResourceUtils
+ */
+ public InputStreamReader getInputStreamReader() {
+ InputStream in = ResourceUtils.getResourceAsStream(this, this);
+
+ if (in == null) {
+ return null;
+ }
+
+ InputStreamReader reader = new InputStreamReader(in);
+
+ return reader;
+ }
+
+ /**
+ * Get the URL of the Resource. Uses the classloader from this Resource.
+ *
+ * @see ResourceUtils
+ */
+ public URL getURL() {
+ return (ResourceUtils.getResourceAsURL(this, this));
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/util/ResourceUtils.java b/java/src/org/apache/log4j/lf5/util/ResourceUtils.java
new file mode 100644
index 0000000..5f022b6
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/util/ResourceUtils.java
@@ -0,0 +1,133 @@
+/*
+ * 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.log4j.lf5.util;
+
+import java.io.InputStream;
+import java.net.URL;
+
+/**
+ * ResourceUtils. Provide a set of convenience methods for working with
+ * Resources.
+ *
+ * @see org.apache.log4j.lf5.util.Resource
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class ResourceUtils {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Get the InputStream for this resource. Note: to convert an InputStream
+ * into an InputReader, use: new InputStreamReader(InputStream).
+ *
+ * @param object The object to grab the Classloader from.
+ * This parameter is quite important from a
+ * visibility of resources standpoint as the
+ * hierarchy of Classloaders plays a role.
+ *
+ * @param resource The resource to load.
+ *
+ * @return If the Resource was found, the InputStream, otherwise null.
+ *
+ * @see Resource
+ * @see #getResourceAsURL(Object,Resource)
+ * @see InputStream
+ */
+ public static InputStream getResourceAsStream(Object object, Resource resource) {
+ ClassLoader loader = object.getClass().getClassLoader();
+
+ InputStream in = null;
+
+ if (loader != null) {
+ in = loader.getResourceAsStream(resource.getName());
+ } else {
+ in = ClassLoader.getSystemResourceAsStream(resource.getName());
+ }
+
+ return in;
+ }
+
+ /**
+ * Get the URL for this resource.
+ *
+ * @param object The object to grab the Classloader from.
+ * This parameter is quite important from a
+ * visibility of resources standpoint as the
+ * hierarchy of Classloaders plays a role.
+ *
+ * @param resource The resource to load.
+ *
+ * @return If the Resource was found, the URL, otherwise null.
+ *
+ * @see Resource
+ * @see #getResourceAsStream(Object,Resource)
+ */
+ public static URL getResourceAsURL(Object object, Resource resource) {
+ ClassLoader loader = object.getClass().getClassLoader();
+
+ URL url = null;
+
+ if (loader != null) {
+ url = loader.getResource(resource.getName());
+ } else {
+ url = ClassLoader.getSystemResource(resource.getName());
+ }
+
+ return (url);
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/util/StreamUtils.java b/java/src/org/apache/log4j/lf5/util/StreamUtils.java
new file mode 100644
index 0000000..183f9aa
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/util/StreamUtils.java
@@ -0,0 +1,124 @@
+/*
+ * 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.log4j.lf5.util;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Provides utility methods for input and output streams.
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public abstract class StreamUtils {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Default value is 2048.
+ */
+ public static final int DEFAULT_BUFFER_SIZE = 2048;
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Copies information from the input stream to the output stream using
+ * a default buffer size of 2048 bytes.
+ * @throws java.io.IOException
+ */
+ public static void copy(InputStream input, OutputStream output)
+ throws IOException {
+ copy(input, output, DEFAULT_BUFFER_SIZE);
+ }
+
+ /**
+ * Copies information from the input stream to the output stream using
+ * the specified buffer size
+ * @throws java.io.IOException
+ */
+ public static void copy(InputStream input,
+ OutputStream output,
+ int bufferSize)
+ throws IOException {
+ byte[] buf = new byte[bufferSize];
+ int bytesRead = input.read(buf);
+ while (bytesRead != -1) {
+ output.write(buf, 0, bytesRead);
+ bytesRead = input.read(buf);
+ }
+ output.flush();
+ }
+
+ /**
+ * Copies information between specified streams and then closes
+ * both of the streams.
+ * @throws java.io.IOException
+ */
+ public static void copyThenClose(InputStream input, OutputStream output)
+ throws IOException {
+ copy(input, output);
+ input.close();
+ output.close();
+ }
+
+ /**
+ * @return a byte[] containing the information contained in the
+ * specified InputStream.
+ * @throws java.io.IOException
+ */
+ public static byte[] getBytes(InputStream input)
+ throws IOException {
+ ByteArrayOutputStream result = new ByteArrayOutputStream();
+ copy(input, result);
+ result.close();
+ return result.toByteArray();
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+
+}
diff --git a/java/src/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java b/java/src/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java
new file mode 100644
index 0000000..2c68180
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/FilteredLogTableModel.java
@@ -0,0 +1,263 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import org.apache.log4j.lf5.LogRecord;
+import org.apache.log4j.lf5.LogRecordFilter;
+import org.apache.log4j.lf5.PassingLogRecordFilter;
+
+import javax.swing.table.AbstractTableModel;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.List;
+
+
+/**
+ * A TableModel for LogRecords which includes filtering support.
+ *
+ * @author Richard Wan
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class FilteredLogTableModel
+ extends AbstractTableModel {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ protected LogRecordFilter _filter = new PassingLogRecordFilter();
+ protected List _allRecords = new ArrayList();
+ protected List _filteredRecords;
+ protected int _maxNumberOfLogRecords = 5000;
+ protected String[] _colNames = {"Date",
+ "Thread",
+ "Message #",
+ "Level",
+ "NDC",
+ "Category",
+ "Message",
+ "Location",
+ "Thrown"};
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public FilteredLogTableModel() {
+ super();
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public void setLogRecordFilter(LogRecordFilter filter) {
+ _filter = filter;
+ }
+
+ public LogRecordFilter getLogRecordFilter() {
+ return _filter;
+ }
+
+ public String getColumnName(int i) {
+ return _colNames[i];
+ }
+
+ public int getColumnCount() {
+ return _colNames.length;
+ }
+
+ public int getRowCount() {
+ return getFilteredRecords().size();
+ }
+
+ public int getTotalRowCount() {
+ return _allRecords.size();
+ }
+
+ public Object getValueAt(int row, int col) {
+ LogRecord record = getFilteredRecord(row);
+ return getColumn(col, record);
+ }
+
+ public void setMaxNumberOfLogRecords(int maxNumRecords) {
+ if (maxNumRecords > 0) {
+ _maxNumberOfLogRecords = maxNumRecords;
+ }
+
+ }
+
+ public synchronized boolean addLogRecord(LogRecord record) {
+
+ _allRecords.add(record);
+
+ if (_filter.passes(record) == false) {
+ return false;
+ }
+ getFilteredRecords().add(record);
+ fireTableRowsInserted(getRowCount(), getRowCount());
+ trimRecords();
+ return true;
+ }
+
+ /**
+ * Forces the LogTableModel to requery its filters to determine
+ * which records to display.
+ */
+ public synchronized void refresh() {
+ _filteredRecords = createFilteredRecordsList();
+ fireTableDataChanged();
+ }
+
+ public synchronized void fastRefresh() {
+ _filteredRecords.remove(0);
+ fireTableRowsDeleted(0, 0);
+ }
+
+
+ /**
+ * Clears all records from the LogTableModel
+ */
+ public synchronized void clear() {
+ _allRecords.clear();
+ _filteredRecords.clear();
+ fireTableDataChanged();
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected List getFilteredRecords() {
+ if (_filteredRecords == null) {
+ refresh();
+ }
+ return _filteredRecords;
+ }
+
+ protected List createFilteredRecordsList() {
+ List result = new ArrayList();
+ Iterator records = _allRecords.iterator();
+ LogRecord current;
+ while (records.hasNext()) {
+ current = (LogRecord) records.next();
+ if (_filter.passes(current)) {
+ result.add(current);
+ }
+ }
+ return result;
+ }
+
+ protected LogRecord getFilteredRecord(int row) {
+ List records = getFilteredRecords();
+ int size = records.size();
+ if (row < size) {
+ return (LogRecord) records.get(row);
+ }
+ // a minor problem has happened. JTable has asked for
+ // a row outside the bounds, because the size of
+ // _filteredRecords has changed while it was looping.
+ // return the last row.
+ return (LogRecord) records.get(size - 1);
+
+ }
+
+ protected Object getColumn(int col, LogRecord lr) {
+ if (lr == null) {
+ return "NULL Column";
+ }
+ String date = new Date(lr.getMillis()).toString();
+ switch (col) {
+ case 0:
+ return date + " (" + lr.getMillis() + ")";
+ case 1:
+ return lr.getThreadDescription();
+ case 2:
+ return new Long(lr.getSequenceNumber());
+ case 3:
+ return lr.getLevel();
+ case 4:
+ return lr.getNDC();
+ case 5:
+ return lr.getCategory();
+ case 6:
+ return lr.getMessage();
+ case 7:
+ return lr.getLocation();
+ case 8:
+ return lr.getThrownStackTrace();
+ default:
+ String message = "The column number " + col + "must be between 0 and 8";
+ throw new IllegalArgumentException(message);
+ }
+ }
+
+ // We don't want the amount of rows to grow without bound,
+ // leading to a out-of-memory-exception. Especially not good
+ // in a production environment :)
+
+ // This method & clearLogRecords() are synchronized so we don't
+ // delete rows that don't exist.
+ protected void trimRecords() {
+ if (needsTrimming()) {
+ trimOldestRecords();
+ }
+ }
+
+ protected boolean needsTrimming() {
+ return (_allRecords.size() > _maxNumberOfLogRecords);
+ }
+
+ protected void trimOldestRecords() {
+ synchronized (_allRecords) {
+ int trim = numberOfRecordsToTrim();
+ if (trim > 1) {
+ List oldRecords =
+ _allRecords.subList(0, trim);
+ oldRecords.clear();
+ refresh();
+ } else {
+ _allRecords.remove(0);
+ fastRefresh();
+ }
+ }
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+ private int numberOfRecordsToTrim() {
+ return _allRecords.size() - _maxNumberOfLogRecords;
+ }
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/LF5SwingUtils.java b/java/src/org/apache/log4j/lf5/viewer/LF5SwingUtils.java
new file mode 100644
index 0000000..ef29447
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LF5SwingUtils.java
@@ -0,0 +1,153 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import java.awt.Adjustable;
+
+import javax.swing.JComponent;
+import javax.swing.JScrollPane;
+import javax.swing.JTable;
+import javax.swing.ListSelectionModel;
+import javax.swing.SwingUtilities;
+import javax.swing.table.TableModel;
+
+/**
+ * Provides methods to accomplish common yet non-trivial tasks
+ * with Swing. Obvious implementations of these methods have been
+ * tried and failed.
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LF5SwingUtils {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Selects a the specified row in the specified JTable and scrolls
+ * the specified JScrollpane to the newly selected row. More importantly,
+ * the call to repaint() delayed long enough to have the table
+ * properly paint the newly selected row which may be offscre
+ * @param table should belong to the specified JScrollPane
+ */
+ public static void selectRow(int row, JTable table, JScrollPane pane) {
+ if (table == null || pane == null) {
+ return;
+ }
+ if (contains(row, table.getModel()) == false) {
+ return;
+ }
+ moveAdjustable(row * table.getRowHeight(), pane.getVerticalScrollBar());
+ selectRow(row, table.getSelectionModel());
+ // repaint must be done later because moveAdjustable
+ // posts requests to the swing thread which must execute before
+ // the repaint logic gets executed.
+ repaintLater(table);
+ }
+
+ /**
+ * Makes the specified Adjustable track if the view area expands and
+ * the specified Adjustable is located near the of the view.
+ */
+ public static void makeScrollBarTrack(Adjustable scrollBar) {
+ if (scrollBar == null) {
+ return;
+ }
+ scrollBar.addAdjustmentListener(new TrackingAdjustmentListener());
+ }
+
+ /**
+ * Makes the vertical scroll bar of the specified JScrollPane
+ * track if the view expands (e.g. if rows are added to an underlying
+ * table).
+ */
+ public static void makeVerticalScrollBarTrack(JScrollPane pane) {
+ if (pane == null) {
+ return;
+ }
+ makeScrollBarTrack(pane.getVerticalScrollBar());
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+ protected static boolean contains(int row, TableModel model) {
+ if (model == null) {
+ return false;
+ }
+ if (row < 0) {
+ return false;
+ }
+ if (row >= model.getRowCount()) {
+ return false;
+ }
+ return true;
+ }
+
+ protected static void selectRow(int row, ListSelectionModel model) {
+ if (model == null) {
+ return;
+ }
+ model.setSelectionInterval(row, row);
+ }
+
+ protected static void moveAdjustable(int location, Adjustable scrollBar) {
+ if (scrollBar == null) {
+ return;
+ }
+ scrollBar.setValue(location);
+ }
+
+ /**
+ * Work around for JTable/viewport bug.
+ * @link http://developer.java.sun.com/developer/bugParade/bugs/4205145.html
+ */
+ protected static void repaintLater(final JComponent component) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ component.repaint();
+ }
+ });
+ }
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java b/java/src/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java
new file mode 100644
index 0000000..934ba17
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LogBrokerMonitor.java
@@ -0,0 +1,1612 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import java.awt.BorderLayout;
+import java.awt.Color;
+import java.awt.Component;
+import java.awt.Dimension;
+import java.awt.FlowLayout;
+import java.awt.Font;
+import java.awt.GraphicsEnvironment;
+import java.awt.Toolkit;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.WindowAdapter;
+import java.awt.event.WindowEvent;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import javax.swing.BorderFactory;
+import javax.swing.ImageIcon;
+import javax.swing.JButton;
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.JColorChooser;
+import javax.swing.JComboBox;
+import javax.swing.JFileChooser;
+import javax.swing.JFrame;
+import javax.swing.JLabel;
+import javax.swing.JMenu;
+import javax.swing.JMenuBar;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPanel;
+import javax.swing.JScrollPane;
+import javax.swing.JSplitPane;
+import javax.swing.JTextArea;
+import javax.swing.JToolBar;
+import javax.swing.KeyStroke;
+import javax.swing.SwingUtilities;
+
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogRecord;
+import org.apache.log4j.lf5.LogRecordFilter;
+import org.apache.log4j.lf5.util.DateFormatManager;
+import org.apache.log4j.lf5.util.LogFileParser;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerTree;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryPath;
+import org.apache.log4j.lf5.viewer.configure.ConfigurationManager;
+import org.apache.log4j.lf5.viewer.configure.MRUFileManager;
+
+/**
+ * LogBrokerMonitor
+ *.
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brad Marlborough
+ * @author Richard Wan
+ * @author Brent Sprecher
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogBrokerMonitor {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ public static final String DETAILED_VIEW = "Detailed";
+// public static final String STANDARD_VIEW = "Standard";
+// public static final String COMPACT_VIEW = "Compact";
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected JFrame _logMonitorFrame;
+ protected int _logMonitorFrameWidth = 550;
+ protected int _logMonitorFrameHeight = 500;
+ protected LogTable _table;
+ protected CategoryExplorerTree _categoryExplorerTree;
+ protected String _searchText;
+ protected String _NDCTextFilter = "";
+ protected LogLevel _leastSevereDisplayedLogLevel = LogLevel.DEBUG;
+
+ protected JScrollPane _logTableScrollPane;
+ protected JLabel _statusLabel;
+ protected Object _lock = new Object();
+ protected JComboBox _fontSizeCombo;
+
+ protected int _fontSize = 10;
+ protected String _fontName = "Dialog";
+ protected String _currentView = DETAILED_VIEW;
+
+ protected boolean _loadSystemFonts = false;
+ protected boolean _trackTableScrollPane = true;
+ protected Dimension _lastTableViewportSize;
+ protected boolean _callSystemExitOnClose = false;
+ protected List _displayedLogBrokerProperties = new Vector();
+
+ protected Map _logLevelMenuItems = new HashMap();
+ protected Map _logTableColumnMenuItems = new HashMap();
+
+ protected List _levels = null;
+ protected List _columns = null;
+ protected boolean _isDisposed = false;
+
+ protected ConfigurationManager _configurationManager = null;
+ protected MRUFileManager _mruFileManager = null;
+ protected File _fileLocation = null;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Construct a LogBrokerMonitor.
+ */
+ public LogBrokerMonitor(List logLevels) {
+
+ _levels = logLevels;
+ _columns = LogTableColumn.getLogTableColumns();
+ // This allows us to use the LogBroker in command line tools and
+ // have the option for it to shutdown.
+
+ String callSystemExitOnClose =
+ System.getProperty("monitor.exit");
+ if (callSystemExitOnClose == null) {
+ callSystemExitOnClose = "false";
+ }
+ callSystemExitOnClose = callSystemExitOnClose.trim().toLowerCase();
+
+ if (callSystemExitOnClose.equals("true")) {
+ _callSystemExitOnClose = true;
+ }
+
+ initComponents();
+
+
+ _logMonitorFrame.addWindowListener(
+ new LogBrokerMonitorWindowAdaptor(this));
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Show the frame for the LogBrokerMonitor. Dispatched to the
+ * swing thread.
+ */
+ public void show(final int delay) {
+ if (_logMonitorFrame.isVisible()) {
+ return;
+ }
+ // This request is very low priority, let other threads execute first.
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ Thread.yield();
+ pause(delay);
+ _logMonitorFrame.setVisible(true);
+ }
+ });
+ }
+
+ public void show() {
+ show(0);
+ }
+
+ /**
+ * Dispose of the frame for the LogBrokerMonitor.
+ */
+ public void dispose() {
+ _logMonitorFrame.dispose();
+ _isDisposed = true;
+
+ if (_callSystemExitOnClose == true) {
+ System.exit(0);
+ }
+ }
+
+ /**
+ * Hide the frame for the LogBrokerMonitor.
+ */
+ public void hide() {
+ _logMonitorFrame.setVisible(false);
+ }
+
+ /**
+ * Get the DateFormatManager for formatting dates.
+ */
+ public DateFormatManager getDateFormatManager() {
+ return _table.getDateFormatManager();
+ }
+
+ /**
+ * Set the date format manager for formatting dates.
+ */
+ public void setDateFormatManager(DateFormatManager dfm) {
+ _table.setDateFormatManager(dfm);
+ }
+
+ /**
+ * Get the value of whether or not System.exit() will be called
+ * when the LogBrokerMonitor is closed.
+ */
+ public boolean getCallSystemExitOnClose() {
+ return _callSystemExitOnClose;
+ }
+
+ /**
+ * Set the value of whether or not System.exit() will be called
+ * when the LogBrokerMonitor is closed.
+ */
+ public void setCallSystemExitOnClose(boolean callSystemExitOnClose) {
+ _callSystemExitOnClose = callSystemExitOnClose;
+ }
+
+ /**
+ * Add a log record message to be displayed in the LogTable.
+ * This method is thread-safe as it posts requests to the SwingThread
+ * rather than processing directly.
+ */
+ public void addMessage(final LogRecord lr) {
+ if (_isDisposed == true) {
+ // If the frame has been disposed of, do not log any more
+ // messages.
+ return;
+ }
+
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ _categoryExplorerTree.getExplorerModel().addLogRecord(lr);
+ _table.getFilteredLogTableModel().addLogRecord(lr); // update table
+ updateStatusLabel(); // show updated counts
+ }
+ });
+ }
+
+ public void setMaxNumberOfLogRecords(int maxNumberOfLogRecords) {
+ _table.getFilteredLogTableModel().setMaxNumberOfLogRecords(maxNumberOfLogRecords);
+ }
+
+ public JFrame getBaseFrame() {
+ return _logMonitorFrame;
+ }
+
+ public void setTitle(String title) {
+ _logMonitorFrame.setTitle(title + " - LogFactor5");
+ }
+
+ public void setFrameSize(int width, int height) {
+ Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
+ if (0 < width && width < screen.width) {
+ _logMonitorFrameWidth = width;
+ }
+ if (0 < height && height < screen.height) {
+ _logMonitorFrameHeight = height;
+ }
+ updateFrameSize();
+ }
+
+ public void setFontSize(int fontSize) {
+ changeFontSizeCombo(_fontSizeCombo, fontSize);
+ // setFontSizeSilently(actualFontSize); - changeFontSizeCombo fires event
+ // refreshDetailTextArea();
+ }
+
+ public void addDisplayedProperty(Object messageLine) {
+ _displayedLogBrokerProperties.add(messageLine);
+ }
+
+ public Map getLogLevelMenuItems() {
+ return _logLevelMenuItems;
+ }
+
+ public Map getLogTableColumnMenuItems() {
+ return _logTableColumnMenuItems;
+ }
+
+ public JCheckBoxMenuItem getTableColumnMenuItem(LogTableColumn column) {
+ return getLogTableColumnMenuItem(column);
+ }
+
+ public CategoryExplorerTree getCategoryExplorerTree() {
+ return _categoryExplorerTree;
+ }
+
+ // Added in version 1.2 - gets the value of the NDC text filter
+ // This value is set back to null each time the Monitor is initialized.
+ public String getNDCTextFilter() {
+ return _NDCTextFilter;
+ }
+
+ // Added in version 1.2 - sets the NDC Filter based on
+ // a String passed in by the user. This value is persisted
+ // in the XML Configuration file.
+ public void setNDCLogRecordFilter(String textFilter) {
+ _table.getFilteredLogTableModel().
+ setLogRecordFilter(createNDCLogRecordFilter(textFilter));
+ }
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected void setSearchText(String text) {
+ _searchText = text;
+ }
+
+ // Added in version 1.2 - Sets the text filter for the NDC
+ protected void setNDCTextFilter(String text) {
+ // if no value is set, set it to a blank string
+ // otherwise use the value provided
+ if (text == null) {
+ _NDCTextFilter = "";
+ } else {
+ _NDCTextFilter = text;
+ }
+ }
+
+ // Added in version 1.2 - Uses a different filter that sorts
+ // based on an NDC string passed in by the user. If the string
+ // is null or is an empty string, we do nothing.
+ protected void sortByNDC() {
+ String text = _NDCTextFilter;
+ if (text == null || text.length() == 0) {
+ return;
+ }
+
+ // Use new NDC filter
+ _table.getFilteredLogTableModel().
+ setLogRecordFilter(createNDCLogRecordFilter(text));
+ }
+
+ protected void findSearchText() {
+ String text = _searchText;
+ if (text == null || text.length() == 0) {
+ return;
+ }
+ int startRow = getFirstSelectedRow();
+ int foundRow = findRecord(
+ startRow,
+ text,
+ _table.getFilteredLogTableModel().getFilteredRecords()
+ );
+ selectRow(foundRow);
+ }
+
+ protected int getFirstSelectedRow() {
+ return _table.getSelectionModel().getMinSelectionIndex();
+ }
+
+ protected void selectRow(int foundRow) {
+ if (foundRow == -1) {
+ String message = _searchText + " not found.";
+ JOptionPane.showMessageDialog(
+ _logMonitorFrame,
+ message,
+ "Text not found",
+ JOptionPane.INFORMATION_MESSAGE
+ );
+ return;
+ }
+ LF5SwingUtils.selectRow(foundRow, _table, _logTableScrollPane);
+ }
+
+ protected int findRecord(
+ int startRow,
+ String searchText,
+ List records
+ ) {
+ if (startRow < 0) {
+ startRow = 0; // start at first element if no rows are selected
+ } else {
+ startRow++; // start after the first selected row
+ }
+ int len = records.size();
+
+ for (int i = startRow; i < len; i++) {
+ if (matches((LogRecord) records.get(i), searchText)) {
+ return i; // found a record
+ }
+ }
+ // wrap around to beginning if when we reach the end with no match
+ len = startRow;
+ for (int i = 0; i < len; i++) {
+ if (matches((LogRecord) records.get(i), searchText)) {
+ return i; // found a record
+ }
+ }
+ // nothing found
+ return -1;
+ }
+
+ /**
+ * Check to see if the any records contain the search string.
+ * Searching now supports NDC messages and date.
+ */
+ protected boolean matches(LogRecord record, String text) {
+ String message = record.getMessage();
+ String NDC = record.getNDC();
+
+ if (message == null && NDC == null || text == null) {
+ return false;
+ }
+ if (message.toLowerCase().indexOf(text.toLowerCase()) == -1 &&
+ NDC.toLowerCase().indexOf(text.toLowerCase()) == -1) {
+ return false;
+ }
+
+ return true;
+ }
+
+ /**
+ * When the fontsize of a JTextArea is changed, the word-wrapped lines
+ * may become garbled. This method clears and resets the text of the
+ * text area.
+ */
+ protected void refresh(JTextArea textArea) {
+ String text = textArea.getText();
+ textArea.setText("");
+ textArea.setText(text);
+ }
+
+ protected void refreshDetailTextArea() {
+ refresh(_table._detailTextArea);
+ }
+
+ protected void clearDetailTextArea() {
+ _table._detailTextArea.setText("");
+ }
+
+ /**
+ * Changes the font selection in the combo box and returns the
+ * size actually selected.
+ * @return -1 if unable to select an appropriate font
+ */
+ protected int changeFontSizeCombo(JComboBox box, int requestedSize) {
+ int len = box.getItemCount();
+ int currentValue;
+ Object currentObject;
+ Object selectedObject = box.getItemAt(0);
+ int selectedValue = Integer.parseInt(String.valueOf(selectedObject));
+ for (int i = 0; i < len; i++) {
+ currentObject = box.getItemAt(i);
+ currentValue = Integer.parseInt(String.valueOf(currentObject));
+ if (selectedValue < currentValue && currentValue <= requestedSize) {
+ selectedValue = currentValue;
+ selectedObject = currentObject;
+ }
+ }
+ box.setSelectedItem(selectedObject);
+ return selectedValue;
+ }
+
+ /**
+ * Does not update gui or cause any events to be fired.
+ */
+ protected void setFontSizeSilently(int fontSize) {
+ _fontSize = fontSize;
+ setFontSize(_table._detailTextArea, fontSize);
+ selectRow(0);
+ setFontSize(_table, fontSize);
+ }
+
+ protected void setFontSize(Component component, int fontSize) {
+ Font oldFont = component.getFont();
+ Font newFont =
+ new Font(oldFont.getFontName(), oldFont.getStyle(), fontSize);
+ component.setFont(newFont);
+ }
+
+ protected void updateFrameSize() {
+ _logMonitorFrame.setSize(_logMonitorFrameWidth, _logMonitorFrameHeight);
+ centerFrame(_logMonitorFrame);
+ }
+
+ protected void pause(int millis) {
+ try {
+ Thread.sleep(millis);
+ } catch (InterruptedException e) {
+
+ }
+ }
+
+ protected void initComponents() {
+ //
+ // Configure the Frame.
+ //
+ _logMonitorFrame = new JFrame("LogFactor5");
+
+ _logMonitorFrame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
+
+ String resource =
+ "/org/apache/log4j/lf5/viewer/images/lf5_small_icon.gif";
+ URL lf5IconURL = getClass().getResource(resource);
+
+ if (lf5IconURL != null) {
+ _logMonitorFrame.setIconImage(new ImageIcon(lf5IconURL).getImage());
+ }
+ updateFrameSize();
+
+ //
+ // Configure the LogTable.
+ //
+ JTextArea detailTA = createDetailTextArea();
+ JScrollPane detailTAScrollPane = new JScrollPane(detailTA);
+ _table = new LogTable(detailTA);
+ setView(_currentView, _table);
+ _table.setFont(new Font(_fontName, Font.PLAIN, _fontSize));
+ _logTableScrollPane = new JScrollPane(_table);
+
+ if (_trackTableScrollPane) {
+ _logTableScrollPane.getVerticalScrollBar().addAdjustmentListener(
+ new TrackingAdjustmentListener()
+ );
+ }
+
+
+ // Configure the SplitPane between the LogTable & DetailTextArea
+ //
+
+ JSplitPane tableViewerSplitPane = new JSplitPane();
+ tableViewerSplitPane.setOneTouchExpandable(true);
+ tableViewerSplitPane.setOrientation(JSplitPane.VERTICAL_SPLIT);
+ tableViewerSplitPane.setLeftComponent(_logTableScrollPane);
+ tableViewerSplitPane.setRightComponent(detailTAScrollPane);
+ // Make sure to do this last..
+ //tableViewerSplitPane.setDividerLocation(1.0); Doesn't work
+ //the same under 1.2.x & 1.3
+ // "350" is a magic number that provides the correct default
+ // behaviour under 1.2.x & 1.3. For example, bumping this
+ // number to 400, causes the pane to be completely open in 1.2.x
+ // and closed in 1.3
+ tableViewerSplitPane.setDividerLocation(350);
+
+ //
+ // Configure the CategoryExplorer
+ //
+
+ _categoryExplorerTree = new CategoryExplorerTree();
+
+ _table.getFilteredLogTableModel().setLogRecordFilter(createLogRecordFilter());
+
+ JScrollPane categoryExplorerTreeScrollPane =
+ new JScrollPane(_categoryExplorerTree);
+ categoryExplorerTreeScrollPane.setPreferredSize(new Dimension(130, 400));
+
+ // Load most recently used file list
+ _mruFileManager = new MRUFileManager();
+
+ //
+ // Configure the SplitPane between the CategoryExplorer & (LogTable/Detail)
+ //
+
+ JSplitPane splitPane = new JSplitPane();
+ splitPane.setOneTouchExpandable(true);
+ splitPane.setRightComponent(tableViewerSplitPane);
+ splitPane.setLeftComponent(categoryExplorerTreeScrollPane);
+ // Do this last.
+ splitPane.setDividerLocation(130);
+ //
+ // Add the MenuBar, StatusArea, CategoryExplorer|LogTable to the
+ // LogMonitorFrame.
+ //
+ _logMonitorFrame.getRootPane().setJMenuBar(createMenuBar());
+ _logMonitorFrame.getContentPane().add(splitPane, BorderLayout.CENTER);
+ _logMonitorFrame.getContentPane().add(createToolBar(),
+ BorderLayout.NORTH);
+ _logMonitorFrame.getContentPane().add(createStatusArea(),
+ BorderLayout.SOUTH);
+
+ makeLogTableListenToCategoryExplorer();
+ addTableModelProperties();
+
+ //
+ // Configure ConfigurationManager
+ //
+ _configurationManager = new ConfigurationManager(this, _table);
+
+ }
+
+ protected LogRecordFilter createLogRecordFilter() {
+ LogRecordFilter result = new LogRecordFilter() {
+ public boolean passes(LogRecord record) {
+ CategoryPath path = new CategoryPath(record.getCategory());
+ return
+ getMenuItem(record.getLevel()).isSelected() &&
+ _categoryExplorerTree.getExplorerModel().isCategoryPathActive(path);
+ }
+ };
+ return result;
+ }
+
+ // Added in version 1.2 - Creates a new filter that sorts records based on
+ // an NDC string passed in by the user.
+ protected LogRecordFilter createNDCLogRecordFilter(String text) {
+ _NDCTextFilter = text;
+ LogRecordFilter result = new LogRecordFilter() {
+ public boolean passes(LogRecord record) {
+ String NDC = record.getNDC();
+ CategoryPath path = new CategoryPath(record.getCategory());
+ if (NDC == null || _NDCTextFilter == null) {
+ return false;
+ } else if (NDC.toLowerCase().indexOf(_NDCTextFilter.toLowerCase()) == -1) {
+ return false;
+ } else {
+ return getMenuItem(record.getLevel()).isSelected() &&
+ _categoryExplorerTree.getExplorerModel().isCategoryPathActive(path);
+ }
+ }
+ };
+
+ return result;
+ }
+
+
+ protected void updateStatusLabel() {
+ _statusLabel.setText(getRecordsDisplayedMessage());
+ }
+
+ protected String getRecordsDisplayedMessage() {
+ FilteredLogTableModel model = _table.getFilteredLogTableModel();
+ return getStatusText(model.getRowCount(), model.getTotalRowCount());
+ }
+
+ protected void addTableModelProperties() {
+ final FilteredLogTableModel model = _table.getFilteredLogTableModel();
+
+ addDisplayedProperty(new Object() {
+ public String toString() {
+ return getRecordsDisplayedMessage();
+ }
+ });
+ addDisplayedProperty(new Object() {
+ public String toString() {
+ return "Maximum number of displayed LogRecords: "
+ + model._maxNumberOfLogRecords;
+ }
+ });
+ }
+
+ protected String getStatusText(int displayedRows, int totalRows) {
+ StringBuffer result = new StringBuffer();
+ result.append("Displaying: ");
+ result.append(displayedRows);
+ result.append(" records out of a total of: ");
+ result.append(totalRows);
+ result.append(" records.");
+ return result.toString();
+ }
+
+ protected void makeLogTableListenToCategoryExplorer() {
+ ActionListener listener = new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _table.getFilteredLogTableModel().refresh();
+ updateStatusLabel();
+ }
+ };
+ _categoryExplorerTree.getExplorerModel().addActionListener(listener);
+ }
+
+ protected JPanel createStatusArea() {
+ JPanel statusArea = new JPanel();
+ JLabel status =
+ new JLabel("No log records to display.");
+ _statusLabel = status;
+ status.setHorizontalAlignment(JLabel.LEFT);
+
+ statusArea.setBorder(BorderFactory.createEtchedBorder());
+ statusArea.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+ statusArea.add(status);
+
+ return (statusArea);
+ }
+
+ protected JTextArea createDetailTextArea() {
+ JTextArea detailTA = new JTextArea();
+ detailTA.setFont(new Font("Monospaced", Font.PLAIN, 14));
+ detailTA.setTabSize(3);
+ detailTA.setLineWrap(true);
+ detailTA.setWrapStyleWord(false);
+ return (detailTA);
+ }
+
+ protected JMenuBar createMenuBar() {
+ JMenuBar menuBar = new JMenuBar();
+ menuBar.add(createFileMenu());
+ menuBar.add(createEditMenu());
+ menuBar.add(createLogLevelMenu());
+ menuBar.add(createViewMenu());
+ menuBar.add(createConfigureMenu());
+ menuBar.add(createHelpMenu());
+
+ return (menuBar);
+ }
+
+ protected JMenu createLogLevelMenu() {
+ JMenu result = new JMenu("Log Level");
+ result.setMnemonic('l');
+ Iterator levels = getLogLevels();
+ while (levels.hasNext()) {
+ result.add(getMenuItem((LogLevel) levels.next()));
+ }
+
+ result.addSeparator();
+ result.add(createAllLogLevelsMenuItem());
+ result.add(createNoLogLevelsMenuItem());
+ result.addSeparator();
+ result.add(createLogLevelColorMenu());
+ result.add(createResetLogLevelColorMenuItem());
+
+ return result;
+ }
+
+ protected JMenuItem createAllLogLevelsMenuItem() {
+ JMenuItem result = new JMenuItem("Show all LogLevels");
+ result.setMnemonic('s');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ selectAllLogLevels(true);
+ _table.getFilteredLogTableModel().refresh();
+ updateStatusLabel();
+ }
+ });
+ return result;
+ }
+
+ protected JMenuItem createNoLogLevelsMenuItem() {
+ JMenuItem result = new JMenuItem("Hide all LogLevels");
+ result.setMnemonic('h');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ selectAllLogLevels(false);
+ _table.getFilteredLogTableModel().refresh();
+ updateStatusLabel();
+ }
+ });
+ return result;
+ }
+
+ protected JMenu createLogLevelColorMenu() {
+ JMenu colorMenu = new JMenu("Configure LogLevel Colors");
+ colorMenu.setMnemonic('c');
+ Iterator levels = getLogLevels();
+ while (levels.hasNext()) {
+ colorMenu.add(createSubMenuItem((LogLevel) levels.next()));
+ }
+
+ return colorMenu;
+ }
+
+ protected JMenuItem createResetLogLevelColorMenuItem() {
+ JMenuItem result = new JMenuItem("Reset LogLevel Colors");
+ result.setMnemonic('r');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ // reset the level colors in the map
+ LogLevel.resetLogLevelColorMap();
+
+ // refresh the table
+ _table.getFilteredLogTableModel().refresh();
+ }
+ });
+ return result;
+ }
+
+ protected void selectAllLogLevels(boolean selected) {
+ Iterator levels = getLogLevels();
+ while (levels.hasNext()) {
+ getMenuItem((LogLevel) levels.next()).setSelected(selected);
+ }
+ }
+
+ protected JCheckBoxMenuItem getMenuItem(LogLevel level) {
+ JCheckBoxMenuItem result = (JCheckBoxMenuItem) (_logLevelMenuItems.get(level));
+ if (result == null) {
+ result = createMenuItem(level);
+ _logLevelMenuItems.put(level, result);
+ }
+ return result;
+ }
+
+ protected JMenuItem createSubMenuItem(LogLevel level) {
+ final JMenuItem result = new JMenuItem(level.toString());
+ final LogLevel logLevel = level;
+ result.setMnemonic(level.toString().charAt(0));
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ showLogLevelColorChangeDialog(result, logLevel);
+ }
+ });
+
+ return result;
+
+ }
+
+ protected void showLogLevelColorChangeDialog(JMenuItem result, LogLevel level) {
+ JMenuItem menuItem = result;
+ Color newColor = JColorChooser.showDialog(
+ _logMonitorFrame,
+ "Choose LogLevel Color",
+ result.getForeground());
+
+ if (newColor != null) {
+ // set the color for the record
+ level.setLogLevelColorMap(level, newColor);
+ _table.getFilteredLogTableModel().refresh();
+ }
+
+ }
+
+ protected JCheckBoxMenuItem createMenuItem(LogLevel level) {
+ JCheckBoxMenuItem result = new JCheckBoxMenuItem(level.toString());
+ result.setSelected(true);
+ result.setMnemonic(level.toString().charAt(0));
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _table.getFilteredLogTableModel().refresh();
+ updateStatusLabel();
+ }
+ });
+ return result;
+ }
+
+ // view menu
+ protected JMenu createViewMenu() {
+ JMenu result = new JMenu("View");
+ result.setMnemonic('v');
+ Iterator columns = getLogTableColumns();
+ while (columns.hasNext()) {
+ result.add(getLogTableColumnMenuItem((LogTableColumn) columns.next()));
+ }
+
+ result.addSeparator();
+ result.add(createAllLogTableColumnsMenuItem());
+ result.add(createNoLogTableColumnsMenuItem());
+ return result;
+ }
+
+ protected JCheckBoxMenuItem getLogTableColumnMenuItem(LogTableColumn column) {
+ JCheckBoxMenuItem result = (JCheckBoxMenuItem) (_logTableColumnMenuItems.get(column));
+ if (result == null) {
+ result = createLogTableColumnMenuItem(column);
+ _logTableColumnMenuItems.put(column, result);
+ }
+ return result;
+ }
+
+ protected JCheckBoxMenuItem createLogTableColumnMenuItem(LogTableColumn column) {
+ JCheckBoxMenuItem result = new JCheckBoxMenuItem(column.toString());
+
+ result.setSelected(true);
+ result.setMnemonic(column.toString().charAt(0));
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ // update list of columns and reset the view
+ List selectedColumns = updateView();
+ _table.setView(selectedColumns);
+ }
+ });
+ return result;
+ }
+
+ protected List updateView() {
+ ArrayList updatedList = new ArrayList();
+ Iterator columnIterator = _columns.iterator();
+ while (columnIterator.hasNext()) {
+ LogTableColumn column = (LogTableColumn) columnIterator.next();
+ JCheckBoxMenuItem result = getLogTableColumnMenuItem(column);
+ // check and see if the checkbox is checked
+ if (result.isSelected()) {
+ updatedList.add(column);
+ }
+ }
+
+ return updatedList;
+ }
+
+ protected JMenuItem createAllLogTableColumnsMenuItem() {
+ JMenuItem result = new JMenuItem("Show all Columns");
+ result.setMnemonic('s');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ selectAllLogTableColumns(true);
+ // update list of columns and reset the view
+ List selectedColumns = updateView();
+ _table.setView(selectedColumns);
+ }
+ });
+ return result;
+ }
+
+ protected JMenuItem createNoLogTableColumnsMenuItem() {
+ JMenuItem result = new JMenuItem("Hide all Columns");
+ result.setMnemonic('h');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ selectAllLogTableColumns(false);
+ // update list of columns and reset the view
+ List selectedColumns = updateView();
+ _table.setView(selectedColumns);
+ }
+ });
+ return result;
+ }
+
+ protected void selectAllLogTableColumns(boolean selected) {
+ Iterator columns = getLogTableColumns();
+ while (columns.hasNext()) {
+ getLogTableColumnMenuItem((LogTableColumn) columns.next()).setSelected(selected);
+ }
+ }
+
+ protected JMenu createFileMenu() {
+ JMenu fileMenu = new JMenu("File");
+ fileMenu.setMnemonic('f');
+ JMenuItem exitMI;
+ fileMenu.add(createOpenMI());
+ fileMenu.add(createOpenURLMI());
+ fileMenu.addSeparator();
+ fileMenu.add(createCloseMI());
+ createMRUFileListMI(fileMenu);
+ fileMenu.addSeparator();
+ fileMenu.add(createExitMI());
+ return fileMenu;
+ }
+
+ /**
+ * Menu item added to allow log files to be opened with
+ * the LF5 GUI.
+ */
+ protected JMenuItem createOpenMI() {
+ JMenuItem result = new JMenuItem("Open...");
+ result.setMnemonic('o');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ requestOpen();
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Menu item added to allow log files loaded from a URL
+ * to be opened by the LF5 GUI.
+ */
+ protected JMenuItem createOpenURLMI() {
+ JMenuItem result = new JMenuItem("Open URL...");
+ result.setMnemonic('u');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ requestOpenURL();
+ }
+ });
+ return result;
+ }
+
+ protected JMenuItem createCloseMI() {
+ JMenuItem result = new JMenuItem("Close");
+ result.setMnemonic('c');
+ result.setAccelerator(KeyStroke.getKeyStroke("control Q"));
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ requestClose();
+ }
+ });
+ return result;
+ }
+
+ /**
+ * Creates a Most Recently Used file list to be
+ * displayed in the File menu
+ */
+ protected void createMRUFileListMI(JMenu menu) {
+
+ String[] files = _mruFileManager.getMRUFileList();
+
+ if (files != null) {
+ menu.addSeparator();
+ for (int i = 0; i < files.length; i++) {
+ JMenuItem result = new JMenuItem((i + 1) + " " + files[i]);
+ result.setMnemonic(i + 1);
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ requestOpenMRU(e);
+ }
+ });
+ menu.add(result);
+ }
+ }
+ }
+
+ protected JMenuItem createExitMI() {
+ JMenuItem result = new JMenuItem("Exit");
+ result.setMnemonic('x');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ requestExit();
+ }
+ });
+ return result;
+ }
+
+ protected JMenu createConfigureMenu() {
+ JMenu configureMenu = new JMenu("Configure");
+ configureMenu.setMnemonic('c');
+ configureMenu.add(createConfigureSave());
+ configureMenu.add(createConfigureReset());
+ configureMenu.add(createConfigureMaxRecords());
+
+ return configureMenu;
+ }
+
+ protected JMenuItem createConfigureSave() {
+ JMenuItem result = new JMenuItem("Save");
+ result.setMnemonic('s');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ saveConfiguration();
+ }
+ });
+
+ return result;
+ }
+
+ protected JMenuItem createConfigureReset() {
+ JMenuItem result = new JMenuItem("Reset");
+ result.setMnemonic('r');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ resetConfiguration();
+ }
+ });
+
+ return result;
+ }
+
+ protected JMenuItem createConfigureMaxRecords() {
+ JMenuItem result = new JMenuItem("Set Max Number of Records");
+ result.setMnemonic('m');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ setMaxRecordConfiguration();
+ }
+ });
+
+ return result;
+ }
+
+
+ protected void saveConfiguration() {
+ _configurationManager.save();
+ }
+
+ protected void resetConfiguration() {
+ _configurationManager.reset();
+ }
+
+ protected void setMaxRecordConfiguration() {
+ LogFactor5InputDialog inputDialog = new LogFactor5InputDialog(
+ getBaseFrame(), "Set Max Number of Records", "", 10);
+
+ String temp = inputDialog.getText();
+
+ if (temp != null) {
+ try {
+ setMaxNumberOfLogRecords(Integer.parseInt(temp));
+ } catch (NumberFormatException e) {
+ LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+ getBaseFrame(),
+ "'" + temp + "' is an invalid parameter.\nPlease try again.");
+ setMaxRecordConfiguration();
+ }
+ }
+ }
+
+
+ protected JMenu createHelpMenu() {
+ JMenu helpMenu = new JMenu("Help");
+ helpMenu.setMnemonic('h');
+ helpMenu.add(createHelpProperties());
+ return helpMenu;
+ }
+
+ protected JMenuItem createHelpProperties() {
+ final String title = "LogFactor5 Properties";
+ final JMenuItem result = new JMenuItem(title);
+ result.setMnemonic('l');
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ showPropertiesDialog(title);
+ }
+ });
+ return result;
+ }
+
+ protected void showPropertiesDialog(String title) {
+ JOptionPane.showMessageDialog(
+ _logMonitorFrame,
+ _displayedLogBrokerProperties.toArray(),
+ title,
+ JOptionPane.PLAIN_MESSAGE
+ );
+ }
+
+ protected JMenu createEditMenu() {
+ JMenu editMenu = new JMenu("Edit");
+ editMenu.setMnemonic('e');
+ editMenu.add(createEditFindMI());
+ editMenu.add(createEditFindNextMI());
+ editMenu.addSeparator();
+ editMenu.add(createEditSortNDCMI());
+ editMenu.add(createEditRestoreAllNDCMI());
+ return editMenu;
+ }
+
+ protected JMenuItem createEditFindNextMI() {
+ JMenuItem editFindNextMI = new JMenuItem("Find Next");
+ editFindNextMI.setMnemonic('n');
+ editFindNextMI.setAccelerator(KeyStroke.getKeyStroke("F3"));
+ editFindNextMI.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ findSearchText();
+ }
+ });
+ return editFindNextMI;
+ }
+
+ protected JMenuItem createEditFindMI() {
+ JMenuItem editFindMI = new JMenuItem("Find");
+ editFindMI.setMnemonic('f');
+ editFindMI.setAccelerator(KeyStroke.getKeyStroke("control F"));
+
+ editFindMI.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ String inputValue =
+ JOptionPane.showInputDialog(
+ _logMonitorFrame,
+ "Find text: ",
+ "Search Record Messages",
+ JOptionPane.QUESTION_MESSAGE
+ );
+ setSearchText(inputValue);
+ findSearchText();
+ }
+ }
+
+ );
+ return editFindMI;
+ }
+
+ // Added version 1.2 - Allows users to Sort Log Records by an
+ // NDC text filter. A new LogRecordFilter was created to
+ // sort the records.
+ protected JMenuItem createEditSortNDCMI() {
+ JMenuItem editSortNDCMI = new JMenuItem("Sort by NDC");
+ editSortNDCMI.setMnemonic('s');
+ editSortNDCMI.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ String inputValue =
+ JOptionPane.showInputDialog(
+ _logMonitorFrame,
+ "Sort by this NDC: ",
+ "Sort Log Records by NDC",
+ JOptionPane.QUESTION_MESSAGE
+ );
+ setNDCTextFilter(inputValue);
+ sortByNDC();
+ _table.getFilteredLogTableModel().refresh();
+ updateStatusLabel();
+ }
+ }
+
+ );
+ return editSortNDCMI;
+ }
+
+ // Added in version 1.2 - Resets the LogRecordFilter back to default
+ // filter.
+ protected JMenuItem createEditRestoreAllNDCMI() {
+ JMenuItem editRestoreAllNDCMI = new JMenuItem("Restore all NDCs");
+ editRestoreAllNDCMI.setMnemonic('r');
+ editRestoreAllNDCMI.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _table.getFilteredLogTableModel().setLogRecordFilter(createLogRecordFilter());
+ // reset the text filter
+ setNDCTextFilter("");
+ _table.getFilteredLogTableModel().refresh();
+ updateStatusLabel();
+ }
+ }
+ );
+ return editRestoreAllNDCMI;
+ }
+
+ protected JToolBar createToolBar() {
+ JToolBar tb = new JToolBar();
+ tb.putClientProperty("JToolBar.isRollover", Boolean.TRUE);
+ JComboBox fontCombo = new JComboBox();
+ JComboBox fontSizeCombo = new JComboBox();
+ _fontSizeCombo = fontSizeCombo;
+
+ ClassLoader cl = this.getClass().getClassLoader();
+ if(cl == null) {
+ cl = ClassLoader.getSystemClassLoader();
+ }
+ URL newIconURL = cl.getResource("org/apache/log4j/lf5/viewer/" +
+ "images/channelexplorer_new.gif");
+
+ ImageIcon newIcon = null;
+
+ if (newIconURL != null) {
+ newIcon = new ImageIcon(newIconURL);
+ }
+
+ JButton newButton = new JButton("Clear Log Table");
+
+ if (newIcon != null) {
+ newButton.setIcon(newIcon);
+ }
+
+ newButton.setToolTipText("Clear Log Table.");
+ //newButton.setBorder(BorderFactory.createEtchedBorder());
+
+ newButton.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _table.clearLogRecords();
+ _categoryExplorerTree.getExplorerModel().resetAllNodeCounts();
+ updateStatusLabel();
+ clearDetailTextArea();
+ LogRecord.resetSequenceNumber();
+ }
+ }
+ );
+
+ Toolkit tk = Toolkit.getDefaultToolkit();
+ // This will actually grab all the fonts
+
+ String[] fonts;
+
+ if (_loadSystemFonts) {
+ fonts = GraphicsEnvironment.
+ getLocalGraphicsEnvironment().getAvailableFontFamilyNames();
+ } else {
+ fonts = tk.getFontList();
+ }
+
+ for (int j = 0; j < fonts.length; j++) {
+ fontCombo.addItem(fonts[j]);
+ }
+
+ fontCombo.setSelectedItem(_fontName);
+
+ fontCombo.addActionListener(
+
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ JComboBox box = (JComboBox) e.getSource();
+ String font = (String) box.getSelectedItem();
+ _table.setFont(new Font(font, Font.PLAIN, _fontSize));
+ _fontName = font;
+ }
+ }
+ );
+
+ fontSizeCombo.addItem("8");
+ fontSizeCombo.addItem("9");
+ fontSizeCombo.addItem("10");
+ fontSizeCombo.addItem("12");
+ fontSizeCombo.addItem("14");
+ fontSizeCombo.addItem("16");
+ fontSizeCombo.addItem("18");
+ fontSizeCombo.addItem("24");
+
+ fontSizeCombo.setSelectedItem(String.valueOf(_fontSize));
+ fontSizeCombo.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ JComboBox box = (JComboBox) e.getSource();
+ String size = (String) box.getSelectedItem();
+ int s = Integer.valueOf(size).intValue();
+
+ setFontSizeSilently(s);
+ refreshDetailTextArea();
+ _fontSize = s;
+ }
+ }
+ );
+
+ tb.add(new JLabel(" Font: "));
+ tb.add(fontCombo);
+ tb.add(fontSizeCombo);
+ tb.addSeparator();
+ tb.addSeparator();
+ tb.add(newButton);
+
+ newButton.setAlignmentY(0.5f);
+ newButton.setAlignmentX(0.5f);
+
+ fontCombo.setMaximumSize(fontCombo.getPreferredSize());
+ fontSizeCombo.setMaximumSize(
+ fontSizeCombo.getPreferredSize());
+
+ return (tb);
+ }
+
+// protected void setView(String viewString, LogTable table) {
+// if (STANDARD_VIEW.equals(viewString)) {
+// table.setStandardView();
+// } else if (COMPACT_VIEW.equals(viewString)) {
+// table.setCompactView();
+// } else if (DETAILED_VIEW.equals(viewString)) {
+// table.setDetailedView();
+// } else {
+// String message = viewString + "does not match a supported view.";
+// throw new IllegalArgumentException(message);
+// }
+// _currentView = viewString;
+// }
+
+ protected void setView(String viewString, LogTable table) {
+ if (DETAILED_VIEW.equals(viewString)) {
+ table.setDetailedView();
+ } else {
+ String message = viewString + "does not match a supported view.";
+ throw new IllegalArgumentException(message);
+ }
+ _currentView = viewString;
+ }
+
+ protected JComboBox createLogLevelCombo() {
+ JComboBox result = new JComboBox();
+ Iterator levels = getLogLevels();
+ while (levels.hasNext()) {
+ result.addItem(levels.next());
+ }
+ result.setSelectedItem(_leastSevereDisplayedLogLevel);
+
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ JComboBox box = (JComboBox) e.getSource();
+ LogLevel level = (LogLevel) box.getSelectedItem();
+ setLeastSevereDisplayedLogLevel(level);
+ }
+ });
+ result.setMaximumSize(result.getPreferredSize());
+ return result;
+ }
+
+ protected void setLeastSevereDisplayedLogLevel(LogLevel level) {
+ if (level == null || _leastSevereDisplayedLogLevel == level) {
+ return; // nothing to do
+ }
+ _leastSevereDisplayedLogLevel = level;
+ _table.getFilteredLogTableModel().refresh();
+ updateStatusLabel();
+ }
+
+ /**
+ * Ensures that the Table's ScrollPane Viewport will "track" with updates
+ * to the Table. When the vertical scroll bar is at its bottom anchor
+ * and tracking is enabled then viewport will stay at the bottom most
+ * point of the component. The purpose of this feature is to allow
+ * a developer to watch the table as messages arrive and not have to
+ * scroll after each new message arrives. When the vertical scroll bar
+ * is at any other location, then no tracking will happen.
+ * @deprecated tracking is now done automatically.
+ */
+ protected void trackTableScrollPane() {
+ // do nothing
+ }
+
+ protected void centerFrame(JFrame frame) {
+ Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
+ Dimension comp = frame.getSize();
+
+ frame.setLocation(((screen.width - comp.width) / 2),
+ ((screen.height - comp.height) / 2));
+
+ }
+
+ /**
+ * Uses a JFileChooser to select a file to opened with the
+ * LF5 GUI.
+ */
+ protected void requestOpen() {
+ JFileChooser chooser;
+
+ if (_fileLocation == null) {
+ chooser = new JFileChooser();
+ } else {
+ chooser = new JFileChooser(_fileLocation);
+ }
+
+ int returnVal = chooser.showOpenDialog(_logMonitorFrame);
+ if (returnVal == JFileChooser.APPROVE_OPTION) {
+ File f = chooser.getSelectedFile();
+ if (loadLogFile(f)) {
+ _fileLocation = chooser.getSelectedFile();
+ _mruFileManager.set(f);
+ updateMRUList();
+ }
+ }
+ }
+
+ /**
+ * Uses a Dialog box to accept a URL to a file to be opened
+ * with the LF5 GUI.
+ */
+ protected void requestOpenURL() {
+ LogFactor5InputDialog inputDialog = new LogFactor5InputDialog(
+ getBaseFrame(), "Open URL", "URL:");
+ String temp = inputDialog.getText();
+
+ if (temp != null) {
+ if (temp.indexOf("://") == -1) {
+ temp = "http://" + temp;
+ }
+
+ try {
+ URL url = new URL(temp);
+ if (loadLogFile(url)) {
+ _mruFileManager.set(url);
+ updateMRUList();
+ }
+ } catch (MalformedURLException e) {
+ LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+ getBaseFrame(), "Error reading URL.");
+ }
+ }
+ }
+
+ /**
+ * Removes old file list and creates a new file list
+ * with the updated MRU list.
+ */
+ protected void updateMRUList() {
+ JMenu menu = _logMonitorFrame.getJMenuBar().getMenu(0);
+ menu.removeAll();
+ menu.add(createOpenMI());
+ menu.add(createOpenURLMI());
+ menu.addSeparator();
+ menu.add(createCloseMI());
+ createMRUFileListMI(menu);
+ menu.addSeparator();
+ menu.add(createExitMI());
+ }
+
+ protected void requestClose() {
+ setCallSystemExitOnClose(false);
+ closeAfterConfirm();
+ }
+
+ /**
+ * Opens a file in the MRU list.
+ */
+ protected void requestOpenMRU(ActionEvent e) {
+ String file = e.getActionCommand();
+ StringTokenizer st = new StringTokenizer(file);
+ String num = st.nextToken().trim();
+ file = st.nextToken("\n");
+
+ try {
+ int index = Integer.parseInt(num) - 1;
+
+ InputStream in = _mruFileManager.getInputStream(index);
+ LogFileParser lfp = new LogFileParser(in);
+ lfp.parse(this);
+
+ _mruFileManager.moveToTop(index);
+ updateMRUList();
+
+ } catch (Exception me) {
+ LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+ getBaseFrame(), "Unable to load file " + file);
+ }
+
+ }
+
+ protected void requestExit() {
+ _mruFileManager.save();
+ setCallSystemExitOnClose(true);
+ closeAfterConfirm();
+ }
+
+ protected void closeAfterConfirm() {
+ StringBuffer message = new StringBuffer();
+
+ if (_callSystemExitOnClose == false) {
+ message.append("Are you sure you want to close the logging ");
+ message.append("console?\n");
+ message.append("(Note: This will not shut down the Virtual Machine,\n");
+ message.append("or the Swing event thread.)");
+ } else {
+ message.append("Are you sure you want to exit?\n");
+ message.append("This will shut down the Virtual Machine.\n");
+ }
+
+ String title =
+ "Are you sure you want to dispose of the Logging Console?";
+
+ if (_callSystemExitOnClose == true) {
+ title = "Are you sure you want to exit?";
+ }
+ int value = JOptionPane.showConfirmDialog(
+ _logMonitorFrame,
+ message.toString(),
+ title,
+ JOptionPane.OK_CANCEL_OPTION,
+ JOptionPane.QUESTION_MESSAGE,
+ null
+ );
+
+ if (value == JOptionPane.OK_OPTION) {
+ dispose();
+ }
+ }
+
+ protected Iterator getLogLevels() {
+ return _levels.iterator();
+ }
+
+ protected Iterator getLogTableColumns() {
+ return _columns.iterator();
+ }
+
+ /**
+ * Loads and parses a log file.
+ */
+ protected boolean loadLogFile(File file) {
+ boolean ok = false;
+ try {
+ LogFileParser lfp = new LogFileParser(file);
+ lfp.parse(this);
+ ok = true;
+ } catch (IOException e) {
+ LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+ getBaseFrame(), "Error reading " + file.getName());
+ }
+
+ return ok;
+ }
+
+ /**
+ * Loads a parses a log file running on a server.
+ */
+ protected boolean loadLogFile(URL url) {
+ boolean ok = false;
+ try {
+ LogFileParser lfp = new LogFileParser(url.openStream());
+ lfp.parse(this);
+ ok = true;
+ } catch (IOException e) {
+ LogFactor5ErrorDialog error = new LogFactor5ErrorDialog(
+ getBaseFrame(), "Error reading URL:" + url.getFile());
+ }
+ return ok;
+ }
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+ class LogBrokerMonitorWindowAdaptor extends WindowAdapter {
+ protected LogBrokerMonitor _monitor;
+
+ public LogBrokerMonitorWindowAdaptor(LogBrokerMonitor monitor) {
+ _monitor = monitor;
+ }
+
+ public void windowClosing(WindowEvent ev) {
+ _monitor.requestClose();
+ }
+ }
+}
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java b/java/src/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java
new file mode 100644
index 0000000..763d870
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LogFactor5Dialog.java
@@ -0,0 +1,151 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import java.awt.Component;
+import java.awt.Container;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.GridBagConstraints;
+import java.awt.GridBagLayout;
+import java.awt.Insets;
+import java.awt.Label;
+import java.awt.Toolkit;
+import java.awt.Window;
+
+import javax.swing.JDialog;
+import javax.swing.JFrame;
+
+/**
+ * LogFactor5Dialog
+ *
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public abstract class LogFactor5Dialog extends JDialog {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+ protected static final Font DISPLAY_FONT = new Font("Arial", Font.BOLD, 12);
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ protected LogFactor5Dialog(JFrame jframe, String message, boolean modal) {
+ super(jframe, message, modal);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+ public void show() {
+ pack();
+ minimumSizeDialog(this, 200, 100);
+ centerWindow(this);
+ super.show();
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+ protected void centerWindow(Window win) {
+ Dimension screenDim = Toolkit.getDefaultToolkit().getScreenSize();
+
+ // If larger than screen, reduce window width or height
+ if (screenDim.width < win.getSize().width) {
+ win.setSize(screenDim.width, win.getSize().height);
+ }
+
+ if (screenDim.height < win.getSize().height) {
+ win.setSize(win.getSize().width, screenDim.height);
+ }
+
+ // Center Frame, Dialogue or Window on screen
+ int x = (screenDim.width - win.getSize().width) / 2;
+ int y = (screenDim.height - win.getSize().height) / 2;
+ win.setLocation(x, y);
+ }
+
+ protected void wrapStringOnPanel(String message,
+ Container container) {
+ GridBagConstraints c = getDefaultConstraints();
+ c.gridwidth = GridBagConstraints.REMAINDER;
+ // Insets() args are top, left, bottom, right
+ c.insets = new Insets(0, 0, 0, 0);
+ GridBagLayout gbLayout = (GridBagLayout) container.getLayout();
+
+
+ while (message.length() > 0) {
+ int newLineIndex = message.indexOf('\n');
+ String line;
+ if (newLineIndex >= 0) {
+ line = message.substring(0, newLineIndex);
+ message = message.substring(newLineIndex + 1);
+ } else {
+ line = message;
+ message = "";
+ }
+ Label label = new Label(line);
+ label.setFont(DISPLAY_FONT);
+ gbLayout.setConstraints(label, c);
+ container.add(label);
+ }
+ }
+
+ protected GridBagConstraints getDefaultConstraints() {
+ GridBagConstraints constraints = new GridBagConstraints();
+ constraints.weightx = 1.0;
+ constraints.weighty = 1.0;
+ constraints.gridheight = 1; // One row high
+ // Insets() args are top, left, bottom, right
+ constraints.insets = new Insets(4, 4, 4, 4);
+ // fill of NONE means do not change size
+ constraints.fill = GridBagConstraints.NONE;
+ // WEST means align left
+ constraints.anchor = GridBagConstraints.WEST;
+
+ return constraints;
+ }
+
+ protected void minimumSizeDialog(Component component,
+ int minWidth,
+ int minHeight) {
+ // set the min width
+ if (component.getSize().width < minWidth)
+ component.setSize(minWidth, component.getSize().height);
+ // set the min height
+ if (component.getSize().height < minHeight)
+ component.setSize(component.getSize().width, minHeight);
+ }
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java b/java/src/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java
new file mode 100644
index 0000000..64b2e21
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LogFactor5ErrorDialog.java
@@ -0,0 +1,92 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.GridBagLayout;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+
+import javax.swing.JButton;
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+/**
+ * LogFactor5ErrorDialog
+ *
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogFactor5ErrorDialog extends LogFactor5Dialog {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ public LogFactor5ErrorDialog(JFrame jframe, String message) {
+ super(jframe, "Error", true);
+
+ JButton ok = new JButton("Ok");
+ ok.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ hide();
+ }
+ });
+
+ JPanel bottom = new JPanel();
+ bottom.setLayout(new FlowLayout());
+ bottom.add(ok);
+
+ JPanel main = new JPanel();
+ main.setLayout(new GridBagLayout());
+ wrapStringOnPanel(message, main);
+
+ getContentPane().add(main, BorderLayout.CENTER);
+ getContentPane().add(bottom, BorderLayout.SOUTH);
+ show();
+
+ }
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java b/java/src/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java
new file mode 100644
index 0000000..890e6db
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LogFactor5InputDialog.java
@@ -0,0 +1,145 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import javax.swing.*;
+import java.awt.*;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.KeyAdapter;
+import java.awt.event.KeyEvent;
+
+/**
+ * LogFactor5InputDialog
+ *
+ * Creates a popup input dialog box so that users can enter
+ * a URL to open a log file from.
+ *
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogFactor5InputDialog extends LogFactor5Dialog {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+ public static final int SIZE = 30;
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+ private JTextField _textField;
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Configures an input dialog box using a defualt size for the text field.
+ * param jframe the frame where the dialog will be loaded from.
+ * param title the title of the dialog box.
+ * param label the label to be put in the dialog box.
+ */
+ public LogFactor5InputDialog(JFrame jframe, String title, String label) {
+ this(jframe, title, label, SIZE);
+ }
+
+ /**
+ * Configures an input dialog box.
+ * param jframe the frame where the dialog will be loaded from.
+ * param title the title of the dialog box.
+ * param label the label to be put in the dialog box.
+ * param size the size of the text field.
+ */
+ public LogFactor5InputDialog(JFrame jframe, String title, String label,
+ int size) {
+ super(jframe, title, true);
+
+ JPanel bottom = new JPanel();
+ bottom.setLayout(new FlowLayout());
+
+ JPanel main = new JPanel();
+ main.setLayout(new FlowLayout());
+ main.add(new JLabel(label));
+ _textField = new JTextField(size);
+ main.add(_textField);
+
+ addKeyListener(new KeyAdapter() {
+ public void keyPressed(KeyEvent e) {
+ if (e.getKeyCode() == KeyEvent.VK_ENTER) {
+ hide();
+ }
+ }
+ });
+
+ JButton ok = new JButton("Ok");
+ ok.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ hide();
+ }
+ });
+
+ JButton cancel = new JButton("Cancel");
+ cancel.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ hide();
+ // set the text field to blank just in case
+ // a file was selected before the Cancel
+ // button was pressed.
+ _textField.setText("");
+ }
+ });
+
+ bottom.add(ok);
+ bottom.add(cancel);
+ getContentPane().add(main, BorderLayout.CENTER);
+ getContentPane().add(bottom, BorderLayout.SOUTH);
+ pack();
+ centerWindow(this);
+ show();
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+ public String getText() {
+ String s = _textField.getText();
+
+ if (s != null && s.trim().length() == 0) {
+ return null;
+ }
+
+ return s;
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java b/java/src/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java
new file mode 100644
index 0000000..3e5a62b
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LogFactor5LoadingDialog.java
@@ -0,0 +1,82 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import java.awt.BorderLayout;
+import java.awt.FlowLayout;
+import java.awt.GridBagLayout;
+
+import javax.swing.JFrame;
+import javax.swing.JPanel;
+
+/**
+ * LogFactor5LoadingDialog
+ *
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogFactor5LoadingDialog extends LogFactor5Dialog {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public LogFactor5LoadingDialog(JFrame jframe, String message) {
+ super(jframe, "LogFactor5", false);
+
+ JPanel bottom = new JPanel();
+ bottom.setLayout(new FlowLayout());
+
+ JPanel main = new JPanel();
+ main.setLayout(new GridBagLayout());
+ wrapStringOnPanel(message, main);
+
+ getContentPane().add(main, BorderLayout.CENTER);
+ getContentPane().add(bottom, BorderLayout.SOUTH);
+ show();
+
+ }
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/lf5/viewer/LogTable.java b/java/src/org/apache/log4j/lf5/viewer/LogTable.java
new file mode 100644
index 0000000..6bea10d
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LogTable.java
@@ -0,0 +1,277 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import java.awt.Font;
+import java.awt.FontMetrics;
+import java.awt.Graphics;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Vector;
+
+import javax.swing.JTable;
+import javax.swing.JTextArea;
+import javax.swing.ListSelectionModel;
+import javax.swing.event.ListSelectionEvent;
+import javax.swing.event.ListSelectionListener;
+import javax.swing.table.TableColumn;
+import javax.swing.table.TableColumnModel;
+
+import org.apache.log4j.lf5.util.DateFormatManager;
+
+/**
+ * LogTable.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brad Marlborough
+ * @author Brent Sprecher
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogTable extends JTable {
+ private static final long serialVersionUID = 4867085140195148458L;
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected int _rowHeight = 30;
+ protected JTextArea _detailTextArea;
+
+ // For the columns:
+ protected int _numCols = 9;
+ protected TableColumn[] _tableColumns = new TableColumn[_numCols];
+ protected int[] _colWidths = {40, 40, 40, 70, 70, 360, 440, 200, 60};
+ protected LogTableColumn[] _colNames = LogTableColumn.getLogTableColumnArray();
+ protected int _colDate = 0;
+ protected int _colThread = 1;
+ protected int _colMessageNum = 2;
+ protected int _colLevel = 3;
+ protected int _colNDC = 4;
+ protected int _colCategory = 5;
+ protected int _colMessage = 6;
+ protected int _colLocation = 7;
+ protected int _colThrown = 8;
+
+ protected DateFormatManager _dateFormatManager = null;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public LogTable(JTextArea detailTextArea) {
+ super();
+
+ init();
+
+ _detailTextArea = detailTextArea;
+
+ setModel(new FilteredLogTableModel());
+
+ Enumeration columns = getColumnModel().getColumns();
+ int i = 0;
+ while (columns.hasMoreElements()) {
+ TableColumn col = (TableColumn) columns.nextElement();
+ col.setCellRenderer(new LogTableRowRenderer());
+ col.setPreferredWidth(_colWidths[i]);
+
+ _tableColumns[i] = col;
+ i++;
+ }
+
+ ListSelectionModel rowSM = getSelectionModel();
+ rowSM.addListSelectionListener(new LogTableListSelectionListener(this));
+
+ //setAutoResizeMode(JTable.AUTO_RESIZE_ALL_COLUMNS);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Get the DateFormatManager for formatting dates.
+ */
+ public DateFormatManager getDateFormatManager() {
+ return _dateFormatManager;
+ }
+
+ /**
+ * Set the date format manager for formatting dates.
+ */
+ public void setDateFormatManager(DateFormatManager dfm) {
+ _dateFormatManager = dfm;
+ }
+
+ public synchronized void clearLogRecords() {
+ //For JDK1.3
+ //((DefaultTableModel)getModel()).setRowCount(0);
+
+ // For JDK1.2.x
+ getFilteredLogTableModel().clear();
+ }
+
+ public FilteredLogTableModel getFilteredLogTableModel() {
+ return (FilteredLogTableModel) getModel();
+ }
+
+ // default view if a view is not set and saved
+ public void setDetailedView() {
+ //TODO: Defineable Views.
+ TableColumnModel model = getColumnModel();
+ // Remove all the columns:
+ for (int f = 0; f < _numCols; f++) {
+ model.removeColumn(_tableColumns[f]);
+ }
+ // Add them back in the correct order:
+ for (int i = 0; i < _numCols; i++) {
+ model.addColumn(_tableColumns[i]);
+ }
+ //SWING BUG:
+ sizeColumnsToFit(-1);
+ }
+
+ public void setView(List columns) {
+ TableColumnModel model = getColumnModel();
+
+ // Remove all the columns:
+ for (int f = 0; f < _numCols; f++) {
+ model.removeColumn(_tableColumns[f]);
+ }
+ Iterator selectedColumns = columns.iterator();
+ Vector columnNameAndNumber = getColumnNameAndNumber();
+ while (selectedColumns.hasNext()) {
+ // add the column to the view
+ model.addColumn(_tableColumns[columnNameAndNumber.indexOf(selectedColumns.next())]);
+ }
+
+ //SWING BUG:
+ sizeColumnsToFit(-1);
+ }
+
+ public void setFont(Font font) {
+ super.setFont(font);
+ Graphics g = this.getGraphics();
+ if (g != null) {
+ FontMetrics fm = g.getFontMetrics(font);
+ int height = fm.getHeight();
+ _rowHeight = height + height / 3;
+ setRowHeight(_rowHeight);
+ }
+
+
+ }
+
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected void init() {
+ setRowHeight(_rowHeight);
+ setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
+ }
+
+ // assign a column number to a column name
+ protected Vector getColumnNameAndNumber() {
+ Vector columnNameAndNumber = new Vector();
+ for (int i = 0; i < _colNames.length; i++) {
+ columnNameAndNumber.add(i, _colNames[i]);
+ }
+ return columnNameAndNumber;
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+ class LogTableListSelectionListener implements ListSelectionListener {
+ protected JTable _table;
+
+ public LogTableListSelectionListener(JTable table) {
+ _table = table;
+ }
+
+ public void valueChanged(ListSelectionEvent e) {
+ //Ignore extra messages.
+ if (e.getValueIsAdjusting()) {
+ return;
+ }
+
+ ListSelectionModel lsm = (ListSelectionModel) e.getSource();
+ if (lsm.isSelectionEmpty()) {
+ //no rows are selected
+ } else {
+ StringBuffer buf = new StringBuffer();
+ int selectedRow = lsm.getMinSelectionIndex();
+
+ for (int i = 0; i < _numCols - 1; i++) {
+ String value = "";
+ Object obj = _table.getModel().getValueAt(selectedRow, i);
+ if (obj != null) {
+ value = obj.toString();
+ }
+
+ buf.append(_colNames[i] + ":");
+ buf.append("\t");
+
+ if (i == _colThread || i == _colMessage || i == _colLevel) {
+ buf.append("\t"); // pad out the date.
+ }
+
+ if (i == _colDate || i == _colNDC) {
+ buf.append("\t\t"); // pad out the date.
+ }
+
+// if( i == _colSequence)
+// {
+// buf.append("\t\t\t"); // pad out the Sequnce.
+// }
+
+ buf.append(value);
+ buf.append("\n");
+ }
+ buf.append(_colNames[_numCols - 1] + ":\n");
+ Object obj = _table.getModel().getValueAt(selectedRow, _numCols - 1);
+ if (obj != null) {
+ buf.append(obj.toString());
+ }
+
+ _detailTextArea.setText(buf.toString());
+ }
+ }
+ }
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/LogTableColumn.java b/java/src/org/apache/log4j/lf5/viewer/LogTableColumn.java
new file mode 100644
index 0000000..c86c6bb
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LogTableColumn.java
@@ -0,0 +1,166 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * LogTableColumn
+ *
+ * @author Michael J. Sikorsky
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogTableColumn implements java.io.Serializable {
+ private static final long serialVersionUID = -4275827753626456547L;
+
+ // log4j table columns.
+ public final static LogTableColumn DATE = new LogTableColumn("Date");
+ public final static LogTableColumn THREAD = new LogTableColumn("Thread");
+ public final static LogTableColumn MESSAGE_NUM = new LogTableColumn("Message #");
+ public final static LogTableColumn LEVEL = new LogTableColumn("Level");
+ public final static LogTableColumn NDC = new LogTableColumn("NDC");
+ public final static LogTableColumn CATEGORY = new LogTableColumn("Category");
+ public final static LogTableColumn MESSAGE = new LogTableColumn("Message");
+ public final static LogTableColumn LOCATION = new LogTableColumn("Location");
+ public final static LogTableColumn THROWN = new LogTableColumn("Thrown");
+
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected String _label;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+ private static LogTableColumn[] _log4JColumns;
+ private static Map _logTableColumnMap;
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ static {
+ _log4JColumns = new LogTableColumn[]{DATE, THREAD, MESSAGE_NUM, LEVEL, NDC, CATEGORY,
+ MESSAGE, LOCATION, THROWN};
+
+ _logTableColumnMap = new HashMap();
+
+ for (int i = 0; i < _log4JColumns.length; i++) {
+ _logTableColumnMap.put(_log4JColumns[i].getLabel(), _log4JColumns[i]);
+ }
+ }
+
+
+ public LogTableColumn(String label) {
+ _label = label;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Return the Label of the LogLevel.
+ */
+ public String getLabel() {
+ return _label;
+ }
+
+ /**
+ * Convert a column label into a LogTableColumn object.
+ *
+ * @param column The label of a level to be converted into a LogTableColumn.
+ * @return LogTableColumn The LogTableColumn with a label equal to column.
+ * @throws LogTableColumnFormatException Is thrown when the column can not be
+ * converted into a LogTableColumn.
+ */
+ public static LogTableColumn valueOf(String column)
+ throws LogTableColumnFormatException {
+ LogTableColumn tableColumn = null;
+ if (column != null) {
+ column = column.trim();
+ tableColumn = (LogTableColumn) _logTableColumnMap.get(column);
+ }
+
+ if (tableColumn == null) {
+ StringBuffer buf = new StringBuffer();
+ buf.append("Error while trying to parse (" + column + ") into");
+ buf.append(" a LogTableColumn.");
+ throw new LogTableColumnFormatException(buf.toString());
+ }
+ return tableColumn;
+ }
+
+
+ public boolean equals(Object o) {
+ boolean equals = false;
+
+ if (o instanceof LogTableColumn) {
+ if (this.getLabel() ==
+ ((LogTableColumn) o).getLabel()) {
+ equals = true;
+ }
+ }
+
+ return equals;
+ }
+
+ public int hashCode() {
+ return _label.hashCode();
+ }
+
+ public String toString() {
+ return _label;
+ }
+
+ /**
+ * @return A List of LogTableColumn/code> objects that map
+ * to log4j Column objects.
+ */
+ public static List getLogTableColumns() {
+ return Arrays.asList(_log4JColumns);
+ }
+
+ public static LogTableColumn[] getLogTableColumnArray() {
+ return _log4JColumns;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java b/java/src/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java
new file mode 100644
index 0000000..b161fe7
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LogTableColumnFormatException.java
@@ -0,0 +1,74 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+/**
+ * Thrown to indicate that the client has attempted to convert a string
+ * to one the LogLevel types, but the string does not have the appropriate
+ * format.
+ *
+ * @author Michael J. Sikorsky
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogTableColumnFormatException extends Exception {
+ private static final long serialVersionUID = 6529165785030431653L;
+
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public LogTableColumnFormatException(String message) {
+ super(message);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/LogTableModel.java b/java/src/org/apache/log4j/lf5/viewer/LogTableModel.java
new file mode 100644
index 0000000..3b60000
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LogTableModel.java
@@ -0,0 +1,77 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import javax.swing.table.DefaultTableModel;
+
+/**
+ * LogTableModel
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogTableModel extends DefaultTableModel {
+ private static final long serialVersionUID = 3593300685868700894L;
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public LogTableModel(Object[] colNames, int numRows) {
+ super(colNames, numRows);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public boolean isCellEditable(int row, int column) {
+ return (false);
+ }
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java b/java/src/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java
new file mode 100644
index 0000000..3d1e397
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/LogTableRowRenderer.java
@@ -0,0 +1,109 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import java.awt.Color;
+import java.awt.Component;
+
+import javax.swing.JTable;
+import javax.swing.table.DefaultTableCellRenderer;
+
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogRecord;
+
+/**
+ * LogTableRowRenderer
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class LogTableRowRenderer extends DefaultTableCellRenderer {
+ private static final long serialVersionUID = -3951639953706443213L;
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected boolean _highlightFatal = true;
+ protected Color _color = new Color(230, 230, 230);
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public Component getTableCellRendererComponent(JTable table,
+ Object value,
+ boolean isSelected,
+ boolean hasFocus,
+ int row,
+ int col) {
+
+ if ((row % 2) == 0) {
+ setBackground(_color);
+ } else {
+ setBackground(Color.white);
+ }
+
+ FilteredLogTableModel model = (FilteredLogTableModel) table.getModel();
+ LogRecord record = model.getFilteredRecord(row);
+
+ setForeground(getLogLevelColor(record.getLevel()));
+
+ return (super.getTableCellRendererComponent(table,
+ value,
+ isSelected,
+ hasFocus,
+ row, col));
+ }
+
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+ protected Color getLogLevelColor(LogLevel level) {
+ return (Color) LogLevel.getLogLevelColorMap().get(level);
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java b/java/src/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java
new file mode 100644
index 0000000..4aa959c
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/TrackingAdjustmentListener.java
@@ -0,0 +1,86 @@
+/*
+ * 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.log4j.lf5.viewer;
+
+import java.awt.Adjustable;
+import java.awt.event.AdjustmentEvent;
+import java.awt.event.AdjustmentListener;
+
+/**
+ * An AdjustmentListener which ensures that an Adjustable (e.g. a Scrollbar)
+ * will "track" when the Adjustable expands.
+ * For example, when a vertical scroll bar is at its bottom anchor,
+ * the scrollbar will remain at the bottom. When the vertical scroll bar
+ * is at any other location, then no tracking will happen.
+ * An instance of this class should only listen to one Adjustable as
+ * it retains state information about the Adjustable it listens to.
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class TrackingAdjustmentListener implements AdjustmentListener {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ protected int _lastMaximum = -1;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public void adjustmentValueChanged(AdjustmentEvent e) {
+ Adjustable bar = e.getAdjustable();
+ int currentMaximum = bar.getMaximum();
+ if (bar.getMaximum() == _lastMaximum) {
+ return; // nothing to do, the adjustable has not expanded
+ }
+ int bottom = bar.getValue() + bar.getVisibleAmount();
+
+ if (bottom + bar.getUnitIncrement() >= _lastMaximum) {
+ bar.setValue(bar.getMaximum()); // use the most recent maximum
+ }
+ _lastMaximum = currentMaximum;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java
new file mode 100644
index 0000000..e5bbaa6
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryAbstractCellEditor.java
@@ -0,0 +1,172 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import java.awt.Component;
+import java.awt.event.MouseEvent;
+import java.util.EventObject;
+
+import javax.swing.JTable;
+import javax.swing.JTree;
+import javax.swing.event.CellEditorListener;
+import javax.swing.event.ChangeEvent;
+import javax.swing.event.EventListenerList;
+import javax.swing.table.TableCellEditor;
+import javax.swing.tree.TreeCellEditor;
+
+/**
+ * CategoryAbstractCellEditor. Base class to handle the some common
+ * details of cell editing.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryAbstractCellEditor implements TableCellEditor, TreeCellEditor {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected EventListenerList _listenerList = new EventListenerList();
+ protected Object _value;
+ protected ChangeEvent _changeEvent = null;
+ protected int _clickCountToStart = 1;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public Object getCellEditorValue() {
+ return _value;
+ }
+
+ public void setCellEditorValue(Object value) {
+ _value = value;
+ }
+
+ public void setClickCountToStart(int count) {
+ _clickCountToStart = count;
+ }
+
+ public int getClickCountToStart() {
+ return _clickCountToStart;
+ }
+
+ public boolean isCellEditable(EventObject anEvent) {
+ if (anEvent instanceof MouseEvent) {
+ if (((MouseEvent) anEvent).getClickCount() < _clickCountToStart) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public boolean shouldSelectCell(EventObject anEvent) {
+ if (this.isCellEditable(anEvent)) {
+ if (anEvent == null ||
+ ((MouseEvent) anEvent).getClickCount() >= _clickCountToStart) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean stopCellEditing() {
+ fireEditingStopped();
+ return true;
+ }
+
+ public void cancelCellEditing() {
+ fireEditingCanceled();
+ }
+
+ public void addCellEditorListener(CellEditorListener l) {
+ _listenerList.add(CellEditorListener.class, l);
+ }
+
+ public void removeCellEditorListener(CellEditorListener l) {
+ _listenerList.remove(CellEditorListener.class, l);
+ }
+
+ public Component getTreeCellEditorComponent(
+ JTree tree, Object value,
+ boolean isSelected,
+ boolean expanded,
+ boolean leaf, int row) {
+ return null;
+ }
+
+ public Component getTableCellEditorComponent(
+ JTable table, Object value,
+ boolean isSelected,
+ int row, int column) {
+ return null;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+ protected void fireEditingStopped() {
+ Object[] listeners = _listenerList.getListenerList();
+
+ for (int i = listeners.length - 2; i >= 0; i -= 2) {
+ if (listeners[i] == CellEditorListener.class) {
+ if (_changeEvent == null) {
+ _changeEvent = new ChangeEvent(this);
+ }
+
+ ((CellEditorListener) listeners[i + 1]).editingStopped(_changeEvent);
+ }
+ }
+ }
+
+ protected void fireEditingCanceled() {
+ Object[] listeners = _listenerList.getListenerList();
+
+ for (int i = listeners.length - 2; i >= 0; i -= 2) {
+ if (listeners[i] == CellEditorListener.class) {
+ if (_changeEvent == null) {
+ _changeEvent = new ChangeEvent(this);
+ }
+
+ ((CellEditorListener) listeners[i + 1]).editingCanceled(_changeEvent);
+ }
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java
new file mode 100644
index 0000000..1391fd5
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryElement.java
@@ -0,0 +1,84 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+/**
+ * CategoryElement represents a single element or part of a Category.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryElement {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected String _categoryTitle;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public CategoryElement() {
+ super();
+ }
+
+ public CategoryElement(String title) {
+ _categoryTitle = title;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public String getTitle() {
+ return (_categoryTitle);
+ }
+
+ public void setTitle(String title) {
+ _categoryTitle = title;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java
new file mode 100644
index 0000000..63f91ae
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerLogRecordFilter.java
@@ -0,0 +1,98 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import org.apache.log4j.lf5.LogRecord;
+import org.apache.log4j.lf5.LogRecordFilter;
+
+import java.util.Enumeration;
+
+/**
+ * An implementation of LogRecordFilter based on a CategoryExplorerModel
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryExplorerLogRecordFilter implements LogRecordFilter {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ protected CategoryExplorerModel _model;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public CategoryExplorerLogRecordFilter(CategoryExplorerModel model) {
+ _model = model;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * @return true if the CategoryExplorer model specified at construction
+ * is accepting the category of the specified LogRecord. Has a side-effect
+ * of adding the CategoryPath of the LogRecord to the explorer model
+ * if the CategoryPath is new.
+ */
+ public boolean passes(LogRecord record) {
+ CategoryPath path = new CategoryPath(record.getCategory());
+ return _model.isCategoryPathActive(path);
+ }
+
+ /**
+ * Resets the counters for the contained CategoryNodes to zero.
+ */
+ public void reset() {
+ resetAllNodes();
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected void resetAllNodes() {
+ Enumeration nodes = _model.getRootCategoryNode().depthFirstEnumeration();
+ CategoryNode current;
+ while (nodes.hasMoreElements()) {
+ current = (CategoryNode) nodes.nextElement();
+ current.resetNumberOfContainedRecords();
+ _model.nodeChanged(current);
+ }
+ }
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java
new file mode 100644
index 0000000..c70b55c
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerModel.java
@@ -0,0 +1,347 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import java.awt.AWTEventMulticaster;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.util.Enumeration;
+
+import javax.swing.SwingUtilities;
+import javax.swing.tree.DefaultTreeModel;
+import javax.swing.tree.TreeNode;
+import javax.swing.tree.TreePath;
+
+import org.apache.log4j.lf5.LogRecord;
+
+/**
+ * CategoryExplorerModel
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brent Sprecher
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryExplorerModel extends DefaultTreeModel {
+ private static final long serialVersionUID = -3413887384316015901L;
+
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ protected boolean _renderFatal = true;
+ protected ActionListener _listener = null;
+ protected ActionEvent _event = new ActionEvent(this,
+ ActionEvent.ACTION_PERFORMED,
+ "Nodes Selection changed");
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public CategoryExplorerModel(CategoryNode node) {
+ super(node);
+ }
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public void addLogRecord(LogRecord lr) {
+ CategoryPath path = new CategoryPath(lr.getCategory());
+ addCategory(path); // create category path if it is new
+ CategoryNode node = getCategoryNode(path);
+ node.addRecord(); // update category node
+ if (_renderFatal && lr.isFatal()) {
+ TreeNode[] nodes = getPathToRoot(node);
+ int len = nodes.length;
+ CategoryNode parent;
+
+ // i = 0 gives root node
+ // skip node and root, loop through "parents" in between
+ for (int i = 1; i < len - 1; i++) {
+ parent = (CategoryNode) nodes[i];
+ parent.setHasFatalChildren(true);
+ nodeChanged(parent);
+ }
+ node.setHasFatalRecords(true);
+ nodeChanged(node);
+ }
+ }
+
+ public CategoryNode getRootCategoryNode() {
+ return (CategoryNode) getRoot();
+ }
+
+ public CategoryNode getCategoryNode(String category) {
+ CategoryPath path = new CategoryPath(category);
+ return (getCategoryNode(path));
+ }
+
+ /**
+ * returns null if no CategoryNode exists.
+ */
+ public CategoryNode getCategoryNode(CategoryPath path) {
+ CategoryNode root = (CategoryNode) getRoot();
+ CategoryNode parent = root; // Start condition.
+
+ for (int i = 0; i < path.size(); i++) {
+ CategoryElement element = path.categoryElementAt(i);
+
+ // If the two nodes have matching titles they are considered equal.
+ Enumeration children = parent.children();
+
+ boolean categoryAlreadyExists = false;
+ while (children.hasMoreElements()) {
+ CategoryNode node = (CategoryNode) children.nextElement();
+ String title = node.getTitle().toLowerCase();
+
+ String pathLC = element.getTitle().toLowerCase();
+ if (title.equals(pathLC)) {
+ categoryAlreadyExists = true;
+ // This is now the new parent node.
+ parent = node;
+ break; // out of the while, and back to the for().
+ }
+ }
+
+ if (categoryAlreadyExists == false) {
+ return null; // Didn't find the Node.
+ }
+ }
+
+ return (parent);
+ }
+
+ /**
+ * @return true if all the nodes in the specified CategoryPath are
+ * selected.
+ */
+ public boolean isCategoryPathActive(CategoryPath path) {
+ CategoryNode root = (CategoryNode) getRoot();
+ CategoryNode parent = root; // Start condition.
+ boolean active = false;
+
+ for (int i = 0; i < path.size(); i++) {
+ CategoryElement element = path.categoryElementAt(i);
+
+ // If the two nodes have matching titles they are considered equal.
+ Enumeration children = parent.children();
+
+ boolean categoryAlreadyExists = false;
+ active = false;
+
+ while (children.hasMoreElements()) {
+ CategoryNode node = (CategoryNode) children.nextElement();
+ String title = node.getTitle().toLowerCase();
+
+ String pathLC = element.getTitle().toLowerCase();
+ if (title.equals(pathLC)) {
+ categoryAlreadyExists = true;
+ // This is now the new parent node.
+ parent = node;
+
+ if (parent.isSelected()) {
+ active = true;
+ }
+
+ break; // out of the while, and back to the for().
+ }
+ }
+
+ if (active == false || categoryAlreadyExists == false) {
+ return false;
+ }
+ }
+
+ return (active);
+ }
+
+
+ /**
+ * Method altered by Richard Hurst such that it returns the CategoryNode
+ * corresponding to the CategoryPath
+ *
+ * @param path category path.
+ * @return CategoryNode
+ */
+ public CategoryNode addCategory(CategoryPath path) {
+ CategoryNode root = (CategoryNode) getRoot();
+ CategoryNode parent = root; // Start condition.
+
+ for (int i = 0; i < path.size(); i++) {
+ CategoryElement element = path.categoryElementAt(i);
+
+ // If the two nodes have matching titles they are considered equal.
+ Enumeration children = parent.children();
+
+ boolean categoryAlreadyExists = false;
+ while (children.hasMoreElements()) {
+ CategoryNode node = (CategoryNode) children.nextElement();
+ String title = node.getTitle().toLowerCase();
+
+ String pathLC = element.getTitle().toLowerCase();
+ if (title.equals(pathLC)) {
+ categoryAlreadyExists = true;
+ // This is now the new parent node.
+ parent = node;
+ break;
+ }
+ }
+
+ if (categoryAlreadyExists == false) {
+ // We need to add the node.
+ CategoryNode newNode = new CategoryNode(element.getTitle());
+
+ //This method of adding a new node cause parent roots to be
+ // collapsed.
+ //parent.add( newNode );
+ //reload(parent);
+
+ // This doesn't force the nodes to collapse.
+ insertNodeInto(newNode, parent, parent.getChildCount());
+ refresh(newNode);
+
+ // The newly added node is now the parent.
+ parent = newNode;
+
+ }
+ }
+
+ return parent;
+ }
+
+ public void update(CategoryNode node, boolean selected) {
+ if (node.isSelected() == selected) {
+ return; // nothing was changed, nothing to do
+ }
+ // select parents or deselect children
+ if (selected) {
+ setParentSelection(node, true);
+ } else {
+ setDescendantSelection(node, false);
+ }
+ }
+
+ public void setDescendantSelection(CategoryNode node, boolean selected) {
+ Enumeration descendants = node.depthFirstEnumeration();
+ CategoryNode current;
+ while (descendants.hasMoreElements()) {
+ current = (CategoryNode) descendants.nextElement();
+ // does the current node need to be changed?
+ if (current.isSelected() != selected) {
+ current.setSelected(selected);
+ nodeChanged(current);
+ }
+ }
+ notifyActionListeners();
+ }
+
+ public void setParentSelection(CategoryNode node, boolean selected) {
+ TreeNode[] nodes = getPathToRoot(node);
+ int len = nodes.length;
+ CategoryNode parent;
+
+ // i = 0 gives root node, i=len-1 gives this node
+ // skip the root node
+ for (int i = 1; i < len; i++) {
+ parent = (CategoryNode) nodes[i];
+ if (parent.isSelected() != selected) {
+ parent.setSelected(selected);
+ nodeChanged(parent);
+ }
+ }
+ notifyActionListeners();
+ }
+
+
+ public synchronized void addActionListener(ActionListener l) {
+ _listener = AWTEventMulticaster.add(_listener, l);
+ }
+
+ public synchronized void removeActionListener(ActionListener l) {
+ _listener = AWTEventMulticaster.remove(_listener, l);
+ }
+
+ public void resetAllNodeCounts() {
+ Enumeration nodes = getRootCategoryNode().depthFirstEnumeration();
+ CategoryNode current;
+ while (nodes.hasMoreElements()) {
+ current = (CategoryNode) nodes.nextElement();
+ current.resetNumberOfContainedRecords();
+ nodeChanged(current);
+ }
+ }
+
+ /**
+ * Returns the CategoryPath to the specified CategoryNode
+ *
+ * @param node The target CategoryNode
+ * @return CategoryPath
+ */
+ public TreePath getTreePathToRoot(CategoryNode node) {
+ if (node == null) {
+ return null;
+ }
+ return (new TreePath(getPathToRoot(node)));
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+ protected void notifyActionListeners() {
+ if (_listener != null) {
+ _listener.actionPerformed(_event);
+ }
+ }
+
+ /**
+ * Fires a nodechanged event on the SwingThread.
+ */
+ protected void refresh(final CategoryNode node) {
+ SwingUtilities.invokeLater(new Runnable() {
+ public void run() {
+ nodeChanged(node); // remind the tree to render the new node
+ }
+ });
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java
new file mode 100644
index 0000000..a8e97f5
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryExplorerTree.java
@@ -0,0 +1,157 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import java.awt.event.MouseEvent;
+
+import javax.swing.JTree;
+import javax.swing.event.TreeModelEvent;
+import javax.swing.tree.TreePath;
+
+/**
+ * CategoryExplorerTree
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ * @author Brent Sprecher
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryExplorerTree extends JTree {
+ private static final long serialVersionUID = 8066257446951323576L;
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected CategoryExplorerModel _model;
+ protected boolean _rootAlreadyExpanded = false;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Construct a CategoryExplorerTree with a specific model.
+ */
+ public CategoryExplorerTree(CategoryExplorerModel model) {
+ super(model);
+
+ _model = model;
+ init();
+ }
+
+ /**
+ * Construct a CategoryExplorerTree and create a default CategoryExplorerModel.
+ */
+ public CategoryExplorerTree() {
+ super();
+
+ CategoryNode rootNode = new CategoryNode("Categories");
+
+ _model = new CategoryExplorerModel(rootNode);
+
+ setModel(_model);
+
+ init();
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public CategoryExplorerModel getExplorerModel() {
+ return (_model);
+ }
+
+ public String getToolTipText(MouseEvent e) {
+
+ try {
+ return super.getToolTipText(e);
+ } catch (Exception ex) {
+ return "";
+ }
+
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected void init() {
+ // Put visible lines on the JTree.
+ putClientProperty("JTree.lineStyle", "Angled");
+
+ // Configure the Tree with the appropriate Renderers and Editors.
+
+ CategoryNodeRenderer renderer = new CategoryNodeRenderer();
+ setEditable(true);
+ setCellRenderer(renderer);
+
+ CategoryNodeEditor editor = new CategoryNodeEditor(_model);
+
+ setCellEditor(new CategoryImmediateEditor(this,
+ new CategoryNodeRenderer(),
+ editor));
+ setShowsRootHandles(true);
+
+ setToolTipText("");
+
+ ensureRootExpansion();
+
+ }
+
+ protected void expandRootNode() {
+ if (_rootAlreadyExpanded) {
+ return;
+ }
+ _rootAlreadyExpanded = true;
+ TreePath path = new TreePath(_model.getRootCategoryNode().getPath());
+ expandPath(path);
+ }
+
+ protected void ensureRootExpansion() {
+ _model.addTreeModelListener(new TreeModelAdapter() {
+ public void treeNodesInserted(TreeModelEvent e) {
+ expandRootNode();
+ }
+ });
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java
new file mode 100644
index 0000000..4c7374b
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryImmediateEditor.java
@@ -0,0 +1,148 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import java.awt.Dimension;
+import java.awt.Rectangle;
+import java.awt.event.MouseEvent;
+import java.util.EventObject;
+
+import javax.swing.Icon;
+import javax.swing.JTree;
+import javax.swing.tree.DefaultTreeCellEditor;
+import javax.swing.tree.TreePath;
+
+/**
+ * CategoryImmediateEditor
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryImmediateEditor extends DefaultTreeCellEditor {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ private CategoryNodeRenderer renderer;
+ protected Icon editingIcon = null;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ public CategoryImmediateEditor(JTree tree,
+ CategoryNodeRenderer renderer,
+ CategoryNodeEditor editor) {
+ super(tree, renderer, editor);
+ this.renderer = renderer;
+ renderer.setIcon(null);
+ renderer.setLeafIcon(null);
+ renderer.setOpenIcon(null);
+ renderer.setClosedIcon(null);
+
+ super.editingIcon = null;
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+ public boolean shouldSelectCell(EventObject e) {
+ boolean rv = false; // only mouse events
+
+ if (e instanceof MouseEvent) {
+ MouseEvent me = (MouseEvent) e;
+ TreePath path = tree.getPathForLocation(me.getX(),
+ me.getY());
+ CategoryNode node = (CategoryNode)
+ path.getLastPathComponent();
+
+ rv = node.isLeaf() /*|| !inCheckBoxHitRegion(me)*/;
+ }
+ return rv;
+ }
+
+ public boolean inCheckBoxHitRegion(MouseEvent e) {
+ TreePath path = tree.getPathForLocation(e.getX(),
+ e.getY());
+ if (path == null) {
+ return false;
+ }
+ CategoryNode node = (CategoryNode) path.getLastPathComponent();
+ boolean rv = false;
+
+ if (true) {
+ // offset and lastRow DefaultTreeCellEditor
+ // protected members
+
+ Rectangle bounds = tree.getRowBounds(lastRow);
+ Dimension checkBoxOffset =
+ renderer.getCheckBoxOffset();
+
+ bounds.translate(offset + checkBoxOffset.width,
+ checkBoxOffset.height);
+
+ rv = bounds.contains(e.getPoint());
+ }
+ return true;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected boolean canEditImmediately(EventObject e) {
+ boolean rv = false;
+
+ if (e instanceof MouseEvent) {
+ MouseEvent me = (MouseEvent) e;
+ rv = inCheckBoxHitRegion(me);
+ }
+
+ return rv;
+ }
+
+ protected void determineOffset(JTree tree, Object value,
+ boolean isSelected, boolean expanded,
+ boolean leaf, int row) {
+ // Very important: means that the tree won't jump around.
+ offset = 0;
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java
new file mode 100644
index 0000000..95bdc42
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNode.java
@@ -0,0 +1,197 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import javax.swing.tree.DefaultMutableTreeNode;
+import javax.swing.tree.TreeNode;
+import java.util.Enumeration;
+
+/**
+ * CategoryNode
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryNode extends DefaultMutableTreeNode {
+ private static final long serialVersionUID = 5958994817693177319L;
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected boolean _selected = true;
+ protected int _numberOfContainedRecords = 0;
+ protected int _numberOfRecordsFromChildren = 0;
+ protected boolean _hasFatalChildren = false;
+ protected boolean _hasFatalRecords = false;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ /**
+ *
+ */
+ public CategoryNode(String title) {
+ setUserObject(title);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+ public String getTitle() {
+ return (String) getUserObject();
+ }
+
+ public void setSelected(boolean s) {
+ if (s != _selected) {
+ _selected = s;
+ }
+ }
+
+ public boolean isSelected() {
+ return _selected;
+ }
+
+ /**
+ * @deprecated
+ */
+ public void setAllDescendantsSelected() {
+ Enumeration children = children();
+ while (children.hasMoreElements()) {
+ CategoryNode node = (CategoryNode) children.nextElement();
+ node.setSelected(true);
+ node.setAllDescendantsSelected();
+ }
+ }
+
+ /**
+ * @deprecated
+ */
+ public void setAllDescendantsDeSelected() {
+ Enumeration children = children();
+ while (children.hasMoreElements()) {
+ CategoryNode node = (CategoryNode) children.nextElement();
+ node.setSelected(false);
+ node.setAllDescendantsDeSelected();
+ }
+ }
+
+ public String toString() {
+ return (getTitle());
+ }
+
+ public boolean equals(Object obj) {
+ if (obj instanceof CategoryNode) {
+ CategoryNode node = (CategoryNode) obj;
+ String tit1 = getTitle().toLowerCase();
+ String tit2 = node.getTitle().toLowerCase();
+
+ if (tit1.equals(tit2)) {
+ return (true);
+ }
+ }
+ return (false);
+ }
+
+ public int hashCode() {
+ return (getTitle().hashCode());
+ }
+
+ public void addRecord() {
+ _numberOfContainedRecords++;
+ addRecordToParent();
+ }
+
+ public int getNumberOfContainedRecords() {
+ return _numberOfContainedRecords;
+ }
+
+ public void resetNumberOfContainedRecords() {
+ _numberOfContainedRecords = 0;
+ _numberOfRecordsFromChildren = 0;
+ _hasFatalRecords = false;
+ _hasFatalChildren = false;
+ }
+
+ public boolean hasFatalRecords() {
+ return _hasFatalRecords;
+ }
+
+ public boolean hasFatalChildren() {
+ return _hasFatalChildren;
+ }
+
+ public void setHasFatalRecords(boolean flag) {
+ _hasFatalRecords = flag;
+ }
+
+ public void setHasFatalChildren(boolean flag) {
+ _hasFatalChildren = flag;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected int getTotalNumberOfRecords() {
+ return getNumberOfRecordsFromChildren() + getNumberOfContainedRecords();
+ }
+
+ /**
+ * Passes up the addition from child to parent
+ */
+ protected void addRecordFromChild() {
+ _numberOfRecordsFromChildren++;
+ addRecordToParent();
+ }
+
+ protected int getNumberOfRecordsFromChildren() {
+ return _numberOfRecordsFromChildren;
+ }
+
+ protected void addRecordToParent() {
+ TreeNode parent = getParent();
+ if (parent == null) {
+ return;
+ }
+ ((CategoryNode) parent).addRecordFromChild();
+ }
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java
new file mode 100644
index 0000000..b653cd7
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditor.java
@@ -0,0 +1,291 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import java.awt.Component;
+import java.awt.event.ActionEvent;
+import java.awt.event.ActionListener;
+import java.awt.event.MouseAdapter;
+import java.awt.event.MouseEvent;
+import java.util.ArrayList;
+import java.util.Enumeration;
+
+import javax.swing.JCheckBox;
+import javax.swing.JMenuItem;
+import javax.swing.JOptionPane;
+import javax.swing.JPopupMenu;
+import javax.swing.JTree;
+import javax.swing.tree.TreePath;
+
+/**
+ * CategoryNodeEditor
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryNodeEditor extends CategoryAbstractCellEditor {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected CategoryNodeEditorRenderer _renderer;
+ protected CategoryNode _lastEditedNode;
+ protected JCheckBox _checkBox;
+ protected CategoryExplorerModel _categoryModel;
+ protected JTree _tree;
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public CategoryNodeEditor(CategoryExplorerModel model) {
+ _renderer = new CategoryNodeEditorRenderer();
+ _checkBox = _renderer.getCheckBox();
+ _categoryModel = model;
+
+ _checkBox.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _categoryModel.update(_lastEditedNode, _checkBox.isSelected());
+ stopCellEditing();
+ }
+ });
+
+ _renderer.addMouseListener(new MouseAdapter() {
+ public void mousePressed(MouseEvent e) {
+ if ((e.getModifiers() & MouseEvent.BUTTON3_MASK) != 0) {
+ showPopup(_lastEditedNode, e.getX(), e.getY());
+ }
+ stopCellEditing();
+ }
+ });
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public Component getTreeCellEditorComponent(JTree tree, Object value,
+ boolean selected, boolean expanded,
+ boolean leaf, int row) {
+ _lastEditedNode = (CategoryNode) value;
+ _tree = tree;
+
+ return _renderer.getTreeCellRendererComponent(tree,
+ value, selected, expanded,
+ leaf, row, true);
+ // hasFocus ignored
+ }
+
+ public Object getCellEditorValue() {
+ return _lastEditedNode.getUserObject();
+ }
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected JMenuItem createPropertiesMenuItem(final CategoryNode node) {
+ JMenuItem result = new JMenuItem("Properties");
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ showPropertiesDialog(node);
+ }
+ });
+ return result;
+ }
+
+ protected void showPropertiesDialog(CategoryNode node) {
+ JOptionPane.showMessageDialog(
+ _tree,
+ getDisplayedProperties(node),
+ "Category Properties: " + node.getTitle(),
+ JOptionPane.PLAIN_MESSAGE
+ );
+ }
+
+ protected Object getDisplayedProperties(CategoryNode node) {
+ ArrayList result = new ArrayList();
+ result.add("Category: " + node.getTitle());
+ if (node.hasFatalRecords()) {
+ result.add("Contains at least one fatal LogRecord.");
+ }
+ if (node.hasFatalChildren()) {
+ result.add("Contains descendants with a fatal LogRecord.");
+ }
+ result.add("LogRecords in this category alone: " +
+ node.getNumberOfContainedRecords());
+ result.add("LogRecords in descendant categories: " +
+ node.getNumberOfRecordsFromChildren());
+ result.add("LogRecords in this category including descendants: " +
+ node.getTotalNumberOfRecords());
+ return result.toArray();
+ }
+
+ protected void showPopup(CategoryNode node, int x, int y) {
+ JPopupMenu popup = new JPopupMenu();
+ popup.setSize(150, 400);
+ //
+ // Configure the Popup
+ //
+ if (node.getParent() == null) {
+ popup.add(createRemoveMenuItem());
+ popup.addSeparator();
+ }
+ popup.add(createSelectDescendantsMenuItem(node));
+ popup.add(createUnselectDescendantsMenuItem(node));
+ popup.addSeparator();
+ popup.add(createExpandMenuItem(node));
+ popup.add(createCollapseMenuItem(node));
+ popup.addSeparator();
+ popup.add(createPropertiesMenuItem(node));
+ popup.show(_renderer, x, y);
+ }
+
+ protected JMenuItem createSelectDescendantsMenuItem(final CategoryNode node) {
+ JMenuItem selectDescendants =
+ new JMenuItem("Select All Descendant Categories");
+ selectDescendants.addActionListener(
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _categoryModel.setDescendantSelection(node, true);
+ }
+ }
+ );
+ return selectDescendants;
+ }
+
+ protected JMenuItem createUnselectDescendantsMenuItem(final CategoryNode node) {
+ JMenuItem unselectDescendants =
+ new JMenuItem("Deselect All Descendant Categories");
+ unselectDescendants.addActionListener(
+
+ new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ _categoryModel.setDescendantSelection(node, false);
+ }
+ }
+
+ );
+ return unselectDescendants;
+ }
+
+ protected JMenuItem createExpandMenuItem(final CategoryNode node) {
+ JMenuItem result = new JMenuItem("Expand All Descendant Categories");
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ expandDescendants(node);
+ }
+ });
+ return result;
+ }
+
+ protected JMenuItem createCollapseMenuItem(final CategoryNode node) {
+ JMenuItem result = new JMenuItem("Collapse All Descendant Categories");
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ collapseDescendants(node);
+ }
+ });
+ return result;
+ }
+
+ /**
+ * This featured was moved from the LogBrokerMonitor class
+ * to the CategoryNodeExplorer so that the Category tree
+ * could be pruned from the Category Explorer popup menu.
+ * This menu option only appears when a user right clicks on
+ * the Category parent node.
+ *
+ * See removeUnusedNodes()
+ */
+ protected JMenuItem createRemoveMenuItem() {
+ JMenuItem result = new JMenuItem("Remove All Empty Categories");
+ result.addActionListener(new ActionListener() {
+ public void actionPerformed(ActionEvent e) {
+ while (removeUnusedNodes() > 0) ;
+ }
+ });
+ return result;
+ }
+
+ protected void expandDescendants(CategoryNode node) {
+ Enumeration descendants = node.depthFirstEnumeration();
+ CategoryNode current;
+ while (descendants.hasMoreElements()) {
+ current = (CategoryNode) descendants.nextElement();
+ expand(current);
+ }
+ }
+
+ protected void collapseDescendants(CategoryNode node) {
+ Enumeration descendants = node.depthFirstEnumeration();
+ CategoryNode current;
+ while (descendants.hasMoreElements()) {
+ current = (CategoryNode) descendants.nextElement();
+ collapse(current);
+ }
+ }
+
+ /**
+ * Removes any inactive nodes from the Category tree.
+ */
+ protected int removeUnusedNodes() {
+ int count = 0;
+ CategoryNode root = _categoryModel.getRootCategoryNode();
+ Enumeration enumeration = root.depthFirstEnumeration();
+ while (enumeration.hasMoreElements()) {
+ CategoryNode node = (CategoryNode) enumeration.nextElement();
+ if (node.isLeaf() && node.getNumberOfContainedRecords() == 0
+ && node.getParent() != null) {
+ _categoryModel.removeNodeFromParent(node);
+ count++;
+ }
+ }
+
+ return count;
+ }
+
+ protected void expand(CategoryNode node) {
+ _tree.expandPath(getTreePath(node));
+ }
+
+ protected TreePath getTreePath(CategoryNode node) {
+ return new TreePath(node.getPath());
+ }
+
+ protected void collapse(CategoryNode node) {
+ _tree.collapsePath(getTreePath(node));
+ }
+
+ //-----------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java
new file mode 100644
index 0000000..57be9fc
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeEditorRenderer.java
@@ -0,0 +1,84 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import java.awt.Component;
+
+import javax.swing.JCheckBox;
+import javax.swing.JTree;
+
+/**
+ * CategoryNodeEditorRenderer
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryNodeEditorRenderer extends CategoryNodeRenderer {
+ private static final long serialVersionUID = -6094804684259929574L;
+
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public Component getTreeCellRendererComponent(
+ JTree tree, Object value,
+ boolean selected, boolean expanded,
+ boolean leaf, int row,
+ boolean hasFocus) {
+ Component c = super.getTreeCellRendererComponent(tree,
+ value, selected, expanded,
+ leaf, row, hasFocus);
+
+ return c;
+ }
+
+ public JCheckBox getCheckBox() {
+ return _checkBox;
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java
new file mode 100644
index 0000000..4eb461d
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryNodeRenderer.java
@@ -0,0 +1,151 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import javax.swing.*;
+import javax.swing.tree.DefaultTreeCellRenderer;
+import java.awt.*;
+import java.net.URL;
+
+/**
+ * CategoryNodeRenderer
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryNodeRenderer extends DefaultTreeCellRenderer {
+ private static final long serialVersionUID = -6046702673278595048L;
+
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ public static final Color FATAL_CHILDREN = new Color(189, 113, 0);
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected JCheckBox _checkBox = new JCheckBox();
+ protected JPanel _panel = new JPanel();
+ protected static ImageIcon _sat = null;
+// protected JLabel _label = new JLabel();
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ public CategoryNodeRenderer() {
+ _panel.setBackground(UIManager.getColor("Tree.textBackground"));
+
+ if (_sat == null) {
+ // Load the satellite image.
+ String resource =
+ "/org/apache/log4j/lf5/viewer/images/channelexplorer_satellite.gif";
+ URL satURL = getClass().getResource(resource);
+
+ _sat = new ImageIcon(satURL);
+ }
+
+ setOpaque(false);
+ _checkBox.setOpaque(false);
+ _panel.setOpaque(false);
+
+ // The flowlayout set to LEFT is very important so that the editor
+ // doesn't jump around.
+ _panel.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
+ _panel.add(_checkBox);
+ _panel.add(this);
+
+ setOpenIcon(_sat);
+ setClosedIcon(_sat);
+ setLeafIcon(_sat);
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+ public Component getTreeCellRendererComponent(
+ JTree tree, Object value,
+ boolean selected, boolean expanded,
+ boolean leaf, int row,
+ boolean hasFocus) {
+
+ CategoryNode node = (CategoryNode) value;
+ //FileNode node = (FileNode)value;
+ //String s = tree.convertValueToText(value, selected,
+ // expanded, leaf, row, hasFocus);
+
+ super.getTreeCellRendererComponent(
+ tree, value, selected, expanded,
+ leaf, row, hasFocus);
+
+ if (row == 0) {
+ // Root row -- no check box
+ _checkBox.setVisible(false);
+ } else {
+ _checkBox.setVisible(true);
+ _checkBox.setSelected(node.isSelected());
+ }
+ String toolTip = buildToolTip(node);
+ _panel.setToolTipText(toolTip);
+ if (node.hasFatalChildren()) {
+ this.setForeground(FATAL_CHILDREN);
+ }
+ if (node.hasFatalRecords()) {
+ this.setForeground(Color.red);
+ }
+
+ return _panel;
+ }
+
+ public Dimension getCheckBoxOffset() {
+ return new Dimension(0, 0);
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ protected String buildToolTip(CategoryNode node) {
+ StringBuffer result = new StringBuffer();
+ result.append(node.getTitle()).append(" contains a total of ");
+ result.append(node.getTotalNumberOfRecords());
+ result.append(" LogRecords.");
+ result.append(" Right-click for more info.");
+ return result.toString();
+ }
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java
new file mode 100644
index 0000000..8cc3da1
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/CategoryPath.java
@@ -0,0 +1,157 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import java.util.LinkedList;
+import java.util.StringTokenizer;
+
+/**
+ * CategoryPath is a collection of CategoryItems which represent a
+ * path of categories.
+ *
+ * @author Michael J. Sikorsky
+ * @author Robert Shaw
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class CategoryPath {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+ protected LinkedList _categoryElements = new LinkedList();
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ public CategoryPath() {
+ super();
+ }
+
+ /**
+ * Construct a CategoryPath. If the category is null, it defaults to "Debug".
+ */
+ public CategoryPath(String category) {
+ String processedCategory = category;
+
+ if (processedCategory == null) {
+ processedCategory = "Debug";
+ }
+
+ processedCategory = processedCategory.replace('/', '.');
+ processedCategory = processedCategory.replace('\\', '.');
+
+ StringTokenizer st = new StringTokenizer(processedCategory, ".");
+ while (st.hasMoreTokens()) {
+ String element = st.nextToken();
+ addCategoryElement(new CategoryElement(element));
+ }
+ }
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * returns the number of CategoryElements.
+ */
+ public int size() {
+ int count = _categoryElements.size();
+
+ return (count);
+ }
+
+ public boolean isEmpty() {
+ boolean empty = false;
+
+ if (_categoryElements.size() == 0) {
+ empty = true;
+ }
+
+ return (empty);
+ }
+
+
+ /**
+ * Removes all categoryElements.
+ */
+ public void removeAllCategoryElements() {
+ _categoryElements.clear();
+ }
+
+ /**
+ * Adds the specified categoryElement to the end of the categoryElement set.
+ */
+ public void addCategoryElement(CategoryElement categoryElement) {
+ _categoryElements.addLast(categoryElement);
+ }
+
+ /**
+ * Returns the CategoryElement at the specified index.
+ */
+ public CategoryElement categoryElementAt(int index) {
+ return ((CategoryElement) _categoryElements.get(index));
+ }
+
+
+ public String toString() {
+ StringBuffer out = new StringBuffer(100);
+
+ out.append("\n");
+ out.append("===========================\n");
+ out.append("CategoryPath: \n");
+ out.append("---------------------------\n");
+
+ out.append("\nCategoryPath:\n\t");
+
+ if (this.size() > 0) {
+ for (int i = 0; i < this.size(); i++) {
+ out.append(this.categoryElementAt(i).toString());
+ out.append("\n\t");
+ }
+ } else {
+ out.append("<>");
+ }
+
+ out.append("\n");
+ out.append("===========================\n");
+
+ return (out.toString());
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
diff --git a/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java
new file mode 100644
index 0000000..7323dcc
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/categoryexplorer/TreeModelAdapter.java
@@ -0,0 +1,75 @@
+/*
+ * 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.log4j.lf5.viewer.categoryexplorer;
+
+import javax.swing.event.TreeModelEvent;
+import javax.swing.event.TreeModelListener;
+
+/**
+ * Default implementation of TreeModelListener which does nothing.
+ *
+ * @author Richard Wan
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class TreeModelAdapter implements TreeModelListener {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+ public void treeNodesChanged(TreeModelEvent e) {
+ }
+
+ public void treeNodesInserted(TreeModelEvent e) {
+ }
+
+ public void treeNodesRemoved(TreeModelEvent e) {
+ }
+
+ public void treeStructureChanged(TreeModelEvent e) {
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java b/java/src/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java
new file mode 100644
index 0000000..a94ffab
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/configure/ConfigurationManager.java
@@ -0,0 +1,466 @@
+/*
+ * 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.log4j.lf5.viewer.configure;
+
+import java.awt.Color;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import javax.swing.JCheckBoxMenuItem;
+import javax.swing.tree.TreePath;
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+import org.apache.log4j.lf5.LogLevel;
+import org.apache.log4j.lf5.LogLevelFormatException;
+import org.apache.log4j.lf5.viewer.LogBrokerMonitor;
+import org.apache.log4j.lf5.viewer.LogTable;
+import org.apache.log4j.lf5.viewer.LogTableColumn;
+import org.apache.log4j.lf5.viewer.LogTableColumnFormatException;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerModel;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryExplorerTree;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryNode;
+import org.apache.log4j.lf5.viewer.categoryexplorer.CategoryPath;
+import org.w3c.dom.Document;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+/**
+ * ConfigurationManager handles the storage and retrival of the state of
+ * the CategoryExplorer
+ *
+ * @author Richard Hurst
+ * @author Brad Marlborough
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class ConfigurationManager extends Object {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+ private static final String CONFIG_FILE_NAME = "lf5_configuration.xml";
+ private static final String NAME = "name";
+ private static final String PATH = "path";
+ private static final String SELECTED = "selected";
+ private static final String EXPANDED = "expanded";
+ private static final String CATEGORY = "category";
+ private static final String FIRST_CATEGORY_NAME = "Categories";
+ private static final String LEVEL = "level";
+ private static final String COLORLEVEL = "colorlevel";
+ private static final String RED = "red";
+ private static final String GREEN = "green";
+ private static final String BLUE = "blue";
+ private static final String COLUMN = "column";
+ private static final String NDCTEXTFILTER = "searchtext";
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+ private LogBrokerMonitor _monitor = null;
+ private LogTable _table = null;
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ public ConfigurationManager(LogBrokerMonitor monitor, LogTable table) {
+ super();
+ _monitor = monitor;
+ _table = table;
+ load();
+ }
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ public void save() {
+ CategoryExplorerModel model = _monitor.getCategoryExplorerTree().getExplorerModel();
+ CategoryNode root = model.getRootCategoryNode();
+
+ StringBuffer xml = new StringBuffer(2048);
+ openXMLDocument(xml);
+ openConfigurationXML(xml);
+ processLogRecordFilter(_monitor.getNDCTextFilter(), xml);
+ processLogLevels(_monitor.getLogLevelMenuItems(), xml);
+ processLogLevelColors(_monitor.getLogLevelMenuItems(),
+ LogLevel.getLogLevelColorMap(), xml);
+ processLogTableColumns(LogTableColumn.getLogTableColumns(), xml);
+ processConfigurationNode(root, xml);
+ closeConfigurationXML(xml);
+ store(xml.toString());
+ }
+
+ public void reset() {
+ deleteConfigurationFile();
+ collapseTree();
+ selectAllNodes();
+ }
+
+ public static String treePathToString(TreePath path) {
+ // count begins at one so as to not include the 'Categories' - root category
+ StringBuffer sb = new StringBuffer();
+ CategoryNode n = null;
+ Object[] objects = path.getPath();
+ for (int i = 1; i < objects.length; i++) {
+ n = (CategoryNode) objects[i];
+ if (i > 1) {
+ sb.append(".");
+ }
+ sb.append(n.getTitle());
+ }
+ return sb.toString();
+ }
+
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+ protected void load() {
+ File file = new File(getFilename());
+ if (file.exists()) {
+ try {
+ DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.
+ newInstance();
+ DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
+ Document doc = docBuilder.parse(file);
+ processRecordFilter(doc);
+ processCategories(doc);
+ processLogLevels(doc);
+ processLogLevelColors(doc);
+ processLogTableColumns(doc);
+ } catch (Exception e) {
+ // ignore all error and just continue as if there was no
+ // configuration xml file but do report a message
+ System.err.println("Unable process configuration file at " +
+ getFilename() + ". Error Message=" + e.getMessage());
+ }
+ }
+
+ }
+
+ // Added in version 1.2 - reads in the NDC text filter from the
+ // xml configuration file. If the value of the filter is not null
+ // or an empty string ("") then the manager will set the LogBrokerMonitor's
+ // LogRecordFilter to use the NDC LogRecordFilter. Otherwise, the
+ // LogBrokerMonitor will use the default LogRecordFilter.
+ protected void processRecordFilter(Document doc) {
+ NodeList nodeList = doc.getElementsByTagName(NDCTEXTFILTER);
+
+ // there is only one value stored
+ Node n = nodeList.item(0);
+ // add check for backwards compatibility as this feature was added in
+ // version 1.2
+ if (n == null) {
+ return;
+ }
+
+ NamedNodeMap map = n.getAttributes();
+ String text = getValue(map, NAME);
+
+ if (text == null || text.equals("")) {
+ return;
+ }
+ _monitor.setNDCLogRecordFilter(text);
+ }
+
+ protected void processCategories(Document doc) {
+ CategoryExplorerTree tree = _monitor.getCategoryExplorerTree();
+ CategoryExplorerModel model = tree.getExplorerModel();
+ NodeList nodeList = doc.getElementsByTagName(CATEGORY);
+
+ // determine where the starting node is
+ NamedNodeMap map = nodeList.item(0).getAttributes();
+ int j = (getValue(map, NAME).equalsIgnoreCase(FIRST_CATEGORY_NAME)) ? 1 : 0;
+ // iterate backwards throught the nodeList so that expansion of the
+ // list can occur
+ for (int i = nodeList.getLength() - 1; i >= j; i--) {
+ Node n = nodeList.item(i);
+ map = n.getAttributes();
+ CategoryNode chnode = model.addCategory(new CategoryPath(getValue(map, PATH)));
+ chnode.setSelected((getValue(map, SELECTED).equalsIgnoreCase("true")) ? true : false);
+ if (getValue(map, EXPANDED).equalsIgnoreCase("true")) ;
+ tree.expandPath(model.getTreePathToRoot(chnode));
+ }
+
+ }
+
+ protected void processLogLevels(Document doc) {
+ NodeList nodeList = doc.getElementsByTagName(LEVEL);
+ Map menuItems = _monitor.getLogLevelMenuItems();
+
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node n = nodeList.item(i);
+ NamedNodeMap map = n.getAttributes();
+ String name = getValue(map, NAME);
+ try {
+ JCheckBoxMenuItem item =
+ (JCheckBoxMenuItem) menuItems.get(LogLevel.valueOf(name));
+ item.setSelected(getValue(map, SELECTED).equalsIgnoreCase("true"));
+ } catch (LogLevelFormatException e) {
+ // ignore it will be on by default.
+ }
+ }
+ }
+
+ protected void processLogLevelColors(Document doc) {
+ NodeList nodeList = doc.getElementsByTagName(COLORLEVEL);
+ LogLevel.getLogLevelColorMap();
+
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node n = nodeList.item(i);
+ // check for backwards compatibility since this feature was added
+ // in version 1.3
+ if (n == null) {
+ return;
+ }
+
+ NamedNodeMap map = n.getAttributes();
+ String name = getValue(map, NAME);
+ try {
+ LogLevel level = LogLevel.valueOf(name);
+ int red = Integer.parseInt(getValue(map, RED));
+ int green = Integer.parseInt(getValue(map, GREEN));
+ int blue = Integer.parseInt(getValue(map, BLUE));
+ Color c = new Color(red, green, blue);
+ if (level != null) {
+ level.setLogLevelColorMap(level, c);
+ }
+
+ } catch (LogLevelFormatException e) {
+ // ignore it will be on by default.
+ }
+ }
+ }
+
+ protected void processLogTableColumns(Document doc) {
+ NodeList nodeList = doc.getElementsByTagName(COLUMN);
+ Map menuItems = _monitor.getLogTableColumnMenuItems();
+ List selectedColumns = new ArrayList();
+ for (int i = 0; i < nodeList.getLength(); i++) {
+ Node n = nodeList.item(i);
+ // check for backwards compatibility since this feature was added
+ // in version 1.3
+ if (n == null) {
+ return;
+ }
+ NamedNodeMap map = n.getAttributes();
+ String name = getValue(map, NAME);
+ try {
+ LogTableColumn column = LogTableColumn.valueOf(name);
+ JCheckBoxMenuItem item =
+ (JCheckBoxMenuItem) menuItems.get(column);
+ item.setSelected(getValue(map, SELECTED).equalsIgnoreCase("true"));
+
+ if (item.isSelected()) {
+ selectedColumns.add(column);
+ }
+ } catch (LogTableColumnFormatException e) {
+ // ignore it will be on by default.
+ }
+
+ if (selectedColumns.isEmpty()) {
+ _table.setDetailedView();
+ } else {
+ _table.setView(selectedColumns);
+ }
+
+ }
+ }
+
+ protected String getValue(NamedNodeMap map, String attr) {
+ Node n = map.getNamedItem(attr);
+ return n.getNodeValue();
+ }
+
+ protected void collapseTree() {
+ // collapse everything except the first category
+ CategoryExplorerTree tree = _monitor.getCategoryExplorerTree();
+ for (int i = tree.getRowCount() - 1; i > 0; i--) {
+ tree.collapseRow(i);
+ }
+ }
+
+ protected void selectAllNodes() {
+ CategoryExplorerModel model = _monitor.getCategoryExplorerTree().getExplorerModel();
+ CategoryNode root = model.getRootCategoryNode();
+ Enumeration all = root.breadthFirstEnumeration();
+ CategoryNode n = null;
+ while (all.hasMoreElements()) {
+ n = (CategoryNode) all.nextElement();
+ n.setSelected(true);
+ }
+ }
+
+ protected void store(String s) {
+
+ try {
+ PrintWriter writer = new PrintWriter(new FileWriter(getFilename()));
+ writer.print(s);
+ writer.close();
+ } catch (IOException e) {
+ // do something with this error.
+ e.printStackTrace();
+ }
+
+ }
+
+ protected void deleteConfigurationFile() {
+ try {
+ File f = new File(getFilename());
+ if (f.exists()) {
+ f.delete();
+ }
+ } catch (SecurityException e) {
+ System.err.println("Cannot delete " + getFilename() +
+ " because a security violation occured.");
+ }
+ }
+
+ protected String getFilename() {
+ String home = System.getProperty("user.home");
+ String sep = System.getProperty("file.separator");
+
+ return home + sep + "lf5" + sep + CONFIG_FILE_NAME;
+ }
+
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+ private void processConfigurationNode(CategoryNode node, StringBuffer xml) {
+ CategoryExplorerModel model = _monitor.getCategoryExplorerTree().getExplorerModel();
+
+ Enumeration all = node.breadthFirstEnumeration();
+ CategoryNode n = null;
+ while (all.hasMoreElements()) {
+ n = (CategoryNode) all.nextElement();
+ exportXMLElement(n, model.getTreePathToRoot(n), xml);
+ }
+
+ }
+
+ private void processLogLevels(Map logLevelMenuItems, StringBuffer xml) {
+ xml.append("\t\r\n");
+ Iterator it = logLevelMenuItems.keySet().iterator();
+ while (it.hasNext()) {
+ LogLevel level = (LogLevel) it.next();
+ JCheckBoxMenuItem item = (JCheckBoxMenuItem) logLevelMenuItems.get(level);
+ exportLogLevelXMLElement(level.getLabel(), item.isSelected(), xml);
+ }
+
+ xml.append("\t\r\n");
+ }
+
+ private void processLogLevelColors(Map logLevelMenuItems, Map logLevelColors, StringBuffer xml) {
+ xml.append("\t\r\n");
+ // iterate through the list of log levels being used (log4j, jdk1.4, custom levels)
+ Iterator it = logLevelMenuItems.keySet().iterator();
+ while (it.hasNext()) {
+ LogLevel level = (LogLevel) it.next();
+ // for each level, get the associated color from the log level color map
+ Color color = (Color) logLevelColors.get(level);
+ exportLogLevelColorXMLElement(level.getLabel(), color, xml);
+ }
+
+ xml.append("\t\r\n");
+ }
+
+
+ private void processLogTableColumns(List logTableColumnMenuItems, StringBuffer xml) {
+ xml.append("\t\r\n");
+ Iterator it = logTableColumnMenuItems.iterator();
+ while (it.hasNext()) {
+ LogTableColumn column = (LogTableColumn) it.next();
+ JCheckBoxMenuItem item = _monitor.getTableColumnMenuItem(column);
+ exportLogTableColumnXMLElement(column.getLabel(), item.isSelected(), xml);
+ }
+
+ xml.append("\t\r\n");
+ }
+
+ // Added in version 1.2 - stores the NDC text filter in the xml file
+ // for future use.
+ private void processLogRecordFilter(String text, StringBuffer xml) {
+ xml.append("\t<").append(NDCTEXTFILTER).append(" ");
+ xml.append(NAME).append("=\"").append(text).append("\"");
+ xml.append("/>\r\n");
+ }
+
+ private void openXMLDocument(StringBuffer xml) {
+ xml.append("\r\n");
+ }
+
+ private void openConfigurationXML(StringBuffer xml) {
+ xml.append("\r\n");
+ }
+
+ private void closeConfigurationXML(StringBuffer xml) {
+ xml.append("\r\n");
+ }
+
+ private void exportXMLElement(CategoryNode node, TreePath path, StringBuffer xml) {
+ CategoryExplorerTree tree = _monitor.getCategoryExplorerTree();
+
+ xml.append("\t<").append(CATEGORY).append(" ");
+ xml.append(NAME).append("=\"").append(node.getTitle()).append("\" ");
+ xml.append(PATH).append("=\"").append(treePathToString(path)).append("\" ");
+ xml.append(EXPANDED).append("=\"").append(tree.isExpanded(path)).append("\" ");
+ xml.append(SELECTED).append("=\"").append(node.isSelected()).append("\"/>\r\n");
+ }
+
+ private void exportLogLevelXMLElement(String label, boolean selected, StringBuffer xml) {
+ xml.append("\t\t<").append(LEVEL).append(" ").append(NAME);
+ xml.append("=\"").append(label).append("\" ");
+ xml.append(SELECTED).append("=\"").append(selected);
+ xml.append("\"/>\r\n");
+ }
+
+ private void exportLogLevelColorXMLElement(String label, Color color, StringBuffer xml) {
+ xml.append("\t\t<").append(COLORLEVEL).append(" ").append(NAME);
+ xml.append("=\"").append(label).append("\" ");
+ xml.append(RED).append("=\"").append(color.getRed()).append("\" ");
+ xml.append(GREEN).append("=\"").append(color.getGreen()).append("\" ");
+ xml.append(BLUE).append("=\"").append(color.getBlue());
+ xml.append("\"/>\r\n");
+ }
+
+ private void exportLogTableColumnXMLElement(String label, boolean selected, StringBuffer xml) {
+ xml.append("\t\t<").append(COLUMN).append(" ").append(NAME);
+ xml.append("=\"").append(label).append("\" ");
+ xml.append(SELECTED).append("=\"").append(selected);
+ xml.append("\"/>\r\n");
+ }
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces:
+ //--------------------------------------------------------------------------
+
+}
+
+
+
+
+
+
diff --git a/java/src/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java b/java/src/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java
new file mode 100644
index 0000000..6ff275d
--- /dev/null
+++ b/java/src/org/apache/log4j/lf5/viewer/configure/MRUFileManager.java
@@ -0,0 +1,293 @@
+/*
+ * 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.log4j.lf5.viewer.configure;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.net.URL;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+
+/**
+ *
MRUFileManager handles the storage and retrival the most
+ * recently opened log files.
+ *
+ * @author Brad Marlborough
+ * @author Richard Hurst
+ */
+
+// Contributed by ThoughtWorks Inc.
+
+public class MRUFileManager {
+ //--------------------------------------------------------------------------
+ // Constants:
+ //--------------------------------------------------------------------------
+ private static final String CONFIG_FILE_NAME = "mru_file_manager";
+ private static final int DEFAULT_MAX_SIZE = 3;
+
+ //--------------------------------------------------------------------------
+ // Protected Variables:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Private Variables:
+ //--------------------------------------------------------------------------
+ private int _maxSize = 0;
+ private LinkedList _mruFileList;
+
+ //--------------------------------------------------------------------------
+ // Constructors:
+ //--------------------------------------------------------------------------
+ public MRUFileManager() {
+ load();
+ setMaxSize(DEFAULT_MAX_SIZE);
+ }
+
+ public MRUFileManager(int maxSize) {
+ load();
+ setMaxSize(maxSize);
+ }
+ //--------------------------------------------------------------------------
+ // Public Methods:
+ //--------------------------------------------------------------------------
+
+ /**
+ * Saves a list of MRU files out to a file.
+ */
+ public void save() {
+ File file = new File(getFilename());
+
+ try {
+ ObjectOutputStream oos = new ObjectOutputStream(new
+ FileOutputStream(file));
+ oos.writeObject(_mruFileList);
+ oos.flush();
+ oos.close();
+ } catch (Exception e) {
+ // do nothing
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * Gets the size of the MRU file list.
+ */
+ public int size() {
+ return _mruFileList.size();
+ }
+
+ /**
+ * Returns a particular file name stored in a MRU file
+ * list based on an index value.
+ */
+ public Object getFile(int index) {
+ if (index < size()) {
+ return _mruFileList.get(index);
+ }
+
+ return null;
+ }
+
+ /**
+ * Returns a input stream to the resource at the specified index
+ */
+ public InputStream getInputStream(int index) throws IOException,
+ FileNotFoundException {
+ if (index < size()) {
+ Object o = getFile(index);
+ if (o instanceof File) {
+ return getInputStream((File) o);
+ } else {
+ return getInputStream((URL) o);
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Adds a file name to the MRU file list.
+ */
+ public void set(File file) {
+ setMRU(file);
+ }
+
+ /**
+ * Adds a url to the MRU file list.
+ */
+ public void set(URL url) {
+ setMRU(url);
+ }
+
+ /**
+ * Gets the list of files stored in the MRU file list.
+ */
+ public String[] getMRUFileList() {
+ if (size() == 0) {
+ return null;
+ }
+
+ String[] ss = new String[size()];
+
+ for (int i = 0; i < size(); i++) {
+ Object o = getFile(i);
+ if (o instanceof File) {
+ ss[i] = ((File) o).getAbsolutePath();
+ } else // must be a url
+ {
+ ss[i] = o.toString();
+ }
+
+ }
+
+ return ss;
+ }
+
+ /**
+ * Moves the the index to the top of the MRU List
+ *
+ * @param index The index to be first in the mru list
+ */
+ public void moveToTop(int index) {
+ _mruFileList.add(0, _mruFileList.remove(index));
+ }
+
+ /**
+ * Creates the directory where the MRU file list will be written.
+ * The "lf5" directory is created in the Documents and Settings
+ * directory on Windows 2000 machines and where ever the user.home
+ * variable points on all other platforms.
+ */
+ public static void createConfigurationDirectory() {
+ String home = System.getProperty("user.home");
+ String sep = System.getProperty("file.separator");
+ File f = new File(home + sep + "lf5");
+ if (!f.exists()) {
+ try {
+ f.mkdir();
+ } catch (SecurityException e) {
+ e.printStackTrace();
+ }
+ }
+
+ }
+ //--------------------------------------------------------------------------
+ // Protected Methods:
+ //--------------------------------------------------------------------------
+ /**
+ * Gets an input stream for the corresponding file.
+ *
+ * @param file The file to create the input stream from.
+ * @return InputStream
+ */
+ protected InputStream getInputStream(File file) throws IOException,
+ FileNotFoundException {
+ BufferedInputStream reader =
+ new BufferedInputStream(new FileInputStream(file));
+
+ return reader;
+ }
+
+ /**
+ * Gets an input stream for the corresponding URL.
+ *
+ * @param url The url to create the input stream from.
+ * @return InputStream
+ */
+ protected InputStream getInputStream(URL url) throws IOException {
+ return url.openStream();
+ }
+
+ /**
+ * Adds an object to the mru.
+ */
+ protected void setMRU(Object o) {
+ int index = _mruFileList.indexOf(o);
+
+ if (index == -1) {
+ _mruFileList.add(0, o);
+ setMaxSize(_maxSize);
+ } else {
+ moveToTop(index);
+ }
+ }
+
+ /**
+ * Loads the MRU file list in from a file and stores it in a LinkedList.
+ * If no file exists, a new LinkedList is created.
+ */
+ protected void load() {
+ createConfigurationDirectory();
+ File file = new File(getFilename());
+ if (file.exists()) {
+ try {
+ ObjectInputStream ois = new ObjectInputStream(
+ new FileInputStream(file));
+ _mruFileList = (LinkedList) ois.readObject();
+ ois.close();
+
+ // check that only files and url are in linked list
+ Iterator it = _mruFileList.iterator();
+ while (it.hasNext()) {
+ Object o = it.next();
+ if (!(o instanceof File) && !(o instanceof URL)) {
+ it.remove();
+ }
+ }
+ } catch (Exception e) {
+ _mruFileList = new LinkedList();
+ }
+ } else {
+ _mruFileList = new LinkedList();
+ }
+
+ }
+
+ protected String getFilename() {
+ String home = System.getProperty("user.home");
+ String sep = System.getProperty("file.separator");
+
+ return home + sep + "lf5" + sep + CONFIG_FILE_NAME;
+ }
+
+ /**
+ * Ensures that the MRU list will have a MaxSize.
+ */
+ protected void setMaxSize(int maxSize) {
+ if (maxSize < _mruFileList.size()) {
+ for (int i = 0; i < _mruFileList.size() - maxSize; i++) {
+ _mruFileList.removeLast();
+ }
+ }
+
+ _maxSize = maxSize;
+ }
+ //--------------------------------------------------------------------------
+ // Private Methods:
+ //--------------------------------------------------------------------------
+
+ //--------------------------------------------------------------------------
+ // Nested Top-Level Classes or Interfaces
+ //--------------------------------------------------------------------------
+}
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/net/JMSAppender.java b/java/src/org/apache/log4j/net/JMSAppender.java
new file mode 100644
index 0000000..60bd5d0
--- /dev/null
+++ b/java/src/org/apache/log4j/net/JMSAppender.java
@@ -0,0 +1,444 @@
+/*
+ * 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.log4j.net;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.LoggingEvent;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicPublisher;
+import javax.jms.TopicSession;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import java.util.Properties;
+
+/**
+ * A simple appender that publishes events to a JMS Topic. The events
+ * are serialized and transmitted as JMS message type {@link
+ * ObjectMessage}.
+
+ *
JMS {@link Topic topics} and {@link TopicConnectionFactory topic
+ * connection factories} are administered objects that are retrieved
+ * using JNDI messaging which in turn requires the retreival of a JNDI
+ * {@link Context}.
+
+ *
There are two common methods for retrieving a JNDI {@link
+ * Context}. If a file resource named jndi.properties is
+ * available to the JNDI API, it will use the information found
+ * therein to retrieve an initial JNDI context. To obtain an initial
+ * context, your code will simply call:
+
+
+ InitialContext jndiContext = new InitialContext();
+
+
+ * Calling the no-argument InitialContext() method
+ * will also work from within Enterprise Java Beans (EJBs) because it
+ * is part of the EJB contract for application servers to provide each
+ * bean an environment naming context (ENC).
+
+ *
In the second approach, several predetermined properties are set
+ * and these properties are passed to the InitialContext
+ * contructor to connect to the naming service provider. For example,
+ * to connect to JBoss naming service one would write:
+
+
+ Properties env = new Properties( );
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "org.jnp.interfaces.NamingContextFactory");
+ env.put(Context.PROVIDER_URL, "jnp://hostname:1099");
+ env.put(Context.URL_PKG_PREFIXES, "org.jboss.naming:org.jnp.interfaces");
+ InitialContext jndiContext = new InitialContext(env);
+
+
+ * where hostname is the host where the JBoss applicaiton
+ * server is running.
+ *
+ * To connect to the the naming service of Weblogic application
+ * server one would write:
+
+
+ Properties env = new Properties( );
+ env.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
+ env.put(Context.PROVIDER_URL, "t3://localhost:7001");
+ InitialContext jndiContext = new InitialContext(env);
+
+
+ * Other JMS providers will obviously require different values.
+ *
+ * The initial JNDI context can be obtained by calling the
+ * no-argument InitialContext() method in EJBs. Only
+ * clients running in a separate JVM need to be concerned about the
+ * jndi.properties file and calling {@link
+ * InitialContext#InitialContext()} or alternatively correctly
+ * setting the different properties before calling {@link
+ * InitialContext#InitialContext(java.util.Hashtable)} method.
+
+
+ @author Ceki Gülcü */
+public class JMSAppender extends AppenderSkeleton {
+
+ String securityPrincipalName;
+ String securityCredentials;
+ String initialContextFactoryName;
+ String urlPkgPrefixes;
+ String providerURL;
+ String topicBindingName;
+ String tcfBindingName;
+ String userName;
+ String password;
+ boolean locationInfo;
+
+ TopicConnection topicConnection;
+ TopicSession topicSession;
+ TopicPublisher topicPublisher;
+
+ public
+ JMSAppender() {
+ }
+
+ /**
+ The TopicConnectionFactoryBindingName option takes a
+ string value. Its value will be used to lookup the appropriate
+ TopicConnectionFactory from the JNDI context.
+ */
+ public
+ void setTopicConnectionFactoryBindingName(String tcfBindingName) {
+ this.tcfBindingName = tcfBindingName;
+ }
+
+ /**
+ Returns the value of the TopicConnectionFactoryBindingName option.
+ */
+ public
+ String getTopicConnectionFactoryBindingName() {
+ return tcfBindingName;
+ }
+
+ /**
+ The TopicBindingName option takes a
+ string value. Its value will be used to lookup the appropriate
+ Topic from the JNDI context.
+ */
+ public
+ void setTopicBindingName(String topicBindingName) {
+ this.topicBindingName = topicBindingName;
+ }
+
+ /**
+ Returns the value of the TopicBindingName option.
+ */
+ public
+ String getTopicBindingName() {
+ return topicBindingName;
+ }
+
+
+ /**
+ Returns value of the LocationInfo property which
+ determines whether location (stack) info is sent to the remote
+ subscriber. */
+ public
+ boolean getLocationInfo() {
+ return locationInfo;
+ }
+
+ /**
+ * Options are activated and become effective only after calling
+ * this method.*/
+ public void activateOptions() {
+ TopicConnectionFactory topicConnectionFactory;
+
+ try {
+ Context jndi;
+
+ LogLog.debug("Getting initial context.");
+ if(initialContextFactoryName != null) {
+ Properties env = new Properties( );
+ env.put(Context.INITIAL_CONTEXT_FACTORY, initialContextFactoryName);
+ if(providerURL != null) {
+ env.put(Context.PROVIDER_URL, providerURL);
+ } else {
+ LogLog.warn("You have set InitialContextFactoryName option but not the "
+ +"ProviderURL. This is likely to cause problems.");
+ }
+ if(urlPkgPrefixes != null) {
+ env.put(Context.URL_PKG_PREFIXES, urlPkgPrefixes);
+ }
+
+ if(securityPrincipalName != null) {
+ env.put(Context.SECURITY_PRINCIPAL, securityPrincipalName);
+ if(securityCredentials != null) {
+ env.put(Context.SECURITY_CREDENTIALS, securityCredentials);
+ } else {
+ LogLog.warn("You have set SecurityPrincipalName option but not the "
+ +"SecurityCredentials. This is likely to cause problems.");
+ }
+ }
+ jndi = new InitialContext(env);
+ } else {
+ jndi = new InitialContext();
+ }
+
+ LogLog.debug("Looking up ["+tcfBindingName+"]");
+ topicConnectionFactory = (TopicConnectionFactory) lookup(jndi, tcfBindingName);
+ LogLog.debug("About to create TopicConnection.");
+ if(userName != null) {
+ topicConnection = topicConnectionFactory.createTopicConnection(userName,
+ password);
+ } else {
+ topicConnection = topicConnectionFactory.createTopicConnection();
+ }
+
+ LogLog.debug("Creating TopicSession, non-transactional, "
+ +"in AUTO_ACKNOWLEDGE mode.");
+ topicSession = topicConnection.createTopicSession(false,
+ Session.AUTO_ACKNOWLEDGE);
+
+ LogLog.debug("Looking up topic name ["+topicBindingName+"].");
+ Topic topic = (Topic) lookup(jndi, topicBindingName);
+
+ LogLog.debug("Creating TopicPublisher.");
+ topicPublisher = topicSession.createPublisher(topic);
+
+ LogLog.debug("Starting TopicConnection.");
+ topicConnection.start();
+
+ jndi.close();
+ } catch(JMSException e) {
+ errorHandler.error("Error while activating options for appender named ["+name+
+ "].", e, ErrorCode.GENERIC_FAILURE);
+ } catch(NamingException e) {
+ errorHandler.error("Error while activating options for appender named ["+name+
+ "].", e, ErrorCode.GENERIC_FAILURE);
+ } catch(RuntimeException e) {
+ errorHandler.error("Error while activating options for appender named ["+name+
+ "].", e, ErrorCode.GENERIC_FAILURE);
+ }
+ }
+
+ protected Object lookup(Context ctx, String name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch(NameNotFoundException e) {
+ LogLog.error("Could not find name ["+name+"].");
+ throw e;
+ }
+ }
+
+ protected boolean checkEntryConditions() {
+ String fail = null;
+
+ if(this.topicConnection == null) {
+ fail = "No TopicConnection";
+ } else if(this.topicSession == null) {
+ fail = "No TopicSession";
+ } else if(this.topicPublisher == null) {
+ fail = "No TopicPublisher";
+ }
+
+ if(fail != null) {
+ errorHandler.error(fail +" for JMSAppender named ["+name+"].");
+ return false;
+ } else {
+ return true;
+ }
+ }
+
+ /**
+ Close this JMSAppender. Closing releases all resources used by the
+ appender. A closed appender cannot be re-opened. */
+ public synchronized void close() {
+ // The synchronized modifier avoids concurrent append and close operations
+
+ if(this.closed)
+ return;
+
+ LogLog.debug("Closing appender ["+name+"].");
+ this.closed = true;
+
+ try {
+ if(topicSession != null)
+ topicSession.close();
+ if(topicConnection != null)
+ topicConnection.close();
+ } catch(JMSException e) {
+ LogLog.error("Error while closing JMSAppender ["+name+"].", e);
+ } catch(RuntimeException e) {
+ LogLog.error("Error while closing JMSAppender ["+name+"].", e);
+ }
+ // Help garbage collection
+ topicPublisher = null;
+ topicSession = null;
+ topicConnection = null;
+ }
+
+ /**
+ This method called by {@link AppenderSkeleton#doAppend} method to
+ do most of the real appending work. */
+ public void append(LoggingEvent event) {
+ if(!checkEntryConditions()) {
+ return;
+ }
+
+ try {
+ ObjectMessage msg = topicSession.createObjectMessage();
+ if(locationInfo) {
+ event.getLocationInformation();
+ }
+ msg.setObject(event);
+ topicPublisher.publish(msg);
+ } catch(JMSException e) {
+ errorHandler.error("Could not publish message in JMSAppender ["+name+"].", e,
+ ErrorCode.GENERIC_FAILURE);
+ } catch(RuntimeException e) {
+ errorHandler.error("Could not publish message in JMSAppender ["+name+"].", e,
+ ErrorCode.GENERIC_FAILURE);
+ }
+ }
+
+ /**
+ * Returns the value of the InitialContextFactoryName option.
+ * See {@link #setInitialContextFactoryName} for more details on the
+ * meaning of this option.
+ * */
+ public String getInitialContextFactoryName() {
+ return initialContextFactoryName;
+ }
+
+ /**
+ * Setting the InitialContextFactoryName method will cause
+ * this JMSAppender instance to use the {@link
+ * InitialContext#InitialContext(Hashtable)} method instead of the
+ * no-argument constructor. If you set this option, you should also
+ * at least set the ProviderURL option.
+ *
+ *
See also {@link #setProviderURL(String)}.
+ * */
+ public void setInitialContextFactoryName(String initialContextFactoryName) {
+ this.initialContextFactoryName = initialContextFactoryName;
+ }
+
+ public String getProviderURL() {
+ return providerURL;
+ }
+
+ public void setProviderURL(String providerURL) {
+ this.providerURL = providerURL;
+ }
+
+ String getURLPkgPrefixes( ) {
+ return urlPkgPrefixes;
+ }
+
+ public void setURLPkgPrefixes(String urlPkgPrefixes ) {
+ this.urlPkgPrefixes = urlPkgPrefixes;
+ }
+
+ public String getSecurityCredentials() {
+ return securityCredentials;
+ }
+
+ public void setSecurityCredentials(String securityCredentials) {
+ this.securityCredentials = securityCredentials;
+ }
+
+
+ public String getSecurityPrincipalName() {
+ return securityPrincipalName;
+ }
+
+ public void setSecurityPrincipalName(String securityPrincipalName) {
+ this.securityPrincipalName = securityPrincipalName;
+ }
+
+ public String getUserName() {
+ return userName;
+ }
+
+ /**
+ * The user name to use when {@link
+ * TopicConnectionFactory#createTopicConnection(String, String)
+ * creating a topic session}. If you set this option, you should
+ * also set the Password option. See {@link
+ * #setPassword(String)}.
+ * */
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ /**
+ * The paswword to use when creating a topic session.
+ */
+ public void setPassword(String password) {
+ this.password = password;
+ }
+
+
+ /**
+ If true, the information sent to the remote subscriber will
+ include caller's location information. By default no location
+ information is sent to the subscriber. */
+ public void setLocationInfo(boolean locationInfo) {
+ this.locationInfo = locationInfo;
+ }
+
+ /**
+ * Returns the TopicConnection used for this appender. Only valid after
+ * activateOptions() method has been invoked.
+ */
+ protected TopicConnection getTopicConnection() {
+ return topicConnection;
+ }
+
+ /**
+ * Returns the TopicSession used for this appender. Only valid after
+ * activateOptions() method has been invoked.
+ */
+ protected TopicSession getTopicSession() {
+ return topicSession;
+ }
+
+ /**
+ * Returns the TopicPublisher used for this appender. Only valid after
+ * activateOptions() method has been invoked.
+ */
+ protected TopicPublisher getTopicPublisher() {
+ return topicPublisher;
+ }
+
+ /**
+ * The JMSAppender sends serialized events and consequently does not
+ * require a layout.
+ */
+ public boolean requiresLayout() {
+ return false;
+ }
+}
diff --git a/java/src/org/apache/log4j/net/JMSSink.java b/java/src/org/apache/log4j/net/JMSSink.java
new file mode 100644
index 0000000..6a02831
--- /dev/null
+++ b/java/src/org/apache/log4j/net/JMSSink.java
@@ -0,0 +1,153 @@
+/*
+ * 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.log4j.net;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.xml.DOMConfigurator;
+
+import javax.jms.JMSException;
+import javax.jms.ObjectMessage;
+import javax.jms.Session;
+import javax.jms.Topic;
+import javax.jms.TopicConnection;
+import javax.jms.TopicConnectionFactory;
+import javax.jms.TopicSession;
+import javax.jms.TopicSubscriber;
+import javax.naming.Context;
+import javax.naming.InitialContext;
+import javax.naming.NameNotFoundException;
+import javax.naming.NamingException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+
+/**
+ * A simple application that consumes logging events sent by a {@link
+ * JMSAppender}.
+ *
+ *
+ * @author Ceki Gülcü
+ * */
+public class JMSSink implements javax.jms.MessageListener {
+
+ static Logger logger = Logger.getLogger(JMSSink.class);
+
+ static public void main(String[] args) throws Exception {
+ if(args.length != 5) {
+ usage("Wrong number of arguments.");
+ }
+
+ String tcfBindingName = args[0];
+ String topicBindingName = args[1];
+ String username = args[2];
+ String password = args[3];
+
+
+ String configFile = args[4];
+
+ if(configFile.endsWith(".xml")) {
+ DOMConfigurator.configure(configFile);
+ } else {
+ PropertyConfigurator.configure(configFile);
+ }
+
+ new JMSSink(tcfBindingName, topicBindingName, username, password);
+
+ BufferedReader stdin = new BufferedReader(new InputStreamReader(System.in));
+ // Loop until the word "exit" is typed
+ System.out.println("Type \"exit\" to quit JMSSink.");
+ while(true){
+ String s = stdin.readLine( );
+ if (s.equalsIgnoreCase("exit")) {
+ System.out.println("Exiting. Kill the application if it does not exit "
+ + "due to daemon threads.");
+ return;
+ }
+ }
+ }
+
+ public JMSSink( String tcfBindingName, String topicBindingName, String username,
+ String password) {
+
+ try {
+ Context ctx = new InitialContext();
+ TopicConnectionFactory topicConnectionFactory;
+ topicConnectionFactory = (TopicConnectionFactory) lookup(ctx,
+ tcfBindingName);
+
+ TopicConnection topicConnection =
+ topicConnectionFactory.createTopicConnection(username,
+ password);
+ topicConnection.start();
+
+ TopicSession topicSession = topicConnection.createTopicSession(false,
+ Session.AUTO_ACKNOWLEDGE);
+
+ Topic topic = (Topic)ctx.lookup(topicBindingName);
+
+ TopicSubscriber topicSubscriber = topicSession.createSubscriber(topic);
+
+ topicSubscriber.setMessageListener(this);
+
+ } catch(JMSException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch(NamingException e) {
+ logger.error("Could not read JMS message.", e);
+ } catch(RuntimeException e) {
+ logger.error("Could not read JMS message.", e);
+ }
+ }
+
+ public void onMessage(javax.jms.Message message) {
+ LoggingEvent event;
+ Logger remoteLogger;
+
+ try {
+ if(message instanceof ObjectMessage) {
+ ObjectMessage objectMessage = (ObjectMessage) message;
+ event = (LoggingEvent) objectMessage.getObject();
+ remoteLogger = Logger.getLogger(event.getLoggerName());
+ remoteLogger.callAppenders(event);
+ } else {
+ logger.warn("Received message is of type "+message.getJMSType()
+ +", was expecting ObjectMessage.");
+ }
+ } catch(JMSException jmse) {
+ logger.error("Exception thrown while processing incoming message.",
+ jmse);
+ }
+ }
+
+
+ protected static Object lookup(Context ctx, String name) throws NamingException {
+ try {
+ return ctx.lookup(name);
+ } catch(NameNotFoundException e) {
+ logger.error("Could not find name ["+name+"].");
+ throw e;
+ }
+ }
+
+ static void usage(String msg) {
+ System.err.println(msg);
+ System.err.println("Usage: java " + JMSSink.class.getName()
+ + " TopicConnectionFactoryBindingName TopicBindingName username password configFile");
+ System.exit(1);
+ }
+}
diff --git a/java/src/org/apache/log4j/net/SMTPAppender.java b/java/src/org/apache/log4j/net/SMTPAppender.java
new file mode 100644
index 0000000..7350474
--- /dev/null
+++ b/java/src/org/apache/log4j/net/SMTPAppender.java
@@ -0,0 +1,787 @@
+/*
+ * 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.log4j.net;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.helpers.CyclicBuffer;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.OptionHandler;
+import org.apache.log4j.spi.TriggeringEventEvaluator;
+import org.apache.log4j.xml.UnrecognizedElementHandler;
+import org.w3c.dom.Element;
+
+import javax.mail.Authenticator;
+import javax.mail.Message;
+import javax.mail.MessagingException;
+import javax.mail.Multipart;
+import javax.mail.PasswordAuthentication;
+import javax.mail.Session;
+import javax.mail.Transport;
+import javax.mail.internet.AddressException;
+import javax.mail.internet.InternetAddress;
+import javax.mail.internet.InternetHeaders;
+import javax.mail.internet.MimeBodyPart;
+import javax.mail.internet.MimeMessage;
+import javax.mail.internet.MimeMultipart;
+import javax.mail.internet.MimeUtility;
+import java.io.ByteArrayOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.UnsupportedEncodingException;
+import java.io.Writer;
+import java.util.Date;
+import java.util.Properties;
+
+/**
+ Send an e-mail when a specific logging event occurs, typically on
+ errors or fatal errors.
+
+
The number of logging events delivered in this e-mail depend on
+ the value of BufferSize option. The
+ SMTPAppender keeps only the last
+ BufferSize logging events in its cyclic buffer. This
+ keeps memory requirements at a reasonable level while still
+ delivering useful application context.
+
+ By default, an email message will be sent when an ERROR or higher
+ severity message is appended. The triggering criteria can be
+ modified by setting the evaluatorClass property with the name
+ of a class implementing TriggeringEventEvaluator, setting the evaluator
+ property with an instance of TriggeringEventEvaluator or
+ nesting a triggeringPolicy element where the specified
+ class implements TriggeringEventEvaluator.
+
+ This class has implemented UnrecognizedElementHandler since 1.2.15.
+
+ Since 1.2.16, SMTP over SSL is supported by setting SMTPProtocol to "smpts".
+
+ @author Ceki Gülcü
+ @since 1.0 */
+public class SMTPAppender extends AppenderSkeleton
+ implements UnrecognizedElementHandler {
+ private String to;
+ /**
+ * Comma separated list of cc recipients.
+ */
+ private String cc;
+ /**
+ * Comma separated list of bcc recipients.
+ */
+ private String bcc;
+ private String from;
+ /**
+ * Comma separated list of replyTo addresses.
+ */
+ private String replyTo;
+ private String subject;
+ private String smtpHost;
+ private String smtpUsername;
+ private String smtpPassword;
+ private String smtpProtocol;
+ private int smtpPort = -1;
+ private boolean smtpDebug = false;
+ private int bufferSize = 512;
+ private boolean locationInfo = false;
+ private boolean sendOnClose = false;
+
+ protected CyclicBuffer cb = new CyclicBuffer(bufferSize);
+ protected Message msg;
+
+ protected TriggeringEventEvaluator evaluator;
+
+
+
+ /**
+ The default constructor will instantiate the appender with a
+ {@link TriggeringEventEvaluator} that will trigger on events with
+ level ERROR or higher.*/
+ public
+ SMTPAppender() {
+ this(new DefaultEvaluator());
+ }
+
+
+ /**
+ Use evaluator passed as parameter as the {@link
+ TriggeringEventEvaluator} for this SMTPAppender. */
+ public
+ SMTPAppender(TriggeringEventEvaluator evaluator) {
+ this.evaluator = evaluator;
+ }
+
+
+ /**
+ Activate the specified options, such as the smtp host, the
+ recipient, from, etc. */
+ public
+ void activateOptions() {
+ Session session = createSession();
+ msg = new MimeMessage(session);
+
+ try {
+ addressMessage(msg);
+ if(subject != null) {
+ try {
+ msg.setSubject(MimeUtility.encodeText(subject, "UTF-8", null));
+ } catch(UnsupportedEncodingException ex) {
+ LogLog.error("Unable to encode SMTP subject", ex);
+ }
+ }
+ } catch(MessagingException e) {
+ LogLog.error("Could not activate SMTPAppender options.", e );
+ }
+
+ if (evaluator instanceof OptionHandler) {
+ ((OptionHandler) evaluator).activateOptions();
+ }
+ }
+
+ /**
+ * Address message.
+ * @param msg message, may not be null.
+ * @throws MessagingException thrown if error addressing message.
+ * @since 1.2.14
+ */
+ protected void addressMessage(final Message msg) throws MessagingException {
+ if (from != null) {
+ msg.setFrom(getAddress(from));
+ } else {
+ msg.setFrom();
+ }
+
+ //Add ReplyTo addresses if defined.
+ if (replyTo != null && replyTo.length() > 0) {
+ msg.setReplyTo(parseAddress(replyTo));
+ }
+
+ if (to != null && to.length() > 0) {
+ msg.setRecipients(Message.RecipientType.TO, parseAddress(to));
+ }
+
+ //Add CC receipients if defined.
+ if (cc != null && cc.length() > 0) {
+ msg.setRecipients(Message.RecipientType.CC, parseAddress(cc));
+ }
+
+ //Add BCC receipients if defined.
+ if (bcc != null && bcc.length() > 0) {
+ msg.setRecipients(Message.RecipientType.BCC, parseAddress(bcc));
+ }
+ }
+
+ /**
+ * Create mail session.
+ * @return mail session, may not be null.
+ * @since 1.2.14
+ */
+ protected Session createSession() {
+ Properties props = null;
+ try {
+ props = new Properties (System.getProperties());
+ } catch(SecurityException ex) {
+ props = new Properties();
+ }
+
+ String prefix = "mail.smtp";
+ if (smtpProtocol != null) {
+ props.put("mail.transport.protocol", smtpProtocol);
+ prefix = "mail." + smtpProtocol;
+ }
+ if (smtpHost != null) {
+ props.put(prefix + ".host", smtpHost);
+ }
+ if (smtpPort > 0) {
+ props.put(prefix + ".port", String.valueOf(smtpPort));
+ }
+
+ Authenticator auth = null;
+ if(smtpPassword != null && smtpUsername != null) {
+ props.put(prefix + ".auth", "true");
+ auth = new Authenticator() {
+ protected PasswordAuthentication getPasswordAuthentication() {
+ return new PasswordAuthentication(smtpUsername, smtpPassword);
+ }
+ };
+ }
+ Session session = Session.getInstance(props, auth);
+ if (smtpProtocol != null) {
+ session.setProtocolForAddress("rfc822", smtpProtocol);
+ }
+ if (smtpDebug) {
+ session.setDebug(smtpDebug);
+ }
+ return session;
+ }
+
+ /**
+ Perform SMTPAppender specific appending actions, mainly adding
+ the event to a cyclic buffer and checking if the event triggers
+ an e-mail to be sent. */
+ public
+ void append(LoggingEvent event) {
+
+ if(!checkEntryConditions()) {
+ return;
+ }
+
+ event.getThreadName();
+ event.getNDC();
+ event.getMDCCopy();
+ if(locationInfo) {
+ event.getLocationInformation();
+ }
+ event.getRenderedMessage();
+ event.getThrowableStrRep();
+ cb.add(event);
+ if(evaluator.isTriggeringEvent(event)) {
+ sendBuffer();
+ }
+ }
+
+ /**
+ This method determines if there is a sense in attempting to append.
+
+
It checks whether there is a set output target and also if
+ there is a set layout. If these checks fail, then the boolean
+ value false is returned. */
+ protected
+ boolean checkEntryConditions() {
+ if(this.msg == null) {
+ errorHandler.error("Message object not configured.");
+ return false;
+ }
+
+ if(this.evaluator == null) {
+ errorHandler.error("No TriggeringEventEvaluator is set for appender ["+
+ name+"].");
+ return false;
+ }
+
+
+ if(this.layout == null) {
+ errorHandler.error("No layout set for appender named ["+name+"].");
+ return false;
+ }
+ return true;
+ }
+
+
+ synchronized
+ public
+ void close() {
+ this.closed = true;
+ if (sendOnClose && cb.length() > 0) {
+ sendBuffer();
+ }
+ }
+
+ InternetAddress getAddress(String addressStr) {
+ try {
+ return new InternetAddress(addressStr);
+ } catch(AddressException e) {
+ errorHandler.error("Could not parse address ["+addressStr+"].", e,
+ ErrorCode.ADDRESS_PARSE_FAILURE);
+ return null;
+ }
+ }
+
+ InternetAddress[] parseAddress(String addressStr) {
+ try {
+ return InternetAddress.parse(addressStr, true);
+ } catch(AddressException e) {
+ errorHandler.error("Could not parse address ["+addressStr+"].", e,
+ ErrorCode.ADDRESS_PARSE_FAILURE);
+ return null;
+ }
+ }
+
+ /**
+ Returns value of the To option.
+ */
+ public
+ String getTo() {
+ return to;
+ }
+
+
+ /**
+ The SMTPAppender requires a {@link
+ org.apache.log4j.Layout layout}. */
+ public
+ boolean requiresLayout() {
+ return true;
+ }
+
+ /**
+ * Layout body of email message.
+ * @since 1.2.16
+ */
+ protected String formatBody() {
+
+ // Note: this code already owns the monitor for this
+ // appender. This frees us from needing to synchronize on 'cb'.
+
+ StringBuffer sbuf = new StringBuffer();
+ String t = layout.getHeader();
+ if(t != null)
+ sbuf.append(t);
+ int len = cb.length();
+ for(int i = 0; i < len; i++) {
+ //sbuf.append(MimeUtility.encodeText(layout.format(cb.get())));
+ LoggingEvent event = cb.get();
+ sbuf.append(layout.format(event));
+ if(layout.ignoresThrowable()) {
+ String[] s = event.getThrowableStrRep();
+ if (s != null) {
+ for(int j = 0; j < s.length; j++) {
+ sbuf.append(s[j]);
+ sbuf.append(Layout.LINE_SEP);
+ }
+ }
+ }
+ }
+ t = layout.getFooter();
+ if(t != null) {
+ sbuf.append(t);
+ }
+
+ return sbuf.toString();
+ }
+
+ /**
+ Send the contents of the cyclic buffer as an e-mail message.
+ */
+ protected
+ void sendBuffer() {
+
+ try {
+ String s = formatBody();
+ boolean allAscii = true;
+ for(int i = 0; i < s.length() && allAscii; i++) {
+ allAscii = s.charAt(i) <= 0x7F;
+ }
+ MimeBodyPart part;
+ if (allAscii) {
+ part = new MimeBodyPart();
+ part.setContent(s, layout.getContentType());
+ } else {
+ try {
+ ByteArrayOutputStream os = new ByteArrayOutputStream();
+ Writer writer = new OutputStreamWriter(
+ MimeUtility.encode(os, "quoted-printable"), "UTF-8");
+ writer.write(s);
+ writer.close();
+ InternetHeaders headers = new InternetHeaders();
+ headers.setHeader("Content-Type", layout.getContentType() + "; charset=UTF-8");
+ headers.setHeader("Content-Transfer-Encoding", "quoted-printable");
+ part = new MimeBodyPart(headers, os.toByteArray());
+ } catch(Exception ex) {
+ StringBuffer sbuf = new StringBuffer(s);
+ for (int i = 0; i < sbuf.length(); i++) {
+ if (sbuf.charAt(i) >= 0x80) {
+ sbuf.setCharAt(i, '?');
+ }
+ }
+ part = new MimeBodyPart();
+ part.setContent(sbuf.toString(), layout.getContentType());
+ }
+ }
+
+
+
+ Multipart mp = new MimeMultipart();
+ mp.addBodyPart(part);
+ msg.setContent(mp);
+
+ msg.setSentDate(new Date());
+ Transport.send(msg);
+ } catch(MessagingException e) {
+ LogLog.error("Error occured while sending e-mail notification.", e);
+ } catch(RuntimeException e) {
+ LogLog.error("Error occured while sending e-mail notification.", e);
+ }
+ }
+
+
+
+ /**
+ Returns value of the EvaluatorClass option.
+ */
+ public
+ String getEvaluatorClass() {
+ return evaluator == null ? null : evaluator.getClass().getName();
+ }
+
+ /**
+ Returns value of the From option.
+ */
+ public
+ String getFrom() {
+ return from;
+ }
+
+ /**
+ Get the reply addresses.
+ @return reply addresses as comma separated string, may be null.
+ @since 1.2.16
+ */
+ public
+ String getReplyTo() {
+ return replyTo;
+ }
+
+ /**
+ Returns value of the Subject option.
+ */
+ public
+ String getSubject() {
+ return subject;
+ }
+
+ /**
+ The From option takes a string value which should be a
+ e-mail address of the sender.
+ */
+ public
+ void setFrom(String from) {
+ this.from = from;
+ }
+
+ /**
+ Set the e-mail addresses to which replies should be directed.
+ @param addresses reply addresses as comma separated string, may be null.
+ @since 1.2.16
+ */
+ public
+ void setReplyTo(final String addresses) {
+ this.replyTo = addresses;
+ }
+
+
+ /**
+ The Subject option takes a string value which should be a
+ the subject of the e-mail message.
+ */
+ public
+ void setSubject(String subject) {
+ this.subject = subject;
+ }
+
+
+ /**
+ The BufferSize option takes a positive integer
+ representing the maximum number of logging events to collect in a
+ cyclic buffer. When the BufferSize is reached,
+ oldest events are deleted as new events are added to the
+ buffer. By default the size of the cyclic buffer is 512 events.
+ */
+ public
+ void setBufferSize(int bufferSize) {
+ this.bufferSize = bufferSize;
+ cb.resize(bufferSize);
+ }
+
+ /**
+ The SMTPHost option takes a string value which should be a
+ the host name of the SMTP server that will send the e-mail message.
+ */
+ public
+ void setSMTPHost(String smtpHost) {
+ this.smtpHost = smtpHost;
+ }
+
+ /**
+ Returns value of the SMTPHost option.
+ */
+ public
+ String getSMTPHost() {
+ return smtpHost;
+ }
+
+ /**
+ The To option takes a string value which should be a
+ comma separated list of e-mail address of the recipients.
+ */
+ public
+ void setTo(String to) {
+ this.to = to;
+ }
+
+
+
+ /**
+ Returns value of the BufferSize option.
+ */
+ public
+ int getBufferSize() {
+ return bufferSize;
+ }
+
+ /**
+ The EvaluatorClass option takes a string value
+ representing the name of the class implementing the {@link
+ TriggeringEventEvaluator} interface. A corresponding object will
+ be instantiated and assigned as the triggering event evaluator
+ for the SMTPAppender.
+ */
+ public
+ void setEvaluatorClass(String value) {
+ evaluator = (TriggeringEventEvaluator)
+ OptionConverter.instantiateByClassName(value,
+ TriggeringEventEvaluator.class,
+ evaluator);
+ }
+
+
+ /**
+ The LocationInfo option takes a boolean value. By
+ default, it is set to false which means there will be no effort
+ to extract the location information related to the event. As a
+ result, the layout that formats the events as they are sent out
+ in an e-mail is likely to place the wrong location information
+ (if present in the format).
+
+
Location information extraction is comparatively very slow and
+ should be avoided unless performance is not a concern.
+ */
+ public
+ void setLocationInfo(boolean locationInfo) {
+ this.locationInfo = locationInfo;
+ }
+
+ /**
+ Returns value of the LocationInfo option.
+ */
+ public
+ boolean getLocationInfo() {
+ return locationInfo;
+ }
+
+ /**
+ Set the cc recipient addresses.
+ @param addresses recipient addresses as comma separated string, may be null.
+ @since 1.2.14
+ */
+ public void setCc(final String addresses) {
+ this.cc = addresses;
+ }
+
+ /**
+ Get the cc recipient addresses.
+ @return recipient addresses as comma separated string, may be null.
+ @since 1.2.14
+ */
+ public String getCc() {
+ return cc;
+ }
+
+ /**
+ Set the bcc recipient addresses.
+ @param addresses recipient addresses as comma separated string, may be null.
+ @since 1.2.14
+ */
+ public void setBcc(final String addresses) {
+ this.bcc = addresses;
+ }
+
+ /**
+ Get the bcc recipient addresses.
+ @return recipient addresses as comma separated string, may be null.
+ @since 1.2.14
+ */
+ public String getBcc() {
+ return bcc;
+ }
+
+ /**
+ * The SmtpPassword option takes a string value which should be the password required to authenticate against
+ * the mail server.
+ * @param password password, may be null.
+ * @since 1.2.14
+ */
+ public void setSMTPPassword(final String password) {
+ this.smtpPassword = password;
+ }
+
+ /**
+ * The SmtpUsername option takes a string value which should be the username required to authenticate against
+ * the mail server.
+ * @param username user name, may be null.
+ * @since 1.2.14
+ */
+ public void setSMTPUsername(final String username) {
+ this.smtpUsername = username;
+ }
+
+ /**
+ * Setting the SmtpDebug option to true will cause the mail session to log its server interaction to stdout.
+ * This can be useful when debuging the appender but should not be used during production because username and
+ * password information is included in the output.
+ * @param debug debug flag.
+ * @since 1.2.14
+ */
+ public void setSMTPDebug(final boolean debug) {
+ this.smtpDebug = debug;
+ }
+
+ /**
+ * Get SMTP password.
+ * @return SMTP password, may be null.
+ * @since 1.2.14
+ */
+ public String getSMTPPassword() {
+ return smtpPassword;
+ }
+
+ /**
+ * Get SMTP user name.
+ * @return SMTP user name, may be null.
+ * @since 1.2.14
+ */
+ public String getSMTPUsername() {
+ return smtpUsername;
+ }
+
+ /**
+ * Get SMTP debug.
+ * @return SMTP debug flag.
+ * @since 1.2.14
+ */
+ public boolean getSMTPDebug() {
+ return smtpDebug;
+ }
+
+ /**
+ * Sets triggering evaluator.
+ * @param trigger triggering event evaluator.
+ * @since 1.2.15
+ */
+ public final void setEvaluator(final TriggeringEventEvaluator trigger) {
+ if (trigger == null) {
+ throw new NullPointerException("trigger");
+ }
+ this.evaluator = trigger;
+ }
+
+ /**
+ * Get triggering evaluator.
+ * @return triggering event evaluator.
+ * @since 1.2.15
+ */
+ public final TriggeringEventEvaluator getEvaluator() {
+ return evaluator;
+ }
+
+ /** {@inheritDoc}
+ * @since 1.2.15
+ */
+ public boolean parseUnrecognizedElement(final Element element,
+ final Properties props) throws Exception {
+ if ("triggeringPolicy".equals(element.getNodeName())) {
+ Object triggerPolicy =
+ org.apache.log4j.xml.DOMConfigurator.parseElement(
+ element, props, TriggeringEventEvaluator.class);
+ if (triggerPolicy instanceof TriggeringEventEvaluator) {
+ setEvaluator((TriggeringEventEvaluator) triggerPolicy);
+ }
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Get transport protocol.
+ * Typically null or "smtps".
+ *
+ * @return transport protocol, may be null.
+ * @since 1.2.16
+ */
+ public final String getSMTPProtocol() {
+ return smtpProtocol;
+ }
+
+ /**
+ * Set transport protocol.
+ * Typically null or "smtps".
+ *
+ * @param val transport protocol, may be null.
+ * @since 1.2.16
+ */
+ public final void setSMTPProtocol(final String val) {
+ smtpProtocol = val;
+ }
+
+ /**
+ * Get port.
+ *
+ * @return port, negative values indicate use of default ports for protocol.
+ * @since 1.2.16
+ */
+ public final int getSMTPPort() {
+ return smtpPort;
+ }
+
+ /**
+ * Set port.
+ *
+ * @param val port, negative values indicate use of default ports for protocol.
+ * @since 1.2.16
+ */
+ public final void setSMTPPort(final int val) {
+ smtpPort = val;
+ }
+
+ /**
+ * Get sendOnClose.
+ *
+ * @return if true all buffered logging events will be sent when the appender is closed.
+ * @since 1.2.16
+ */
+ public final boolean getSendOnClose() {
+ return sendOnClose;
+ }
+
+ /**
+ * Set sendOnClose.
+ *
+ * @param val if true all buffered logging events will be sent when appender is closed.
+ * @since 1.2.16
+ */
+ public final void setSendOnClose(final boolean val) {
+ sendOnClose = val;
+ }
+
+}
+
+class DefaultEvaluator implements TriggeringEventEvaluator {
+ /**
+ Is this event the e-mail triggering event?
+
+
This method returns true, if the event level
+ has ERROR level or higher. Otherwise it returns
+ false. */
+ public
+ boolean isTriggeringEvent(LoggingEvent event) {
+ return event.getLevel().isGreaterOrEqual(Level.ERROR);
+ }
+}
diff --git a/java/src/org/apache/log4j/net/SimpleSocketServer.java b/java/src/org/apache/log4j/net/SimpleSocketServer.java
new file mode 100644
index 0000000..c15aa3c
--- /dev/null
+++ b/java/src/org/apache/log4j/net/SimpleSocketServer.java
@@ -0,0 +1,97 @@
+/*
+ * 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.log4j.net;
+
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.xml.DOMConfigurator;
+
+
+/**
+ * A simple {@link SocketNode} based server.
+ *
+
+ Usage: java org.apache.log4j.net.SimpleSocketServer port configFile
+
+ where port is a part number where the server listens and
+ configFile is a configuration file fed to the {@link
+ PropertyConfigurator} or to {@link DOMConfigurator} if an XML file.
+
+ *
+ * @author Ceki Gülcü
+ *
+ * @since 0.8.4
+ * */
+public class SimpleSocketServer {
+
+ static Logger cat = Logger.getLogger(SimpleSocketServer.class);
+
+ static int port;
+
+ public
+ static
+ void main(String argv[]) {
+ if(argv.length == 2) {
+ init(argv[0], argv[1]);
+ } else {
+ usage("Wrong number of arguments.");
+ }
+
+ try {
+ cat.info("Listening on port " + port);
+ ServerSocket serverSocket = new ServerSocket(port);
+ while(true) {
+ cat.info("Waiting to accept a new client.");
+ Socket socket = serverSocket.accept();
+ cat.info("Connected to client at " + socket.getInetAddress());
+ cat.info("Starting new socket node.");
+ new Thread(new SocketNode(socket,
+ LogManager.getLoggerRepository()),"SimpleSocketServer-" + port).start();
+ }
+ } catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ static void usage(String msg) {
+ System.err.println(msg);
+ System.err.println(
+ "Usage: java " +SimpleSocketServer.class.getName() + " port configFile");
+ System.exit(1);
+ }
+
+ static void init(String portStr, String configFile) {
+ try {
+ port = Integer.parseInt(portStr);
+ } catch(java.lang.NumberFormatException e) {
+ e.printStackTrace();
+ usage("Could not interpret port number ["+ portStr +"].");
+ }
+
+ if(configFile.endsWith(".xml")) {
+ DOMConfigurator.configure(configFile);
+ } else {
+ PropertyConfigurator.configure(configFile);
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/net/SocketAppender.java b/java/src/org/apache/log4j/net/SocketAppender.java
new file mode 100644
index 0000000..855b057
--- /dev/null
+++ b/java/src/org/apache/log4j/net/SocketAppender.java
@@ -0,0 +1,474 @@
+/*
+ * 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.
+ */
+
+// Contributors: Dan MacDonald
+
+package org.apache.log4j.net;
+
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.io.InterruptedIOException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.ErrorCode;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ Sends {@link LoggingEvent} objects to a remote a log server,
+ usually a {@link SocketNode}.
+
+ The SocketAppender has the following properties:
+
+
+
+ - If sent to a {@link SocketNode}, remote logging is
+ non-intrusive as far as the log event is concerned. In other
+ words, the event will be logged with the same time stamp, {@link
+ org.apache.log4j.NDC}, location info as if it were logged locally by
+ the client.
+
+
- SocketAppenders do not use a layout. They ship a
+ serialized {@link LoggingEvent} object to the server side.
+
+
- Remote logging uses the TCP protocol. Consequently, if
+ the server is reachable, then log events will eventually arrive
+ at the server.
+
+
- If the remote server is down, the logging requests are
+ simply dropped. However, if and when the server comes back up,
+ then event transmission is resumed transparently. This
+ transparent reconneciton is performed by a connector
+ thread which periodically attempts to connect to the server.
+
+
- Logging events are automatically buffered by the
+ native TCP implementation. This means that if the link to server
+ is slow but still faster than the rate of (log) event production
+ by the client, the client will not be affected by the slow
+ network connection. However, if the network connection is slower
+ then the rate of event production, then the client can only
+ progress at the network rate. In particular, if the network link
+ to the the server is down, the client will be blocked.
+
+
On the other hand, if the network link is up, but the server
+ is down, the client will not be blocked when making log requests
+ but the log events will be lost due to server unavailability.
+
+
- Even if a
SocketAppender is no longer
+ attached to any category, it will not be garbage collected in
+ the presence of a connector thread. A connector thread exists
+ only if the connection to the server is down. To avoid this
+ garbage collection problem, you should {@link #close} the the
+ SocketAppender explicitly. See also next item.
+
+ Long lived applications which create/destroy many
+ SocketAppender instances should be aware of this
+ garbage collection problem. Most other applications can safely
+ ignore it.
+
+
- If the JVM hosting the
SocketAppender exits
+ before the SocketAppender is closed either
+ explicitly or subsequent to garbage collection, then there might
+ be untransmitted data in the pipe which might be lost. This is a
+ common problem on Windows based systems.
+
+ To avoid lost data, it is usually sufficient to {@link
+ #close} the SocketAppender either explicitly or by
+ calling the {@link org.apache.log4j.LogManager#shutdown} method
+ before exiting the application.
+
+
+
+
+ @author Ceki Gülcü
+ @since 0.8.4 */
+
+public class SocketAppender extends AppenderSkeleton {
+
+ /**
+ The default port number of remote logging server (4560).
+ @since 1.2.15
+ */
+ static public final int DEFAULT_PORT = 4560;
+
+ /**
+ The default reconnection delay (30000 milliseconds or 30 seconds).
+ */
+ static final int DEFAULT_RECONNECTION_DELAY = 30000;
+
+ /**
+ We remember host name as String in addition to the resolved
+ InetAddress so that it can be returned via getOption().
+ */
+ String remoteHost;
+
+ /**
+ * The MulticastDNS zone advertised by a SocketAppender
+ */
+ public static final String ZONE = "_log4j_obj_tcpconnect_appender.local.";
+
+ InetAddress address;
+ int port = DEFAULT_PORT;
+ ObjectOutputStream oos;
+ int reconnectionDelay = DEFAULT_RECONNECTION_DELAY;
+ boolean locationInfo = false;
+ private String application;
+
+ private Connector connector;
+
+ int counter = 0;
+
+ // reset the ObjectOutputStream every 70 calls
+ //private static final int RESET_FREQUENCY = 70;
+ private static final int RESET_FREQUENCY = 1;
+ private boolean advertiseViaMulticastDNS;
+ private ZeroConfSupport zeroConf;
+
+ public SocketAppender() {
+ }
+
+ /**
+ Connects to remote server at address and port.
+ */
+ public SocketAppender(InetAddress address, int port) {
+ this.address = address;
+ this.remoteHost = address.getHostName();
+ this.port = port;
+ connect(address, port);
+ }
+
+ /**
+ Connects to remote server at host and port.
+ */
+ public SocketAppender(String host, int port) {
+ this.port = port;
+ this.address = getAddressByName(host);
+ this.remoteHost = host;
+ connect(address, port);
+ }
+
+ /**
+ Connect to the specified RemoteHost and Port.
+ */
+ public void activateOptions() {
+ if (advertiseViaMulticastDNS) {
+ zeroConf = new ZeroConfSupport(ZONE, port, getName());
+ zeroConf.advertise();
+ }
+ connect(address, port);
+ }
+
+ /**
+ * Close this appender.
+ *
+ * This will mark the appender as closed and call then {@link
+ * #cleanUp} method.
+ * */
+ synchronized public void close() {
+ if(closed)
+ return;
+
+ this.closed = true;
+ if (advertiseViaMulticastDNS) {
+ zeroConf.unadvertise();
+ }
+
+ cleanUp();
+ }
+
+ /**
+ * Drop the connection to the remote host and release the underlying
+ * connector thread if it has been created
+ * */
+ public void cleanUp() {
+ if(oos != null) {
+ try {
+ oos.close();
+ } catch(IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not close oos.", e);
+ }
+ oos = null;
+ }
+ if(connector != null) {
+ //LogLog.debug("Interrupting the connector.");
+ connector.interrupted = true;
+ connector = null; // allow gc
+ }
+ }
+
+ void connect(InetAddress address, int port) {
+ if(this.address == null)
+ return;
+ try {
+ // First, close the previous connection if any.
+ cleanUp();
+ oos = new ObjectOutputStream(new Socket(address, port).getOutputStream());
+ } catch(IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ String msg = "Could not connect to remote log4j server at ["
+ +address.getHostName()+"].";
+ if(reconnectionDelay > 0) {
+ msg += " We will try again later.";
+ fireConnector(); // fire the connector thread
+ } else {
+ msg += " We are not retrying.";
+ errorHandler.error(msg, e, ErrorCode.GENERIC_FAILURE);
+ }
+ LogLog.error(msg);
+ }
+ }
+
+
+ public void append(LoggingEvent event) {
+ if(event == null)
+ return;
+
+ if(address==null) {
+ errorHandler.error("No remote host is set for SocketAppender named \""+
+ this.name+"\".");
+ return;
+ }
+
+ if(oos != null) {
+ try {
+
+ if(locationInfo) {
+ event.getLocationInformation();
+ }
+ if (application != null) {
+ event.setProperty("application", application);
+ }
+ event.getNDC();
+ event.getThreadName();
+ event.getMDCCopy();
+ event.getRenderedMessage();
+ event.getThrowableStrRep();
+
+ oos.writeObject(event);
+ //LogLog.debug("=========Flushing.");
+ oos.flush();
+ if(++counter >= RESET_FREQUENCY) {
+ counter = 0;
+ // Failing to reset the object output stream every now and
+ // then creates a serious memory leak.
+ //System.err.println("Doing oos.reset()");
+ oos.reset();
+ }
+ } catch(IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ oos = null;
+ LogLog.warn("Detected problem with connection: "+e);
+ if(reconnectionDelay > 0) {
+ fireConnector();
+ } else {
+ errorHandler.error("Detected problem with connection, not reconnecting.", e,
+ ErrorCode.GENERIC_FAILURE);
+ }
+ }
+ }
+ }
+
+ public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
+ this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
+ }
+
+ public boolean isAdvertiseViaMulticastDNS() {
+ return advertiseViaMulticastDNS;
+ }
+
+ void fireConnector() {
+ if(connector == null) {
+ LogLog.debug("Starting a new connector thread.");
+ connector = new Connector();
+ connector.setDaemon(true);
+ connector.setPriority(Thread.MIN_PRIORITY);
+ connector.start();
+ }
+ }
+
+ static
+ InetAddress getAddressByName(String host) {
+ try {
+ return InetAddress.getByName(host);
+ } catch(Exception e) {
+ if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not find address of ["+host+"].", e);
+ return null;
+ }
+ }
+
+ /**
+ * The SocketAppender does not use a layout. Hence, this method
+ * returns false.
+ * */
+ public boolean requiresLayout() {
+ return false;
+ }
+
+ /**
+ * The RemoteHost option takes a string value which should be
+ * the host name of the server where a {@link SocketNode} is
+ * running.
+ * */
+ public void setRemoteHost(String host) {
+ address = getAddressByName(host);
+ remoteHost = host;
+ }
+
+ /**
+ Returns value of the RemoteHost option.
+ */
+ public String getRemoteHost() {
+ return remoteHost;
+ }
+
+ /**
+ The Port option takes a positive integer representing
+ the port where the server is waiting for connections.
+ */
+ public void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ Returns value of the Port option.
+ */
+ public int getPort() {
+ return port;
+ }
+
+ /**
+ The LocationInfo option takes a boolean value. If true,
+ the information sent to the remote host will include location
+ information. By default no location information is sent to the server.
+ */
+ public void setLocationInfo(boolean locationInfo) {
+ this.locationInfo = locationInfo;
+ }
+
+ /**
+ Returns value of the LocationInfo option.
+ */
+ public boolean getLocationInfo() {
+ return locationInfo;
+ }
+
+ /**
+ * The App option takes a string value which should be the name of the
+ * application getting logged.
+ * If property was already set (via system property), don't set here.
+ * @since 1.2.15
+ */
+ public void setApplication(String lapp) {
+ this.application = lapp;
+ }
+
+ /**
+ * Returns value of the Application option.
+ * @since 1.2.15
+ */
+ public String getApplication() {
+ return application;
+ }
+
+ /**
+ The ReconnectionDelay option takes a positive integer
+ representing the number of milliseconds to wait between each
+ failed connection attempt to the server. The default value of
+ this option is 30000 which corresponds to 30 seconds.
+
+
Setting this option to zero turns off reconnection
+ capability.
+ */
+ public void setReconnectionDelay(int delay) {
+ this.reconnectionDelay = delay;
+ }
+
+ /**
+ Returns value of the ReconnectionDelay option.
+ */
+ public int getReconnectionDelay() {
+ return reconnectionDelay;
+ }
+
+ /**
+ The Connector will reconnect when the server becomes available
+ again. It does this by attempting to open a new connection every
+ reconnectionDelay milliseconds.
+
+
It stops trying whenever a connection is established. It will
+ restart to try reconnect to the server when previously open
+ connection is droppped.
+
+ @author Ceki Gülcü
+ @since 0.8.4
+ */
+ class Connector extends Thread {
+
+ boolean interrupted = false;
+
+ public
+ void run() {
+ Socket socket;
+ while(!interrupted) {
+ try {
+ sleep(reconnectionDelay);
+ LogLog.debug("Attempting connection to "+address.getHostName());
+ socket = new Socket(address, port);
+ synchronized(this) {
+ oos = new ObjectOutputStream(socket.getOutputStream());
+ connector = null;
+ LogLog.debug("Connection established. Exiting connector thread.");
+ break;
+ }
+ } catch(InterruptedException e) {
+ LogLog.debug("Connector interrupted. Leaving loop.");
+ return;
+ } catch(java.net.ConnectException e) {
+ LogLog.debug("Remote host "+address.getHostName()
+ +" refused connection.");
+ } catch(IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.debug("Could not connect to " + address.getHostName()+
+ ". Exception is " + e);
+ }
+ }
+ //LogLog.debug("Exiting Connector.run() method.");
+ }
+
+ /**
+ public
+ void finalize() {
+ LogLog.debug("Connector finalize() has been called.");
+ }
+ */
+ }
+
+}
diff --git a/java/src/org/apache/log4j/net/SocketHubAppender.java b/java/src/org/apache/log4j/net/SocketHubAppender.java
new file mode 100644
index 0000000..665c58a
--- /dev/null
+++ b/java/src/org/apache/log4j/net/SocketHubAppender.java
@@ -0,0 +1,507 @@
+/*
+ * 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.log4j.net;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.ObjectOutputStream;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.net.SocketException;
+import java.util.Vector;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.helpers.CyclicBuffer;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ Sends {@link LoggingEvent} objects to a set of remote log servers,
+ usually a {@link SocketNode SocketNodes}.
+
+
Acts just like {@link SocketAppender} except that instead of
+ connecting to a given remote log server,
+ SocketHubAppender accepts connections from the remote
+ log servers as clients. It can accept more than one connection.
+ When a log event is received, the event is sent to the set of
+ currently connected remote log servers. Implemented this way it does
+ not require any update to the configuration file to send data to
+ another remote log server. The remote log server simply connects to
+ the host and port the SocketHubAppender is running on.
+
+
The SocketHubAppender does not store events such
+ that the remote side will events that arrived after the
+ establishment of its connection. Once connected, events arrive in
+ order as guaranteed by the TCP protocol.
+
+
This implementation borrows heavily from the {@link
+ SocketAppender}.
+
+
The SocketHubAppender has the following characteristics:
+
+
+
+ - If sent to a {@link SocketNode}, logging is non-intrusive as
+ far as the log event is concerned. In other words, the event will be
+ logged with the same time stamp, {@link org.apache.log4j.NDC},
+ location info as if it were logged locally.
+
+
SocketHubAppender does not use a layout. It
+ ships a serialized {@link LoggingEvent} object to the remote side.
+
+ SocketHubAppender relies on the TCP
+ protocol. Consequently, if the remote side is reachable, then log
+ events will eventually arrive at remote client.
+
+ - If no remote clients are attached, the logging requests are
+ simply dropped.
+
+
- Logging events are automatically buffered by the
+ native TCP implementation. This means that if the link to remote
+ client is slow but still faster than the rate of (log) event
+ production, the application will not be affected by the slow network
+ connection. However, if the network connection is slower then the
+ rate of event production, then the local application can only
+ progress at the network rate. In particular, if the network link to
+ the the remote client is down, the application will be blocked.
+
+
On the other hand, if the network link is up, but the remote
+ client is down, the client will not be blocked when making log
+ requests but the log events will be lost due to client
+ unavailability.
+
+
The single remote client case extends to multiple clients
+ connections. The rate of logging will be determined by the slowest
+ link.
+
+
- If the JVM hosting the
SocketHubAppender exits
+ before the SocketHubAppender is closed either
+ explicitly or subsequent to garbage collection, then there might
+ be untransmitted data in the pipe which might be lost. This is a
+ common problem on Windows based systems.
+
+ To avoid lost data, it is usually sufficient to {@link #close}
+ the SocketHubAppender either explicitly or by calling
+ the {@link org.apache.log4j.LogManager#shutdown} method before
+ exiting the application.
+
+
+
+ @author Mark Womack */
+
+public class SocketHubAppender extends AppenderSkeleton {
+
+ /**
+ The default port number of the ServerSocket will be created on. */
+ static final int DEFAULT_PORT = 4560;
+
+ private int port = DEFAULT_PORT;
+ private Vector oosList = new Vector();
+ private ServerMonitor serverMonitor = null;
+ private boolean locationInfo = false;
+ private CyclicBuffer buffer = null;
+ private String application;
+ private boolean advertiseViaMulticastDNS;
+ private ZeroConfSupport zeroConf;
+
+ /**
+ * The MulticastDNS zone advertised by a SocketHubAppender
+ */
+ public static final String ZONE = "_log4j_obj_tcpaccept_appender.local.";
+
+
+ public SocketHubAppender() { }
+
+ /**
+ Connects to remote server at address and port. */
+ public
+ SocketHubAppender(int _port) {
+ port = _port;
+ startServer();
+ }
+
+ /**
+ Set up the socket server on the specified port. */
+ public
+ void activateOptions() {
+ if (advertiseViaMulticastDNS) {
+ zeroConf = new ZeroConfSupport(ZONE, port, getName());
+ zeroConf.advertise();
+ }
+ startServer();
+ }
+
+ /**
+ Close this appender.
+ This will mark the appender as closed and
+ call then {@link #cleanUp} method. */
+ synchronized
+ public
+ void close() {
+ if(closed)
+ return;
+
+ LogLog.debug("closing SocketHubAppender " + getName());
+ this.closed = true;
+ if (advertiseViaMulticastDNS) {
+ zeroConf.unadvertise();
+ }
+ cleanUp();
+
+ LogLog.debug("SocketHubAppender " + getName() + " closed");
+ }
+
+ /**
+ Release the underlying ServerMonitor thread, and drop the connections
+ to all connected remote servers. */
+ public
+ void cleanUp() {
+ // stop the monitor thread
+ LogLog.debug("stopping ServerSocket");
+ serverMonitor.stopMonitor();
+ serverMonitor = null;
+
+ // close all of the connections
+ LogLog.debug("closing client connections");
+ while (oosList.size() != 0) {
+ ObjectOutputStream oos = (ObjectOutputStream)oosList.elementAt(0);
+ if(oos != null) {
+ try {
+ oos.close();
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ LogLog.error("could not close oos.", e);
+ } catch(IOException e) {
+ LogLog.error("could not close oos.", e);
+ }
+
+ oosList.removeElementAt(0);
+ }
+ }
+ }
+
+ /**
+ Append an event to all of current connections. */
+ public
+ void append(LoggingEvent event) {
+ if (event != null) {
+ // set up location info if requested
+ if (locationInfo) {
+ event.getLocationInformation();
+ }
+ if (application != null) {
+ event.setProperty("application", application);
+ }
+ event.getNDC();
+ event.getThreadName();
+ event.getMDCCopy();
+ event.getRenderedMessage();
+ event.getThrowableStrRep();
+
+ if (buffer != null) {
+ buffer.add(event);
+ }
+ }
+
+ // if no event or no open connections, exit now
+ if ((event == null) || (oosList.size() == 0)) {
+ return;
+ }
+
+ // loop through the current set of open connections, appending the event to each
+ for (int streamCount = 0; streamCount < oosList.size(); streamCount++) {
+
+ ObjectOutputStream oos = null;
+ try {
+ oos = (ObjectOutputStream)oosList.elementAt(streamCount);
+ }
+ catch (ArrayIndexOutOfBoundsException e) {
+ // catch this, but just don't assign a value
+ // this should not really occur as this method is
+ // the only one that can remove oos's (besides cleanUp).
+ }
+
+ // list size changed unexpectedly? Just exit the append.
+ if (oos == null)
+ break;
+
+ try {
+ oos.writeObject(event);
+ oos.flush();
+ // Failing to reset the object output stream every now and
+ // then creates a serious memory leak.
+ // right now we always reset. TODO - set up frequency counter per oos?
+ oos.reset();
+ }
+ catch(IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // there was an io exception so just drop the connection
+ oosList.removeElementAt(streamCount);
+ LogLog.debug("dropped connection");
+
+ // decrement to keep the counter in place (for loop always increments)
+ streamCount--;
+ }
+ }
+ }
+
+ /**
+ The SocketHubAppender does not use a layout. Hence, this method returns
+ false. */
+ public
+ boolean requiresLayout() {
+ return false;
+ }
+
+ /**
+ The Port option takes a positive integer representing
+ the port where the server is waiting for connections. */
+ public
+ void setPort(int _port) {
+ port = _port;
+ }
+
+ /**
+ * The App option takes a string value which should be the name of the application getting logged. If property was already set (via system
+ * property), don't set here.
+ */
+ public
+ void setApplication(String lapp) {
+ this.application = lapp;
+ }
+
+ /**
+ * Returns value of the Application option.
+ */
+ public
+ String getApplication() {
+ return application;
+ }
+
+ /**
+ Returns value of the Port option. */
+ public
+ int getPort() {
+ return port;
+ }
+
+ /**
+ * The BufferSize option takes a positive integer representing the number of events this appender will buffer and send to newly connected
+ * clients.
+ */
+ public
+ void setBufferSize(int _bufferSize) {
+ buffer = new CyclicBuffer(_bufferSize);
+ }
+
+ /**
+ * Returns value of the bufferSize option.
+ */
+ public
+ int getBufferSize() {
+ if (buffer == null) {
+ return 0;
+ } else {
+ return buffer.getMaxSize();
+ }
+ }
+
+ /**
+ The LocationInfo option takes a boolean value. If true,
+ the information sent to the remote host will include location
+ information. By default no location information is sent to the server. */
+ public
+ void setLocationInfo(boolean _locationInfo) {
+ locationInfo = _locationInfo;
+ }
+
+ /**
+ Returns value of the LocationInfo option. */
+ public
+ boolean getLocationInfo() {
+ return locationInfo;
+ }
+
+ public void setAdvertiseViaMulticastDNS(boolean advertiseViaMulticastDNS) {
+ this.advertiseViaMulticastDNS = advertiseViaMulticastDNS;
+ }
+
+ public boolean isAdvertiseViaMulticastDNS() {
+ return advertiseViaMulticastDNS;
+ }
+
+ /**
+ Start the ServerMonitor thread. */
+ private
+ void startServer() {
+ serverMonitor = new ServerMonitor(port, oosList);
+ }
+
+ /**
+ * Creates a server socket to accept connections.
+ * @param socketPort port on which the socket should listen, may be zero.
+ * @return new socket.
+ * @throws IOException IO error when opening the socket.
+ */
+ protected ServerSocket createServerSocket(final int socketPort) throws IOException {
+ return new ServerSocket(socketPort);
+ }
+
+ /**
+ This class is used internally to monitor a ServerSocket
+ and register new connections in a vector passed in the
+ constructor. */
+ private
+ class ServerMonitor implements Runnable {
+ private int port;
+ private Vector oosList;
+ private boolean keepRunning;
+ private Thread monitorThread;
+
+ /**
+ Create a thread and start the monitor. */
+ public
+ ServerMonitor(int _port, Vector _oosList) {
+ port = _port;
+ oosList = _oosList;
+ keepRunning = true;
+ monitorThread = new Thread(this);
+ monitorThread.setDaemon(true);
+ monitorThread.setName("SocketHubAppender-Monitor-" + port);
+ monitorThread.start();
+ }
+
+ /**
+ Stops the monitor. This method will not return until
+ the thread has finished executing. */
+ public
+ synchronized
+ void stopMonitor() {
+ if (keepRunning) {
+ LogLog.debug("server monitor thread shutting down");
+ keepRunning = false;
+ try {
+ monitorThread.join();
+ }
+ catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ // do nothing?
+ }
+
+ // release the thread
+ monitorThread = null;
+ LogLog.debug("server monitor thread shut down");
+ }
+ }
+
+ private
+ void sendCachedEvents(ObjectOutputStream stream) throws IOException {
+ if (buffer != null) {
+ for (int i = 0; i < buffer.length(); i++) {
+ stream.writeObject(buffer.get(i));
+ }
+ stream.flush();
+ stream.reset();
+ }
+ }
+
+ /**
+ Method that runs, monitoring the ServerSocket and adding connections as
+ they connect to the socket. */
+ public
+ void run() {
+ ServerSocket serverSocket = null;
+ try {
+ serverSocket = createServerSocket(port);
+ serverSocket.setSoTimeout(1000);
+ }
+ catch (Exception e) {
+ if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("exception setting timeout, shutting down server socket.", e);
+ keepRunning = false;
+ return;
+ }
+
+ try {
+ try {
+ serverSocket.setSoTimeout(1000);
+ }
+ catch (SocketException e) {
+ LogLog.error("exception setting timeout, shutting down server socket.", e);
+ return;
+ }
+
+ while (keepRunning) {
+ Socket socket = null;
+ try {
+ socket = serverSocket.accept();
+ }
+ catch (InterruptedIOException e) {
+ // timeout occurred, so just loop
+ }
+ catch (SocketException e) {
+ LogLog.error("exception accepting socket, shutting down server socket.", e);
+ keepRunning = false;
+ }
+ catch (IOException e) {
+ LogLog.error("exception accepting socket.", e);
+ }
+
+ // if there was a socket accepted
+ if (socket != null) {
+ try {
+ InetAddress remoteAddress = socket.getInetAddress();
+ LogLog.debug("accepting connection from " + remoteAddress.getHostName()
+ + " (" + remoteAddress.getHostAddress() + ")");
+
+ // create an ObjectOutputStream
+ ObjectOutputStream oos = new ObjectOutputStream(socket.getOutputStream());
+ if (buffer != null && buffer.length() > 0) {
+ sendCachedEvents(oos);
+ }
+
+ // add it to the oosList. OK since Vector is synchronized.
+ oosList.addElement(oos);
+ } catch (IOException e) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("exception creating output stream on socket.", e);
+ }
+ }
+ }
+ }
+ finally {
+ // close the socket
+ try {
+ serverSocket.close();
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ } catch (IOException e) {
+ // do nothing with it?
+ }
+ }
+ }
+ }
+}
+
diff --git a/java/src/org/apache/log4j/net/SocketNode.java b/java/src/org/apache/log4j/net/SocketNode.java
new file mode 100644
index 0000000..e977f13
--- /dev/null
+++ b/java/src/org/apache/log4j/net/SocketNode.java
@@ -0,0 +1,124 @@
+/*
+ * 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.log4j.net;
+
+import java.io.BufferedInputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.io.ObjectInputStream;
+import java.net.Socket;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.LoggingEvent;
+
+// Contributors: Moses Hohman
+
+/**
+ Read {@link LoggingEvent} objects sent from a remote client using
+ Sockets (TCP). These logging events are logged according to local
+ policy, as if they were generated locally.
+
+ For example, the socket node might decide to log events to a
+ local file and also resent them to a second socket node.
+
+ @author Ceki Gülcü
+
+ @since 0.8.4
+*/
+public class SocketNode implements Runnable {
+
+ Socket socket;
+ LoggerRepository hierarchy;
+ ObjectInputStream ois;
+
+ static Logger logger = Logger.getLogger(SocketNode.class);
+
+ public SocketNode(Socket socket, LoggerRepository hierarchy) {
+ this.socket = socket;
+ this.hierarchy = hierarchy;
+ try {
+ ois = new ObjectInputStream(
+ new BufferedInputStream(socket.getInputStream()));
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ logger.error("Could not open ObjectInputStream to "+socket, e);
+ } catch(IOException e) {
+ logger.error("Could not open ObjectInputStream to "+socket, e);
+ } catch(RuntimeException e) {
+ logger.error("Could not open ObjectInputStream to "+socket, e);
+ }
+ }
+
+ //public
+ //void finalize() {
+ //System.err.println("-------------------------Finalize called");
+ // System.err.flush();
+ //}
+
+ public void run() {
+ LoggingEvent event;
+ Logger remoteLogger;
+
+ try {
+ if (ois != null) {
+ while(true) {
+ // read an event from the wire
+ event = (LoggingEvent) ois.readObject();
+ // get a logger from the hierarchy. The name of the logger is taken to be the name contained in the event.
+ remoteLogger = hierarchy.getLogger(event.getLoggerName());
+ //event.logger = remoteLogger;
+ // apply the logger-level filter
+ if(event.getLevel().isGreaterOrEqual(remoteLogger.getEffectiveLevel())) {
+ // finally log the event as if was generated locally
+ remoteLogger.callAppenders(event);
+ }
+ }
+ }
+ } catch(java.io.EOFException e) {
+ logger.info("Caught java.io.EOFException closing conneciton.");
+ } catch(java.net.SocketException e) {
+ logger.info("Caught java.net.SocketException closing conneciton.");
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ logger.info("Caught java.io.InterruptedIOException: "+e);
+ logger.info("Closing connection.");
+ } catch(IOException e) {
+ logger.info("Caught java.io.IOException: "+e);
+ logger.info("Closing connection.");
+ } catch(Exception e) {
+ logger.error("Unexpected exception. Closing conneciton.", e);
+ } finally {
+ if (ois != null) {
+ try {
+ ois.close();
+ } catch(Exception e) {
+ logger.info("Could not close connection.", e);
+ }
+ }
+ if (socket != null) {
+ try {
+ socket.close();
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ } catch(IOException ex) {
+ }
+ }
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/net/SocketServer.java b/java/src/org/apache/log4j/net/SocketServer.java
new file mode 100644
index 0000000..8ea3cb7
--- /dev/null
+++ b/java/src/org/apache/log4j/net/SocketServer.java
@@ -0,0 +1,212 @@
+/*
+ * 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.log4j.net;
+
+import java.io.File;
+import java.net.InetAddress;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Hashtable;
+
+import org.apache.log4j.Hierarchy;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.RootLogger;
+
+
+/**
+ A {@link SocketNode} based server that uses a different hierarchy
+ for each client.
+
+
+ Usage: java org.apache.log4j.net.SocketServer port configFile configDir
+
+ where port is a part number where the server listens,
+ configFile is a configuration file fed to the {@link PropertyConfigurator} and
+ configDir is a path to a directory containing configuration files, possibly one for each client host.
+
+
+ The configFile is used to configure the log4j
+ default hierarchy that the SocketServer will use to
+ report on its actions.
+
+
When a new connection is opened from a previously unknown
+ host, say foo.bar.net, then the
+ SocketServer will search for a configuration file
+ called foo.bar.net.lcf under the directory
+ configDir that was passed as the third argument. If
+ the file can be found, then a new hierarchy is instantiated and
+ configured using the configuration file
+ foo.bar.net.lcf. If and when the host
+ foo.bar.net opens another connection to the server,
+ then the previously configured hierarchy is used.
+
+
In case there is no file called foo.bar.net.lcf
+ under the directory configDir, then the
+ generic hierarchy is used. The generic hierarchy is
+ configured using a configuration file called
+ generic.lcf under the configDir
+ directory. If no such file exists, then the generic hierarchy will be
+ identical to the log4j default hierarchy.
+
+
Having different client hosts log using different hierarchies
+ ensures the total independence of the clients with respect to
+ their logging settings.
+
+
Currently, the hierarchy that will be used for a given request
+ depends on the IP address of the client host. For example, two
+ separate applicatons running on the same host and logging to the
+ same server will share the same hierarchy. This is perfectly safe
+ except that it might not provide the right amount of independence
+ between applications. The SocketServer is intended
+ as an example to be enhanced in order to implement more elaborate
+ policies.
+
+
+ @author Ceki Gülcü
+
+ @since 1.0 */
+
+public class SocketServer {
+
+ static String GENERIC = "generic";
+ static String CONFIG_FILE_EXT = ".lcf";
+
+ static Logger cat = Logger.getLogger(SocketServer.class);
+ static SocketServer server;
+ static int port;
+
+ // key=inetAddress, value=hierarchy
+ Hashtable hierarchyMap;
+ LoggerRepository genericHierarchy;
+ File dir;
+
+ public
+ static
+ void main(String argv[]) {
+ if(argv.length == 3)
+ init(argv[0], argv[1], argv[2]);
+ else
+ usage("Wrong number of arguments.");
+
+ try {
+ cat.info("Listening on port " + port);
+ ServerSocket serverSocket = new ServerSocket(port);
+ while(true) {
+ cat.info("Waiting to accept a new client.");
+ Socket socket = serverSocket.accept();
+ InetAddress inetAddress = socket.getInetAddress();
+ cat.info("Connected to client at " + inetAddress);
+
+ LoggerRepository h = (LoggerRepository) server.hierarchyMap.get(inetAddress);
+ if(h == null) {
+ h = server.configureHierarchy(inetAddress);
+ }
+
+ cat.info("Starting new socket node.");
+ new Thread(new SocketNode(socket, h)).start();
+ }
+ }
+ catch(Exception e) {
+ e.printStackTrace();
+ }
+ }
+
+
+ static
+ void usage(String msg) {
+ System.err.println(msg);
+ System.err.println(
+ "Usage: java " +SocketServer.class.getName() + " port configFile directory");
+ System.exit(1);
+ }
+
+ static
+ void init(String portStr, String configFile, String dirStr) {
+ try {
+ port = Integer.parseInt(portStr);
+ }
+ catch(java.lang.NumberFormatException e) {
+ e.printStackTrace();
+ usage("Could not interpret port number ["+ portStr +"].");
+ }
+
+ PropertyConfigurator.configure(configFile);
+
+ File dir = new File(dirStr);
+ if(!dir.isDirectory()) {
+ usage("["+dirStr+"] is not a directory.");
+ }
+ server = new SocketServer(dir);
+ }
+
+
+ public
+ SocketServer(File directory) {
+ this.dir = directory;
+ hierarchyMap = new Hashtable(11);
+ }
+
+ // This method assumes that there is no hiearchy for inetAddress
+ // yet. It will configure one and return it.
+ LoggerRepository configureHierarchy(InetAddress inetAddress) {
+ cat.info("Locating configuration file for "+inetAddress);
+ // We assume that the toSting method of InetAddress returns is in
+ // the format hostname/d1.d2.d3.d4 e.g. torino/192.168.1.1
+ String s = inetAddress.toString();
+ int i = s.indexOf("/");
+ if(i == -1) {
+ cat.warn("Could not parse the inetAddress ["+inetAddress+
+ "]. Using default hierarchy.");
+ return genericHierarchy();
+ } else {
+ String key = s.substring(0, i);
+
+ File configFile = new File(dir, key+CONFIG_FILE_EXT);
+ if(configFile.exists()) {
+ Hierarchy h = new Hierarchy(new RootLogger(Level.DEBUG));
+ hierarchyMap.put(inetAddress, h);
+
+ new PropertyConfigurator().doConfigure(configFile.getAbsolutePath(), h);
+
+ return h;
+ } else {
+ cat.warn("Could not find config file ["+configFile+"].");
+ return genericHierarchy();
+ }
+ }
+ }
+
+ LoggerRepository genericHierarchy() {
+ if(genericHierarchy == null) {
+ File f = new File(dir, GENERIC+CONFIG_FILE_EXT);
+ if(f.exists()) {
+ genericHierarchy = new Hierarchy(new RootLogger(Level.DEBUG));
+ new PropertyConfigurator().doConfigure(f.getAbsolutePath(), genericHierarchy);
+ } else {
+ cat.warn("Could not find config file ["+f+
+ "]. Will use the default hierarchy.");
+ genericHierarchy = LogManager.getLoggerRepository();
+ }
+ }
+ return genericHierarchy;
+ }
+}
diff --git a/java/src/org/apache/log4j/net/SyslogAppender.java b/java/src/org/apache/log4j/net/SyslogAppender.java
new file mode 100644
index 0000000..6ab7edd
--- /dev/null
+++ b/java/src/org/apache/log4j/net/SyslogAppender.java
@@ -0,0 +1,536 @@
+/*
+ * 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.log4j.net;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Layout;
+import org.apache.log4j.helpers.SyslogQuietWriter;
+import org.apache.log4j.helpers.SyslogWriter;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.Locale;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.io.IOException;
+
+// Contributors: Yves Bossel
+// Christopher Taylor
+
+/**
+ Use SyslogAppender to send log messages to a remote syslog daemon.
+
+ @author Ceki Gülcü
+ @author Anders Kristensen
+ */
+public class SyslogAppender extends AppenderSkeleton {
+ // The following constants are extracted from a syslog.h file
+ // copyrighted by the Regents of the University of California
+ // I hope nobody at Berkley gets offended.
+
+ /** Kernel messages */
+ final static public int LOG_KERN = 0;
+ /** Random user-level messages */
+ final static public int LOG_USER = 1<<3;
+ /** Mail system */
+ final static public int LOG_MAIL = 2<<3;
+ /** System daemons */
+ final static public int LOG_DAEMON = 3<<3;
+ /** security/authorization messages */
+ final static public int LOG_AUTH = 4<<3;
+ /** messages generated internally by syslogd */
+ final static public int LOG_SYSLOG = 5<<3;
+
+ /** line printer subsystem */
+ final static public int LOG_LPR = 6<<3;
+ /** network news subsystem */
+ final static public int LOG_NEWS = 7<<3;
+ /** UUCP subsystem */
+ final static public int LOG_UUCP = 8<<3;
+ /** clock daemon */
+ final static public int LOG_CRON = 9<<3;
+ /** security/authorization messages (private) */
+ final static public int LOG_AUTHPRIV = 10<<3;
+ /** ftp daemon */
+ final static public int LOG_FTP = 11<<3;
+
+ // other codes through 15 reserved for system use
+ /** reserved for local use */
+ final static public int LOG_LOCAL0 = 16<<3;
+ /** reserved for local use */
+ final static public int LOG_LOCAL1 = 17<<3;
+ /** reserved for local use */
+ final static public int LOG_LOCAL2 = 18<<3;
+ /** reserved for local use */
+ final static public int LOG_LOCAL3 = 19<<3;
+ /** reserved for local use */
+ final static public int LOG_LOCAL4 = 20<<3;
+ /** reserved for local use */
+ final static public int LOG_LOCAL5 = 21<<3;
+ /** reserved for local use */
+ final static public int LOG_LOCAL6 = 22<<3;
+ /** reserved for local use*/
+ final static public int LOG_LOCAL7 = 23<<3;
+
+ protected static final int SYSLOG_HOST_OI = 0;
+ protected static final int FACILITY_OI = 1;
+
+ static final String TAB = " ";
+
+ // Have LOG_USER as default
+ int syslogFacility = LOG_USER;
+ String facilityStr;
+ boolean facilityPrinting = false;
+
+ //SyslogTracerPrintWriter stp;
+ SyslogQuietWriter sqw;
+ String syslogHost;
+
+ /**
+ * If true, the appender will generate the HEADER (timestamp and host name)
+ * part of the syslog packet.
+ * @since 1.2.15
+ */
+ private boolean header = false;
+ /**
+ * Date format used if header = true.
+ * @since 1.2.15
+ */
+ private final SimpleDateFormat dateFormat = new SimpleDateFormat("MMM dd HH:mm:ss ", Locale.ENGLISH);
+ /**
+ * Host name used to identify messages from this appender.
+ * @since 1.2.15
+ */
+ private String localHostname;
+
+ /**
+ * Set to true after the header of the layout has been sent or if it has none.
+ */
+ private boolean layoutHeaderChecked = false;
+
+ public
+ SyslogAppender() {
+ this.initSyslogFacilityStr();
+ }
+
+ public
+ SyslogAppender(Layout layout, int syslogFacility) {
+ this.layout = layout;
+ this.syslogFacility = syslogFacility;
+ this.initSyslogFacilityStr();
+ }
+
+ public
+ SyslogAppender(Layout layout, String syslogHost, int syslogFacility) {
+ this(layout, syslogFacility);
+ setSyslogHost(syslogHost);
+ }
+
+ /**
+ Release any resources held by this SyslogAppender.
+
+ @since 0.8.4
+ */
+ synchronized
+ public
+ void close() {
+ closed = true;
+ if (sqw != null) {
+ try {
+ if (layoutHeaderChecked && layout != null && layout.getFooter() != null) {
+ sendLayoutMessage(layout.getFooter());
+ }
+ sqw.close();
+ sqw = null;
+ } catch(java.io.InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ sqw = null;
+ } catch(IOException e) {
+ sqw = null;
+ }
+ }
+ }
+
+ private
+ void initSyslogFacilityStr() {
+ facilityStr = getFacilityString(this.syslogFacility);
+
+ if (facilityStr == null) {
+ System.err.println("\"" + syslogFacility +
+ "\" is an unknown syslog facility. Defaulting to \"USER\".");
+ this.syslogFacility = LOG_USER;
+ facilityStr = "user:";
+ } else {
+ facilityStr += ":";
+ }
+ }
+
+ /**
+ Returns the specified syslog facility as a lower-case String,
+ e.g. "kern", "user", etc.
+ */
+ public
+ static
+ String getFacilityString(int syslogFacility) {
+ switch(syslogFacility) {
+ case LOG_KERN: return "kern";
+ case LOG_USER: return "user";
+ case LOG_MAIL: return "mail";
+ case LOG_DAEMON: return "daemon";
+ case LOG_AUTH: return "auth";
+ case LOG_SYSLOG: return "syslog";
+ case LOG_LPR: return "lpr";
+ case LOG_NEWS: return "news";
+ case LOG_UUCP: return "uucp";
+ case LOG_CRON: return "cron";
+ case LOG_AUTHPRIV: return "authpriv";
+ case LOG_FTP: return "ftp";
+ case LOG_LOCAL0: return "local0";
+ case LOG_LOCAL1: return "local1";
+ case LOG_LOCAL2: return "local2";
+ case LOG_LOCAL3: return "local3";
+ case LOG_LOCAL4: return "local4";
+ case LOG_LOCAL5: return "local5";
+ case LOG_LOCAL6: return "local6";
+ case LOG_LOCAL7: return "local7";
+ default: return null;
+ }
+ }
+
+ /**
+ Returns the integer value corresponding to the named syslog
+ facility, or -1 if it couldn't be recognized.
+
+ @param facilityName one of the strings KERN, USER, MAIL, DAEMON,
+ AUTH, SYSLOG, LPR, NEWS, UUCP, CRON, AUTHPRIV, FTP, LOCAL0,
+ LOCAL1, LOCAL2, LOCAL3, LOCAL4, LOCAL5, LOCAL6, LOCAL7.
+ The matching is case-insensitive.
+
+ @since 1.1
+ */
+ public
+ static
+ int getFacility(String facilityName) {
+ if(facilityName != null) {
+ facilityName = facilityName.trim();
+ }
+ if("KERN".equalsIgnoreCase(facilityName)) {
+ return LOG_KERN;
+ } else if("USER".equalsIgnoreCase(facilityName)) {
+ return LOG_USER;
+ } else if("MAIL".equalsIgnoreCase(facilityName)) {
+ return LOG_MAIL;
+ } else if("DAEMON".equalsIgnoreCase(facilityName)) {
+ return LOG_DAEMON;
+ } else if("AUTH".equalsIgnoreCase(facilityName)) {
+ return LOG_AUTH;
+ } else if("SYSLOG".equalsIgnoreCase(facilityName)) {
+ return LOG_SYSLOG;
+ } else if("LPR".equalsIgnoreCase(facilityName)) {
+ return LOG_LPR;
+ } else if("NEWS".equalsIgnoreCase(facilityName)) {
+ return LOG_NEWS;
+ } else if("UUCP".equalsIgnoreCase(facilityName)) {
+ return LOG_UUCP;
+ } else if("CRON".equalsIgnoreCase(facilityName)) {
+ return LOG_CRON;
+ } else if("AUTHPRIV".equalsIgnoreCase(facilityName)) {
+ return LOG_AUTHPRIV;
+ } else if("FTP".equalsIgnoreCase(facilityName)) {
+ return LOG_FTP;
+ } else if("LOCAL0".equalsIgnoreCase(facilityName)) {
+ return LOG_LOCAL0;
+ } else if("LOCAL1".equalsIgnoreCase(facilityName)) {
+ return LOG_LOCAL1;
+ } else if("LOCAL2".equalsIgnoreCase(facilityName)) {
+ return LOG_LOCAL2;
+ } else if("LOCAL3".equalsIgnoreCase(facilityName)) {
+ return LOG_LOCAL3;
+ } else if("LOCAL4".equalsIgnoreCase(facilityName)) {
+ return LOG_LOCAL4;
+ } else if("LOCAL5".equalsIgnoreCase(facilityName)) {
+ return LOG_LOCAL5;
+ } else if("LOCAL6".equalsIgnoreCase(facilityName)) {
+ return LOG_LOCAL6;
+ } else if("LOCAL7".equalsIgnoreCase(facilityName)) {
+ return LOG_LOCAL7;
+ } else {
+ return -1;
+ }
+ }
+
+
+ private void splitPacket(final String header, final String packet) {
+ int byteCount = packet.getBytes().length;
+ //
+ // if packet is less than RFC 3164 limit
+ // of 1024 bytes, then write it
+ // (must allow for up 5to 5 characters in the PRI section
+ // added by SyslogQuietWriter)
+ if (byteCount <= 1019) {
+ sqw.write(packet);
+ } else {
+ int split = header.length() + (packet.length() - header.length())/2;
+ splitPacket(header, packet.substring(0, split) + "...");
+ splitPacket(header, header + "..." + packet.substring(split));
+ }
+ }
+
+ public
+ void append(LoggingEvent event) {
+
+ if(!isAsSevereAsThreshold(event.getLevel()))
+ return;
+
+ // We must not attempt to append if sqw is null.
+ if(sqw == null) {
+ errorHandler.error("No syslog host is set for SyslogAppedender named \""+
+ this.name+"\".");
+ return;
+ }
+
+ if (!layoutHeaderChecked) {
+ if (layout != null && layout.getHeader() != null) {
+ sendLayoutMessage(layout.getHeader());
+ }
+ layoutHeaderChecked = true;
+ }
+
+ String hdr = getPacketHeader(event.timeStamp);
+ String packet;
+ if (layout == null) {
+ packet = String.valueOf(event.getMessage());
+ } else {
+ packet = layout.format(event);
+ }
+ if(facilityPrinting || hdr.length() > 0) {
+ StringBuffer buf = new StringBuffer(hdr);
+ if(facilityPrinting) {
+ buf.append(facilityStr);
+ }
+ buf.append(packet);
+ packet = buf.toString();
+ }
+
+ sqw.setLevel(event.getLevel().getSyslogEquivalent());
+ //
+ // if message has a remote likelihood of exceeding 1024 bytes
+ // when encoded, consider splitting message into multiple packets
+ if (packet.length() > 256) {
+ splitPacket(hdr, packet);
+ } else {
+ sqw.write(packet);
+ }
+
+ if (layout == null || layout.ignoresThrowable()) {
+ String[] s = event.getThrowableStrRep();
+ if (s != null) {
+ for(int i = 0; i < s.length; i++) {
+ if (s[i].startsWith("\t")) {
+ sqw.write(hdr+TAB+s[i].substring(1));
+ } else {
+ sqw.write(hdr+s[i]);
+ }
+ }
+ }
+ }
+ }
+
+ /**
+ This method returns immediately as options are activated when they
+ are set.
+ */
+ public
+ void activateOptions() {
+ if (header) {
+ getLocalHostname();
+ }
+ if (layout != null && layout.getHeader() != null) {
+ sendLayoutMessage(layout.getHeader());
+ }
+ layoutHeaderChecked = true;
+ }
+
+ /**
+ The SyslogAppender requires a layout. Hence, this method returns
+ true.
+
+ @since 0.8.4 */
+ public
+ boolean requiresLayout() {
+ return true;
+ }
+
+ /**
+ The SyslogHost option is the name of the the syslog host
+ where log output should go. A non-default port can be specified by
+ appending a colon and port number to a host name,
+ an IPv4 address or an IPv6 address enclosed in square brackets.
+
+ WARNING If the SyslogHost is not set, then this appender
+ will fail.
+ */
+ public
+ void setSyslogHost(final String syslogHost) {
+ this.sqw = new SyslogQuietWriter(new SyslogWriter(syslogHost),
+ syslogFacility, errorHandler);
+ //this.stp = new SyslogTracerPrintWriter(sqw);
+ this.syslogHost = syslogHost;
+ }
+
+ /**
+ Returns the value of the SyslogHost option.
+ */
+ public
+ String getSyslogHost() {
+ return syslogHost;
+ }
+
+ /**
+ Set the syslog facility. This is the Facility option.
+
+ The facilityName parameter must be one of the
+ strings KERN, USER, MAIL, DAEMON, AUTH, SYSLOG, LPR, NEWS, UUCP,
+ CRON, AUTHPRIV, FTP, LOCAL0, LOCAL1, LOCAL2, LOCAL3, LOCAL4,
+ LOCAL5, LOCAL6, LOCAL7. Case is unimportant.
+
+ @since 0.8.1 */
+ public
+ void setFacility(String facilityName) {
+ if(facilityName == null)
+ return;
+
+ syslogFacility = getFacility(facilityName);
+ if (syslogFacility == -1) {
+ System.err.println("["+facilityName +
+ "] is an unknown syslog facility. Defaulting to [USER].");
+ syslogFacility = LOG_USER;
+ }
+
+ this.initSyslogFacilityStr();
+
+ // If there is already a sqw, make it use the new facility.
+ if(sqw != null) {
+ sqw.setSyslogFacility(this.syslogFacility);
+ }
+ }
+
+ /**
+ Returns the value of the Facility option.
+ */
+ public
+ String getFacility() {
+ return getFacilityString(syslogFacility);
+ }
+
+ /**
+ If the FacilityPrinting option is set to true, the printed
+ message will include the facility name of the application. It is
+ false by default.
+ */
+ public
+ void setFacilityPrinting(boolean on) {
+ facilityPrinting = on;
+ }
+
+ /**
+ Returns the value of the FacilityPrinting option.
+ */
+ public
+ boolean getFacilityPrinting() {
+ return facilityPrinting;
+ }
+
+ /**
+ * If true, the appender will generate the HEADER part (that is, timestamp and host name)
+ * of the syslog packet. Default value is false for compatibility with existing behavior,
+ * however should be true unless there is a specific justification.
+ * @since 1.2.15
+ */
+ public final boolean getHeader() {
+ return header;
+ }
+
+ /**
+ * Returns whether the appender produces the HEADER part (that is, timestamp and host name)
+ * of the syslog packet.
+ * @since 1.2.15
+ */
+ public final void setHeader(final boolean val) {
+ header = val;
+ }
+
+ /**
+ * Get the host name used to identify this appender.
+ * @return local host name
+ * @since 1.2.15
+ */
+ private String getLocalHostname() {
+ if (localHostname == null) {
+ try {
+ InetAddress addr = InetAddress.getLocalHost();
+ localHostname = addr.getHostName();
+ } catch (UnknownHostException uhe) {
+ localHostname = "UNKNOWN_HOST";
+ }
+ }
+ return localHostname;
+ }
+
+ /**
+ * Gets HEADER portion of packet.
+ * @param timeStamp number of milliseconds after the standard base time.
+ * @return HEADER portion of packet, will be zero-length string if header is false.
+ * @since 1.2.15
+ */
+ private String getPacketHeader(final long timeStamp) {
+ if (header) {
+ StringBuffer buf = new StringBuffer(dateFormat.format(new Date(timeStamp)));
+ // RFC 3164 says leading space, not leading zero on days 1-9
+ if (buf.charAt(4) == '0') {
+ buf.setCharAt(4, ' ');
+ }
+ buf.append(getLocalHostname());
+ buf.append(' ');
+ return buf.toString();
+ }
+ return "";
+ }
+
+ /**
+ * Set header or footer of layout.
+ * @param msg message body, may not be null.
+ */
+ private void sendLayoutMessage(final String msg) {
+ if (sqw != null) {
+ String packet = msg;
+ String hdr = getPacketHeader(new Date().getTime());
+ if(facilityPrinting || hdr.length() > 0) {
+ StringBuffer buf = new StringBuffer(hdr);
+ if(facilityPrinting) {
+ buf.append(facilityStr);
+ }
+ buf.append(msg);
+ packet = buf.toString();
+ }
+ sqw.setLevel(6);
+ sqw.write(packet);
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/net/TelnetAppender.java b/java/src/org/apache/log4j/net/TelnetAppender.java
new file mode 100644
index 0000000..23847b5
--- /dev/null
+++ b/java/src/org/apache/log4j/net/TelnetAppender.java
@@ -0,0 +1,236 @@
+/*
+ * 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.log4j.net;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.io.InterruptedIOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+import java.util.Enumeration;
+import java.util.Iterator;
+import java.util.Vector;
+
+/**
+
The TelnetAppender is a log4j appender that specializes in
+ writing to a read-only socket. The output is provided in a
+ telnet-friendly way so that a log can be monitored over TCP/IP.
+ Clients using telnet connect to the socket and receive log data.
+ This is handy for remote monitoring, especially when monitoring a
+ servlet.
+
+
Here is a list of the available configuration options:
+
+
+
+ | Name |
+ Requirement |
+ Description |
+ Sample Value |
+
+
+
+ | Port |
+ optional |
+ This parameter determines the port to use for announcing log events. The default port is 23 (telnet). |
+ 5875 |
+
+
+ @author Jay Funnell
+*/
+
+public class TelnetAppender extends AppenderSkeleton {
+
+ private SocketHandler sh;
+ private int port = 23;
+
+ /**
+ This appender requires a layout to format the text to the
+ attached client(s). */
+ public boolean requiresLayout() {
+ return true;
+ }
+
+ /** all of the options have been set, create the socket handler and
+ wait for connections. */
+ public void activateOptions() {
+ try {
+ sh = new SocketHandler(port);
+ sh.start();
+ }
+ catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ e.printStackTrace();
+ } catch(IOException e) {
+ e.printStackTrace();
+ } catch(RuntimeException e) {
+ e.printStackTrace();
+ }
+ super.activateOptions();
+ }
+
+ public
+ int getPort() {
+ return port;
+ }
+
+ public
+ void setPort(int port) {
+ this.port = port;
+ }
+
+
+ /** shuts down the appender. */
+ public void close() {
+ if (sh != null) {
+ sh.close();
+ try {
+ sh.join();
+ } catch(InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ }
+ }
+ }
+
+ /** Handles a log event. For this appender, that means writing the
+ message to each connected client. */
+ protected void append(LoggingEvent event) {
+ if(sh != null) {
+ sh.send(layout.format(event));
+ if(layout.ignoresThrowable()) {
+ String[] s = event.getThrowableStrRep();
+ if (s != null) {
+ StringBuffer buf = new StringBuffer();
+ for(int i = 0; i < s.length; i++) {
+ buf.append(s[i]);
+ buf.append("\r\n");
+ }
+ sh.send(buf.toString());
+ }
+ }
+ }
+ }
+
+ //---------------------------------------------------------- SocketHandler:
+
+ /** The SocketHandler class is used to accept connections from
+ clients. It is threaded so that clients can connect/disconnect
+ asynchronously. */
+ protected class SocketHandler extends Thread {
+
+ private Vector writers = new Vector();
+ private Vector connections = new Vector();
+ private ServerSocket serverSocket;
+ private int MAX_CONNECTIONS = 20;
+
+ public void finalize() {
+ close();
+ }
+
+ /**
+ * make sure we close all network connections when this handler is destroyed.
+ * @since 1.2.15
+ */
+ public void close() {
+ synchronized(this) {
+ for(Enumeration e = connections.elements();e.hasMoreElements();) {
+ try {
+ ((Socket)e.nextElement()).close();
+ } catch(InterruptedIOException ex) {
+ Thread.currentThread().interrupt();
+ } catch(IOException ex) {
+ } catch(RuntimeException ex) {
+ }
+ }
+ }
+
+ try {
+ serverSocket.close();
+ } catch(InterruptedIOException ex) {
+ Thread.currentThread().interrupt();
+ } catch(IOException ex) {
+ } catch(RuntimeException ex) {
+ }
+ }
+
+ /** sends a message to each of the clients in telnet-friendly output. */
+ public synchronized void send(final String message) {
+ Iterator ce = connections.iterator();
+ for(Iterator e = writers.iterator();e.hasNext();) {
+ ce.next();
+ PrintWriter writer = (PrintWriter)e.next();
+ writer.print(message);
+ if(writer.checkError()) {
+ ce.remove();
+ e.remove();
+ }
+ }
+ }
+
+ /**
+ Continually accepts client connections. Client connections
+ are refused when MAX_CONNECTIONS is reached.
+ */
+ public void run() {
+ while(!serverSocket.isClosed()) {
+ try {
+ Socket newClient = serverSocket.accept();
+ PrintWriter pw = new PrintWriter(newClient.getOutputStream());
+ if(connections.size() < MAX_CONNECTIONS) {
+ synchronized(this) {
+ connections.addElement(newClient);
+ writers.addElement(pw);
+ pw.print("TelnetAppender v1.0 (" + connections.size()
+ + " active connections)\r\n\r\n");
+ pw.flush();
+ }
+ } else {
+ pw.print("Too many connections.\r\n");
+ pw.flush();
+ newClient.close();
+ }
+ } catch(Exception e) {
+ if (e instanceof InterruptedIOException || e instanceof InterruptedException) {
+ Thread.currentThread().interrupt();
+ }
+ if (!serverSocket.isClosed()) {
+ LogLog.error("Encountered error while in SocketHandler loop.", e);
+ }
+ break;
+ }
+ }
+
+ try {
+ serverSocket.close();
+ } catch(InterruptedIOException ex) {
+ Thread.currentThread().interrupt();
+ } catch(IOException ex) {
+ }
+ }
+
+ public SocketHandler(int port) throws IOException {
+ serverSocket = new ServerSocket(port);
+ setName("TelnetAppender-" + getName() + "-" + port);
+ }
+
+ }
+}
diff --git a/java/src/org/apache/log4j/net/ZeroConfSupport.java b/java/src/org/apache/log4j/net/ZeroConfSupport.java
new file mode 100644
index 0000000..32ac971
--- /dev/null
+++ b/java/src/org/apache/log4j/net/ZeroConfSupport.java
@@ -0,0 +1,206 @@
+/*
+ * 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.log4j.net;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+
+import org.apache.log4j.helpers.LogLog;
+
+public class ZeroConfSupport {
+ private static Object jmDNS = initializeJMDNS();
+
+ Object serviceInfo;
+ private static Class jmDNSClass;
+ private static Class serviceInfoClass;
+
+ public ZeroConfSupport(String zone, int port, String name, Map properties) {
+ //if version 3 is available, use it to constuct a serviceInfo instance, otherwise support the version1 API
+ boolean isVersion3 = false;
+ try {
+ //create method is in version 3, not version 1
+ jmDNSClass.getMethod("create", null);
+ isVersion3 = true;
+ } catch (NoSuchMethodException e) {
+ //no-op
+ }
+
+ if (isVersion3) {
+ LogLog.debug("using JmDNS version 3 to construct serviceInfo instance");
+ serviceInfo = buildServiceInfoVersion3(zone, port, name, properties);
+ } else {
+ LogLog.debug("using JmDNS version 1.0 to construct serviceInfo instance");
+ serviceInfo = buildServiceInfoVersion1(zone, port, name, properties);
+ }
+ }
+
+ public ZeroConfSupport(String zone, int port, String name) {
+ this(zone, port, name, new HashMap());
+ }
+
+ private static Object createJmDNSVersion1()
+ {
+ try {
+ return jmDNSClass.newInstance();
+ } catch (InstantiationException e) {
+ LogLog.warn("Unable to instantiate JMDNS", e);
+ } catch (IllegalAccessException e) {
+ LogLog.warn("Unable to instantiate JMDNS", e);
+ }
+ return null;
+ }
+
+ private static Object createJmDNSVersion3()
+ {
+ try {
+ Method jmDNSCreateMethod = jmDNSClass.getMethod("create", null);
+ return jmDNSCreateMethod.invoke(null, null);
+ } catch (IllegalAccessException e) {
+ LogLog.warn("Unable to instantiate jmdns class", e);
+ } catch (NoSuchMethodException e) {
+ LogLog.warn("Unable to access constructor", e);
+ } catch (InvocationTargetException e) {
+ LogLog.warn("Unable to call constructor", e);
+ }
+ return null;
+ }
+
+ private Object buildServiceInfoVersion1(String zone, int port, String name, Map properties) {
+ //version 1 uses a hashtable
+ Hashtable hashtableProperties = new Hashtable(properties);
+ try {
+ Class[] args = new Class[6];
+ args[0] = String.class;
+ args[1] = String.class;
+ args[2] = int.class;
+ args[3] = int.class; //weight (0)
+ args[4] = int.class; //priority (0)
+ args[5] = Hashtable.class;
+ Constructor constructor = serviceInfoClass.getConstructor(args);
+ Object[] values = new Object[6];
+ values[0] = zone;
+ values[1] = name;
+ values[2] = new Integer(port);
+ values[3] = new Integer(0);
+ values[4] = new Integer(0);
+ values[5] = hashtableProperties;
+ Object result = constructor.newInstance(values);
+ LogLog.debug("created serviceinfo: " + result);
+ return result;
+ } catch (IllegalAccessException e) {
+ LogLog.warn("Unable to construct ServiceInfo instance", e);
+ } catch (NoSuchMethodException e) {
+ LogLog.warn("Unable to get ServiceInfo constructor", e);
+ } catch (InstantiationException e) {
+ LogLog.warn("Unable to construct ServiceInfo instance", e);
+ } catch (InvocationTargetException e) {
+ LogLog.warn("Unable to construct ServiceInfo instance", e);
+ }
+ return null;
+ }
+
+ private Object buildServiceInfoVersion3(String zone, int port, String name, Map properties) {
+ try {
+ Class[] args = new Class[6];
+ args[0] = String.class; //zone/type
+ args[1] = String.class; //display name
+ args[2] = int.class; //port
+ args[3] = int.class; //weight (0)
+ args[4] = int.class; //priority (0)
+ args[5] = Map.class;
+ Method serviceInfoCreateMethod = serviceInfoClass.getMethod("create", args);
+ Object[] values = new Object[6];
+ values[0] = zone;
+ values[1] = name;
+ values[2] = new Integer(port);
+ values[3] = new Integer(0);
+ values[4] = new Integer(0);
+ values[5] = properties;
+ Object result = serviceInfoCreateMethod.invoke(null, values);
+ LogLog.debug("created serviceinfo: " + result);
+ return result;
+ } catch (IllegalAccessException e) {
+ LogLog.warn("Unable to invoke create method", e);
+ } catch (NoSuchMethodException e) {
+ LogLog.warn("Unable to find create method", e);
+ } catch (InvocationTargetException e) {
+ LogLog.warn("Unable to invoke create method", e);
+ }
+ return null;
+ }
+
+ public void advertise() {
+ try {
+ Method method = jmDNSClass.getMethod("registerService", new Class[]{serviceInfoClass});
+ method.invoke(jmDNS, new Object[]{serviceInfo});
+ LogLog.debug("registered serviceInfo: " + serviceInfo);
+ } catch(IllegalAccessException e) {
+ LogLog.warn("Unable to invoke registerService method", e);
+ } catch(NoSuchMethodException e) {
+ LogLog.warn("No registerService method", e);
+ } catch(InvocationTargetException e) {
+ LogLog.warn("Unable to invoke registerService method", e);
+ }
+ }
+
+ public void unadvertise() {
+ try {
+ Method method = jmDNSClass.getMethod("unregisterService", new Class[]{serviceInfoClass});
+ method.invoke(jmDNS, new Object[]{serviceInfo});
+ LogLog.debug("unregistered serviceInfo: " + serviceInfo);
+ } catch(IllegalAccessException e) {
+ LogLog.warn("Unable to invoke unregisterService method", e);
+ } catch(NoSuchMethodException e) {
+ LogLog.warn("No unregisterService method", e);
+ } catch(InvocationTargetException e) {
+ LogLog.warn("Unable to invoke unregisterService method", e);
+ }
+ }
+
+ private static Object initializeJMDNS() {
+ try {
+ jmDNSClass = Class.forName("javax.jmdns.JmDNS");
+ serviceInfoClass = Class.forName("javax.jmdns.ServiceInfo");
+ } catch (ClassNotFoundException e) {
+ LogLog.warn("JmDNS or serviceInfo class not found", e);
+ }
+
+ //if version 3 is available, use it to constuct a serviceInfo instance, otherwise support the version1 API
+ boolean isVersion3 = false;
+ try {
+ //create method is in version 3, not version 1
+ jmDNSClass.getMethod("create", null);
+ isVersion3 = true;
+ } catch (NoSuchMethodException e) {
+ //no-op
+ }
+
+ if (isVersion3) {
+ return createJmDNSVersion3();
+ } else {
+ return createJmDNSVersion1();
+ }
+ }
+
+ public static Object getJMDNSInstance() {
+ return jmDNS;
+ }
+}
diff --git a/java/src/org/apache/log4j/net/package.html b/java/src/org/apache/log4j/net/package.html
new file mode 100644
index 0000000..1d2e965
--- /dev/null
+++ b/java/src/org/apache/log4j/net/package.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+Package for remote logging.
+
+
+
+
+Last modified: Tue Mar 21 20:28:14 MET 2000
+
+
diff --git a/java/src/org/apache/log4j/nt/NTEventLogAppender.java b/java/src/org/apache/log4j/nt/NTEventLogAppender.java
new file mode 100644
index 0000000..9f80add
--- /dev/null
+++ b/java/src/org/apache/log4j/nt/NTEventLogAppender.java
@@ -0,0 +1,182 @@
+/*
+ * 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.log4j.nt;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.Layout;
+import org.apache.log4j.TTCCLayout;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ Append to the NT event log system.
+
+ WARNING This appender can only be installed and used on a
+ Windows system.
+
+
Do not forget to place NTEventLogAppender.dll,
+ NTEventLogAppender.amd64.dll, NTEventLogAppender.ia64.dll
+ or NTEventLogAppender.x86.dll as appropriate in a
+ directory that is on the PATH of the Windows system. Otherwise, you
+ will get a java.lang.UnsatisfiedLinkError.
+
+ @author Chris Taylor
+ @author Jim Cakalic */
+public class NTEventLogAppender extends AppenderSkeleton {
+ private int _handle = 0;
+
+ private String source = null;
+ private String server = null;
+
+
+ public NTEventLogAppender() {
+ this(null, null, null);
+ }
+
+ public NTEventLogAppender(String source) {
+ this(null, source, null);
+ }
+
+ public NTEventLogAppender(String server, String source) {
+ this(server, source, null);
+ }
+
+ public NTEventLogAppender(Layout layout) {
+ this(null, null, layout);
+ }
+
+ public NTEventLogAppender(String source, Layout layout) {
+ this(null, source, layout);
+ }
+
+ public NTEventLogAppender(String server, String source, Layout layout) {
+ if (source == null) {
+ source = "Log4j";
+ }
+ if (layout == null) {
+ this.layout = new TTCCLayout();
+ } else {
+ this.layout = layout;
+ }
+
+ try {
+ _handle = registerEventSource(server, source);
+ } catch (Exception e) {
+ e.printStackTrace();
+ _handle = 0;
+ }
+ }
+
+ public
+ void close() {
+ // unregister ...
+ }
+
+ public
+ void activateOptions() {
+ if (source != null) {
+ try {
+ _handle = registerEventSource(server, source);
+ } catch (Exception e) {
+ LogLog.error("Could not register event source.", e);
+ _handle = 0;
+ }
+ }
+ }
+
+
+ public void append(LoggingEvent event) {
+
+ StringBuffer sbuf = new StringBuffer();
+
+ sbuf.append(layout.format(event));
+ if(layout.ignoresThrowable()) {
+ String[] s = event.getThrowableStrRep();
+ if (s != null) {
+ int len = s.length;
+ for(int i = 0; i < len; i++) {
+ sbuf.append(s[i]);
+ }
+ }
+ }
+ // Normalize the log message level into the supported categories
+ int nt_category = event.getLevel().toInt();
+
+ // Anything above FATAL or below DEBUG is labeled as INFO.
+ //if (nt_category > FATAL || nt_category < DEBUG) {
+ // nt_category = INFO;
+ //}
+ reportEvent(_handle, sbuf.toString(), nt_category);
+ }
+
+
+ public
+ void finalize() {
+ deregisterEventSource(_handle);
+ _handle = 0;
+ }
+
+ /**
+ The Source option which names the source of the event. The
+ current value of this constant is Source.
+ */
+ public
+ void setSource(String source) {
+ this.source = source.trim();
+ }
+
+ public
+ String getSource() {
+ return source;
+ }
+
+/**
+ The NTEventLogAppender requires a layout. Hence,
+ this method always returns true. */
+ public
+ boolean requiresLayout() {
+ return true;
+ }
+
+ native private int registerEventSource(String server, String source);
+ native private void reportEvent(int handle, String message, int level);
+ native private void deregisterEventSource(int handle);
+
+ static {
+ String[] archs;
+ try {
+ archs = new String[] { System.getProperty("os.arch")};
+ } catch(SecurityException e) {
+ archs = new String[] { "amd64", "ia64", "x86"};
+ }
+ boolean loaded = false;
+ for(int i = 0; i < archs.length; i++) {
+ try {
+ System.loadLibrary("NTEventLogAppender." + archs[i]);
+ loaded = true;
+ break;
+ } catch(java.lang.UnsatisfiedLinkError e) {
+ loaded = false;
+ }
+ }
+ if (!loaded) {
+ System.loadLibrary("NTEventLogAppender");
+ }
+}
+}
diff --git a/java/src/org/apache/log4j/nt/package.html b/java/src/org/apache/log4j/nt/package.html
new file mode 100644
index 0000000..a50567c
--- /dev/null
+++ b/java/src/org/apache/log4j/nt/package.html
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+Package for NT event logging.
+
+
+
+
+Last modified: Sat Apr 29 14:30:12 MDT 2000
+
+
diff --git a/java/src/org/apache/log4j/or/DefaultRenderer.java b/java/src/org/apache/log4j/or/DefaultRenderer.java
new file mode 100644
index 0000000..7fe4ac6
--- /dev/null
+++ b/java/src/org/apache/log4j/or/DefaultRenderer.java
@@ -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.
+ */
+
+package org.apache.log4j.or;
+
+/**
+ The default Renderer renders objects by calling their
+ toString method.
+
+ @author Ceki Gülcü
+ @since 1.0 */
+class DefaultRenderer implements ObjectRenderer {
+
+ DefaultRenderer() {
+ }
+
+ /**
+ Render the object passed as parameter by calling its
+ toString method. */
+ public
+ String doRender(final Object o) {
+ try {
+ return o.toString();
+ } catch(Exception ex) {
+ return ex.toString();
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/or/ObjectRenderer.java b/java/src/org/apache/log4j/or/ObjectRenderer.java
new file mode 100644
index 0000000..8ad9943
--- /dev/null
+++ b/java/src/org/apache/log4j/or/ObjectRenderer.java
@@ -0,0 +1,32 @@
+/*
+ * 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.log4j.or;
+
+/**
+ Implement this interface in order to render objects as strings.
+
+ @author Ceki Gülcü
+ @since 1.0 */
+public interface ObjectRenderer {
+
+ /**
+ Render the object passed as parameter as a String.
+ */
+ public
+ String doRender(Object o);
+}
diff --git a/java/src/org/apache/log4j/or/RendererMap.java b/java/src/org/apache/log4j/or/RendererMap.java
new file mode 100644
index 0000000..f60bd5a
--- /dev/null
+++ b/java/src/org/apache/log4j/or/RendererMap.java
@@ -0,0 +1,198 @@
+/*
+ * 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.log4j.or;
+
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.Loader;
+import org.apache.log4j.helpers.OptionConverter;
+import java.util.Hashtable;
+
+/**
+ Map class objects to an {@link ObjectRenderer}.
+
+ @author Ceki Gülcü
+ @since version 1.0 */
+public class RendererMap {
+
+ Hashtable map;
+
+ static ObjectRenderer defaultRenderer = new DefaultRenderer();
+
+ public
+ RendererMap() {
+ map = new Hashtable();
+ }
+
+ /**
+ Add a renderer to a hierarchy passed as parameter.
+ */
+ static
+ public
+ void addRenderer(RendererSupport repository, String renderedClassName,
+ String renderingClassName) {
+ LogLog.debug("Rendering class: ["+renderingClassName+"], Rendered class: ["+
+ renderedClassName+"].");
+ ObjectRenderer renderer = (ObjectRenderer)
+ OptionConverter.instantiateByClassName(renderingClassName,
+ ObjectRenderer.class,
+ null);
+ if(renderer == null) {
+ LogLog.error("Could not instantiate renderer ["+renderingClassName+"].");
+ return;
+ } else {
+ try {
+ Class renderedClass = Loader.loadClass(renderedClassName);
+ repository.setRenderer(renderedClass, renderer);
+ } catch(ClassNotFoundException e) {
+ LogLog.error("Could not find class ["+renderedClassName+"].", e);
+ }
+ }
+ }
+
+
+ /**
+ Find the appropriate renderer for the class type of the
+ o parameter. This is accomplished by calling the
+ {@link #get(Class)} method. Once a renderer is found, it is
+ applied on the object o and the result is returned
+ as a {@link String}. */
+ public
+ String findAndRender(Object o) {
+ if(o == null)
+ return null;
+ else
+ return get(o.getClass()).doRender(o);
+ }
+
+
+ /**
+ Syntactic sugar method that calls {@link #get(Class)} with the
+ class of the object parameter. */
+ public
+ ObjectRenderer get(Object o) {
+ if(o == null)
+ return null;
+ else
+ return get(o.getClass());
+ }
+
+
+ /**
+ Search the parents of clazz for a renderer. The
+ renderer closest in the hierarchy will be returned. If no
+ renderers could be found, then the default renderer is returned.
+
+ The search first looks for a renderer configured for
+ clazz. If a renderer could not be found, then the
+ search continues by looking at all the interfaces implemented by
+ clazz including the super-interfaces of each
+ interface. If a renderer cannot be found, then the search looks
+ for a renderer defined for the parent (superclass) of
+ clazz. If that fails, then all the interfaces
+ implemented by the parent of clazz are searched and
+ so on.
+
+
For example, if A0, A1, A2 are classes and X0, X1, X2, Y0, Y1
+ are interfaces where A2 extends A1 which in turn extends A0 and
+ similarly X2 extends X1 which extends X0 and Y1 extends Y0. Let
+ us also assume that A1 implements the Y0 interface and that A2
+ implements the X2 interface.
+
+
The table below shows the results returned by the
+ get(A2.class) method depending on the renderers
+ added to the map.
+
+
+ | Added renderers | Value returned by get(A2.class) |
+
+
|---|
A0Renderer
+ | A0Renderer
+
+ |
A0Renderer, A1Renderer
+ | A1Renderer
+
+ |
X0Renderer
+ | X0Renderer
+
+ |
A1Renderer, X0Renderer
+ | X0Renderer
+
+ |
+
+ This search algorithm is not the most natural, although it is
+ particularly easy to implement. Future log4j versions
+ may implement a more intuitive search
+ algorithm. However, the present algorithm should be acceptable in
+ the vast majority of circumstances.
+
+ */
+ public
+ ObjectRenderer get(Class clazz) {
+ //System.out.println("\nget: "+clazz);
+ ObjectRenderer r = null;
+ for(Class c = clazz; c != null; c = c.getSuperclass()) {
+ //System.out.println("Searching for class: "+c);
+ r = (ObjectRenderer) map.get(c);
+ if(r != null) {
+ return r;
+ }
+ r = searchInterfaces(c);
+ if(r != null)
+ return r;
+ }
+ return defaultRenderer;
+ }
+
+ ObjectRenderer searchInterfaces(Class c) {
+ //System.out.println("Searching interfaces of class: "+c);
+
+ ObjectRenderer r = (ObjectRenderer) map.get(c);
+ if(r != null) {
+ return r;
+ } else {
+ Class[] ia = c.getInterfaces();
+ for(int i = 0; i < ia.length; i++) {
+ r = searchInterfaces(ia[i]);
+ if(r != null)
+ return r;
+ }
+ }
+ return null;
+ }
+
+
+ public
+ ObjectRenderer getDefaultRenderer() {
+ return defaultRenderer;
+ }
+
+
+ public
+ void clear() {
+ map.clear();
+ }
+
+ /**
+ Register an {@link ObjectRenderer} for clazz.
+ */
+ public
+ void put(Class clazz, ObjectRenderer or) {
+ map.put(clazz, or);
+ }
+}
diff --git a/java/src/org/apache/log4j/or/ThreadGroupRenderer.java b/java/src/org/apache/log4j/or/ThreadGroupRenderer.java
new file mode 100644
index 0000000..026ff4f
--- /dev/null
+++ b/java/src/org/apache/log4j/or/ThreadGroupRenderer.java
@@ -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.
+ */
+
+package org.apache.log4j.or;
+
+import org.apache.log4j.Layout;
+
+
+/**
+ Render {@link ThreadGroup} objects in a format similar to the
+ information output by the {@link ThreadGroup#list} method.
+ @author Ceki Gülcü
+ @since 1.0 */
+public class ThreadGroupRenderer implements ObjectRenderer {
+
+ public
+ ThreadGroupRenderer() {
+ }
+
+ /**
+ Render a {@link ThreadGroup} object similar to the way that the
+ {@link ThreadGroup#list} method output information.
+
+
The output of a simple program consisting of one
+ main thread is:
+
+ java.lang.ThreadGroup[name=main, maxpri=10]
+ Thread=[main,5,false]
+
+
+ The boolean value in thread information is the value returned
+ by {@link Thread#isDaemon}.
+
+ */
+ public
+ String doRender(Object o) {
+ if(o instanceof ThreadGroup) {
+ StringBuffer sbuf = new StringBuffer();
+ ThreadGroup tg = (ThreadGroup) o;
+ sbuf.append("java.lang.ThreadGroup[name=");
+ sbuf.append(tg.getName());
+ sbuf.append(", maxpri=");
+ sbuf.append(tg.getMaxPriority());
+ sbuf.append("]");
+ Thread[] t = new Thread[tg.activeCount()];
+ tg.enumerate(t);
+ for(int i = 0; i < t.length; i++) {
+ sbuf.append(Layout.LINE_SEP);
+ sbuf.append(" Thread=[");
+ sbuf.append(t[i].getName());
+ sbuf.append(",");
+ sbuf.append(t[i].getPriority());
+ sbuf.append(",");
+ sbuf.append(t[i].isDaemon());
+ sbuf.append("]");
+ }
+ return sbuf.toString();
+ } else {
+ try {
+ // this is the best we can do
+ return o.toString();
+ } catch(Exception ex) {
+ return ex.toString();
+ }
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/or/jms/MessageRenderer.java b/java/src/org/apache/log4j/or/jms/MessageRenderer.java
new file mode 100644
index 0000000..e3140cb
--- /dev/null
+++ b/java/src/org/apache/log4j/or/jms/MessageRenderer.java
@@ -0,0 +1,100 @@
+/*
+ * 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.log4j.or.jms;
+
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.or.ObjectRenderer;
+
+import javax.jms.Message;
+import javax.jms.JMSException;
+import javax.jms.DeliveryMode;
+
+/**
+ Render javax.jms.Message objects.
+
+ @author Ceki Gülcü
+ @since 1.0 */
+public class MessageRenderer implements ObjectRenderer {
+
+ public
+ MessageRenderer() {
+ }
+
+
+ /**
+ Render a {@link javax.jms.Message}.
+ */
+ public
+ String doRender(Object o) {
+ if(o instanceof Message) {
+ StringBuffer sbuf = new StringBuffer();
+ Message m = (Message) o;
+ try {
+ sbuf.append("DeliveryMode=");
+ switch(m.getJMSDeliveryMode()) {
+ case DeliveryMode.NON_PERSISTENT :
+ sbuf.append("NON_PERSISTENT");
+ break;
+ case DeliveryMode.PERSISTENT :
+ sbuf.append("PERSISTENT");
+ break;
+ default: sbuf.append("UNKNOWN");
+ }
+ sbuf.append(", CorrelationID=");
+ sbuf.append(m.getJMSCorrelationID());
+
+ sbuf.append(", Destination=");
+ sbuf.append(m.getJMSDestination());
+
+ sbuf.append(", Expiration=");
+ sbuf.append(m.getJMSExpiration());
+
+ sbuf.append(", MessageID=");
+ sbuf.append(m.getJMSMessageID());
+
+ sbuf.append(", Priority=");
+ sbuf.append(m.getJMSPriority());
+
+ sbuf.append(", Redelivered=");
+ sbuf.append(m.getJMSRedelivered());
+
+ sbuf.append(", ReplyTo=");
+ sbuf.append(m.getJMSReplyTo());
+
+ sbuf.append(", Timestamp=");
+ sbuf.append(m.getJMSTimestamp());
+
+ sbuf.append(", Type=");
+ sbuf.append(m.getJMSType());
+
+ //Enumeration enum = m.getPropertyNames();
+ //while(enum.hasMoreElements()) {
+ // String key = (String) enum.nextElement();
+ // sbuf.append("; "+key+"=");
+ // sbuf.append(m.getStringProperty(key));
+ //}
+
+ } catch(JMSException e) {
+ LogLog.error("Could not parse Message.", e);
+ }
+ return sbuf.toString();
+ } else {
+ return o.toString();
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/or/jms/package.html b/java/src/org/apache/log4j/or/jms/package.html
new file mode 100644
index 0000000..d4db1c8
--- /dev/null
+++ b/java/src/org/apache/log4j/or/jms/package.html
@@ -0,0 +1,24 @@
+
+
+
+
+ This package contains the MessageRenderer which renders objects of
+ type javax.jms.Message.
+
+
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/or/package.html b/java/src/org/apache/log4j/or/package.html
new file mode 100644
index 0000000..17fd176
--- /dev/null
+++ b/java/src/org/apache/log4j/or/package.html
@@ -0,0 +1,30 @@
+
+
+
+
+org.apache.log4j.or package
+
+
+
+
+ObjectRenders are resposible for rendering messages depending on
+their class type.
+
+
+
diff --git a/java/src/org/apache/log4j/or/sax/AttributesRenderer.java b/java/src/org/apache/log4j/or/sax/AttributesRenderer.java
new file mode 100644
index 0000000..b5d088c
--- /dev/null
+++ b/java/src/org/apache/log4j/or/sax/AttributesRenderer.java
@@ -0,0 +1,66 @@
+/*
+ * 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.log4j.or.sax;
+
+import org.apache.log4j.or.ObjectRenderer;
+
+import org.xml.sax.Attributes;
+
+/**
+ Render org.xml.sax.Attributes objects.
+
+ @author Ceki Gülcü
+ @since 1.2 */
+public class AttributesRenderer implements ObjectRenderer {
+
+ public
+ AttributesRenderer() {
+ }
+
+
+ /**
+ Render a {@link org.xml.sax.Attributes}.
+ */
+ public
+ String doRender(Object o) {
+ if(o instanceof Attributes) {
+ StringBuffer sbuf = new StringBuffer();
+ Attributes a = (Attributes) o;
+ int len = a.getLength();
+ boolean first = true;
+ for(int i = 0; i < len; i++) {
+ if(first) {
+ first = false;
+ } else {
+ sbuf.append(", ");
+ }
+ sbuf.append(a.getQName(i));
+ sbuf.append('=');
+ sbuf.append(a.getValue(i));
+ }
+ return sbuf.toString();
+ } else {
+ try {
+ return o.toString();
+ } catch(Exception ex) {
+ return ex.toString();
+ }
+ }
+ }
+}
+
diff --git a/java/src/org/apache/log4j/or/sax/package.html b/java/src/org/apache/log4j/or/sax/package.html
new file mode 100644
index 0000000..a597141
--- /dev/null
+++ b/java/src/org/apache/log4j/or/sax/package.html
@@ -0,0 +1,24 @@
+
+
+
+
+ This package contains the AttributesRenderer which renders object of
+ class org.xml.sax.Attributes.
+
+
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/package.html b/java/src/org/apache/log4j/package.html
new file mode 100644
index 0000000..3cf6e3e
--- /dev/null
+++ b/java/src/org/apache/log4j/package.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+The main log4j package.
+
+
+
diff --git a/java/src/org/apache/log4j/pattern/BridgePatternConverter.java b/java/src/org/apache/log4j/pattern/BridgePatternConverter.java
new file mode 100644
index 0000000..96068d5
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/BridgePatternConverter.java
@@ -0,0 +1,132 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+
+/**
+ * The class implements the pre log4j 1.3 org.apache.log4j.helpers.PatternConverter
+ * contract by delegating to the log4j 1.3 pattern implementation.
+ *
+ *
+ * @author Curt Arnold
+ *
+ */
+public final class BridgePatternConverter
+ extends org.apache.log4j.helpers.PatternConverter {
+ /**
+ * Pattern converters.
+ */
+ private LoggingEventPatternConverter[] patternConverters;
+
+ /**
+ * Field widths and alignment corresponding to pattern converters.
+ */
+ private FormattingInfo[] patternFields;
+
+ /**
+ * Does pattern process exceptions.
+ */
+ private boolean handlesExceptions;
+
+ /**
+ * Create a new instance.
+ * @param pattern pattern, may not be null.
+ */
+ public BridgePatternConverter(
+ final String pattern) {
+ next = null;
+ handlesExceptions = false;
+
+ List converters = new ArrayList();
+ List fields = new ArrayList();
+ Map converterRegistry = null;
+
+ PatternParser.parse(
+ pattern, converters, fields, converterRegistry,
+ PatternParser.getPatternLayoutRules());
+
+ patternConverters = new LoggingEventPatternConverter[converters.size()];
+ patternFields = new FormattingInfo[converters.size()];
+
+ int i = 0;
+ Iterator converterIter = converters.iterator();
+ Iterator fieldIter = fields.iterator();
+
+ while (converterIter.hasNext()) {
+ Object converter = converterIter.next();
+
+ if (converter instanceof LoggingEventPatternConverter) {
+ patternConverters[i] = (LoggingEventPatternConverter) converter;
+ handlesExceptions |= patternConverters[i].handlesThrowable();
+ } else {
+ patternConverters[i] =
+ new org.apache.log4j.pattern.LiteralPatternConverter("");
+ }
+
+ if (fieldIter.hasNext()) {
+ patternFields[i] = (FormattingInfo) fieldIter.next();
+ } else {
+ patternFields[i] = FormattingInfo.getDefault();
+ }
+
+ i++;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ protected String convert(final LoggingEvent event) {
+ //
+ // code should be unreachable.
+ //
+ StringBuffer sbuf = new StringBuffer();
+ format(sbuf, event);
+
+ return sbuf.toString();
+ }
+
+ /**
+ Format event to string buffer.
+ @param sbuf string buffer to receive formatted event, may not be null.
+ @param e event to format, may not be null.
+ */
+ public void format(final StringBuffer sbuf, final LoggingEvent e) {
+ for (int i = 0; i < patternConverters.length; i++) {
+ int startField = sbuf.length();
+ patternConverters[i].format(e, sbuf);
+ patternFields[i].format(startField, sbuf);
+ }
+ }
+
+ /**
+ * Will return false if any of the conversion specifiers in the pattern
+ * handles {@link Exception Exceptions}.
+ * @return true if the pattern formats any information from exceptions.
+ */
+ public boolean ignoresThrowable() {
+ return !handlesExceptions;
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/BridgePatternParser.java b/java/src/org/apache/log4j/pattern/BridgePatternParser.java
new file mode 100644
index 0000000..1c0d4e7
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/BridgePatternParser.java
@@ -0,0 +1,48 @@
+/*
+ * 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.log4j.pattern;
+
+/**
+ * The class implements the pre log4j 1.3 org.apache.log4j.helpers.PatternConverter
+ * contract by delegating to the log4j 1.3 pattern implementation.
+ *
+ *
+ * @author Curt Arnold
+ *
+ */
+public final class BridgePatternParser
+ extends org.apache.log4j.helpers.PatternParser {
+
+
+ /**
+ * Create a new instance.
+ * @param conversionPattern pattern, may not be null.
+ */
+ public BridgePatternParser(
+ final String conversionPattern) {
+ super(conversionPattern);
+ }
+
+ /**
+ * Create new pattern converter.
+ * @return pattern converter.
+ */
+ public org.apache.log4j.helpers.PatternConverter parse() {
+ return new BridgePatternConverter(pattern);
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/CachedDateFormat.java b/java/src/org/apache/log4j/pattern/CachedDateFormat.java
new file mode 100644
index 0000000..9a468aa
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/CachedDateFormat.java
@@ -0,0 +1,372 @@
+/*
+ * 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.log4j.pattern;
+
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.NumberFormat;
+import java.text.ParsePosition;
+import java.util.Date;
+import java.util.TimeZone;
+
+
+/**
+ * CachedDateFormat optimizes the performance of a wrapped
+ * DateFormat. The implementation is not thread-safe.
+ * If the millisecond pattern is not recognized,
+ * the class will only use the cache if the
+ * same value is requested.
+ *
+ */
+public final class CachedDateFormat extends DateFormat {
+ /**
+ * Serialization version.
+ */
+ private static final long serialVersionUID = 1;
+ /**
+ * Constant used to represent that there was no change
+ * observed when changing the millisecond count.
+ */
+ public static final int NO_MILLISECONDS = -2;
+
+ /**
+ * Supported digit set. If the wrapped DateFormat uses
+ * a different unit set, the millisecond pattern
+ * will not be recognized and duplicate requests
+ * will use the cache.
+ */
+ private static final String DIGITS = "0123456789";
+
+ /**
+ * Constant used to represent that there was an
+ * observed change, but was an expected change.
+ */
+ public static final int UNRECOGNIZED_MILLISECONDS = -1;
+
+ /**
+ * First magic number used to detect the millisecond position.
+ */
+ private static final int MAGIC1 = 654;
+
+ /**
+ * Expected representation of first magic number.
+ */
+ private static final String MAGICSTRING1 = "654";
+
+ /**
+ * Second magic number used to detect the millisecond position.
+ */
+ private static final int MAGIC2 = 987;
+
+ /**
+ * Expected representation of second magic number.
+ */
+ private static final String MAGICSTRING2 = "987";
+
+ /**
+ * Expected representation of 0 milliseconds.
+ */
+ private static final String ZERO_STRING = "000";
+
+ /**
+ * Wrapped formatter.
+ */
+ private final DateFormat formatter;
+
+ /**
+ * Index of initial digit of millisecond pattern or
+ * UNRECOGNIZED_MILLISECONDS or NO_MILLISECONDS.
+ */
+ private int millisecondStart;
+
+ /**
+ * Integral second preceding the previous convered Date.
+ */
+ private long slotBegin;
+
+ /**
+ * Cache of previous conversion.
+ */
+ private StringBuffer cache = new StringBuffer(50);
+
+ /**
+ * Maximum validity period for the cache.
+ * Typically 1, use cache for duplicate requests only, or
+ * 1000, use cache for requests within the same integral second.
+ */
+ private final int expiration;
+
+ /**
+ * Date requested in previous conversion.
+ */
+ private long previousTime;
+
+ /**
+ * Scratch date object used to minimize date object creation.
+ */
+ private final Date tmpDate = new Date(0);
+
+ /**
+ * Creates a new CachedDateFormat object.
+ * @param dateFormat Date format, may not be null.
+ * @param expiration maximum cached range in milliseconds.
+ * If the dateFormat is known to be incompatible with the
+ * caching algorithm, use a value of 0 to totally disable
+ * caching or 1 to only use cache for duplicate requests.
+ */
+ public CachedDateFormat(final DateFormat dateFormat, final int expiration) {
+ if (dateFormat == null) {
+ throw new IllegalArgumentException("dateFormat cannot be null");
+ }
+
+ if (expiration < 0) {
+ throw new IllegalArgumentException("expiration must be non-negative");
+ }
+
+ formatter = dateFormat;
+ this.expiration = expiration;
+ millisecondStart = 0;
+
+ //
+ // set the previousTime so the cache will be invalid
+ // for the next request.
+ previousTime = Long.MIN_VALUE;
+ slotBegin = Long.MIN_VALUE;
+ }
+
+ /**
+ * Finds start of millisecond field in formatted time.
+ * @param time long time, must be integral number of seconds
+ * @param formatted String corresponding formatted string
+ * @param formatter DateFormat date format
+ * @return int position in string of first digit of milliseconds,
+ * -1 indicates no millisecond field, -2 indicates unrecognized
+ * field (likely RelativeTimeDateFormat)
+ */
+ public static int findMillisecondStart(
+ final long time, final String formatted, final DateFormat formatter) {
+ long slotBegin = (time / 1000) * 1000;
+
+ if (slotBegin > time) {
+ slotBegin -= 1000;
+ }
+
+ int millis = (int) (time - slotBegin);
+
+ int magic = MAGIC1;
+ String magicString = MAGICSTRING1;
+
+ if (millis == MAGIC1) {
+ magic = MAGIC2;
+ magicString = MAGICSTRING2;
+ }
+
+ String plusMagic = formatter.format(new Date(slotBegin + magic));
+
+ /**
+ * If the string lengths differ then
+ * we can't use the cache except for duplicate requests.
+ */
+ if (plusMagic.length() != formatted.length()) {
+ return UNRECOGNIZED_MILLISECONDS;
+ } else {
+ // find first difference between values
+ for (int i = 0; i < formatted.length(); i++) {
+ if (formatted.charAt(i) != plusMagic.charAt(i)) {
+ //
+ // determine the expected digits for the base time
+ StringBuffer formattedMillis = new StringBuffer("ABC");
+ millisecondFormat(millis, formattedMillis, 0);
+
+ String plusZero = formatter.format(new Date(slotBegin));
+
+ // If the next 3 characters match the magic
+ // string and the expected string
+ if (
+ (plusZero.length() == formatted.length())
+ && magicString.regionMatches(
+ 0, plusMagic, i, magicString.length())
+ && formattedMillis.toString().regionMatches(
+ 0, formatted, i, magicString.length())
+ && ZERO_STRING.regionMatches(
+ 0, plusZero, i, ZERO_STRING.length())) {
+ return i;
+ } else {
+ return UNRECOGNIZED_MILLISECONDS;
+ }
+ }
+ }
+ }
+
+ return NO_MILLISECONDS;
+ }
+
+ /**
+ * Formats a Date into a date/time string.
+ *
+ * @param date the date to format.
+ * @param sbuf the string buffer to write to.
+ * @param fieldPosition remains untouched.
+ * @return the formatted time string.
+ */
+ public StringBuffer format(
+ Date date, StringBuffer sbuf, FieldPosition fieldPosition) {
+ format(date.getTime(), sbuf);
+
+ return sbuf;
+ }
+
+ /**
+ * Formats a millisecond count into a date/time string.
+ *
+ * @param now Number of milliseconds after midnight 1 Jan 1970 GMT.
+ * @param buf the string buffer to write to.
+ * @return the formatted time string.
+ */
+ public StringBuffer format(long now, StringBuffer buf) {
+ //
+ // If the current requested time is identical to the previously
+ // requested time, then append the cache contents.
+ //
+ if (now == previousTime) {
+ buf.append(cache);
+
+ return buf;
+ }
+
+ //
+ // If millisecond pattern was not unrecognized
+ // (that is if it was found or milliseconds did not appear)
+ //
+ if (millisecondStart != UNRECOGNIZED_MILLISECONDS &&
+ // Check if the cache is still valid.
+ // If the requested time is within the same integral second
+ // as the last request and a shorter expiration was not requested.
+ (now < (slotBegin + expiration)) && (now >= slotBegin)
+ && (now < (slotBegin + 1000L))) {
+ //
+ // if there was a millisecond field then update it
+ //
+ if (millisecondStart >= 0) {
+ millisecondFormat((int) (now - slotBegin), cache, millisecondStart);
+ }
+
+ //
+ // update the previously requested time
+ // (the slot begin should be unchanged)
+ previousTime = now;
+ buf.append(cache);
+
+ return buf;
+ }
+
+ //
+ // could not use previous value.
+ // Call underlying formatter to format date.
+ cache.setLength(0);
+ tmpDate.setTime(now);
+ cache.append(formatter.format(tmpDate));
+ buf.append(cache);
+ previousTime = now;
+ slotBegin = (previousTime / 1000) * 1000;
+
+ if (slotBegin > previousTime) {
+ slotBegin -= 1000;
+ }
+
+ //
+ // if the milliseconds field was previous found
+ // then reevaluate in case it moved.
+ //
+ if (millisecondStart >= 0) {
+ millisecondStart =
+ findMillisecondStart(now, cache.toString(), formatter);
+ }
+
+ return buf;
+ }
+
+ /**
+ * Formats a count of milliseconds (0-999) into a numeric representation.
+ * @param millis Millisecond coun between 0 and 999.
+ * @param buf String buffer, may not be null.
+ * @param offset Starting position in buffer, the length of the
+ * buffer must be at least offset + 3.
+ */
+ private static void millisecondFormat(
+ final int millis, final StringBuffer buf, final int offset) {
+ buf.setCharAt(offset, DIGITS.charAt(millis / 100));
+ buf.setCharAt(offset + 1, DIGITS.charAt((millis / 10) % 10));
+ buf.setCharAt(offset + 2, DIGITS.charAt(millis % 10));
+ }
+
+ /**
+ * Set timezone.
+ *
+ * Setting the timezone using getCalendar().setTimeZone()
+ * will likely cause caching to misbehave.
+ * @param timeZone TimeZone new timezone
+ */
+ public void setTimeZone(final TimeZone timeZone) {
+ formatter.setTimeZone(timeZone);
+ previousTime = Long.MIN_VALUE;
+ slotBegin = Long.MIN_VALUE;
+ }
+
+ /**
+ * This method is delegated to the formatter which most
+ * likely returns null.
+ * @param s string representation of date.
+ * @param pos field position, unused.
+ * @return parsed date, likely null.
+ */
+ public Date parse(String s, ParsePosition pos) {
+ return formatter.parse(s, pos);
+ }
+
+ /**
+ * Gets number formatter.
+ *
+ * @return NumberFormat number formatter
+ */
+ public NumberFormat getNumberFormat() {
+ return formatter.getNumberFormat();
+ }
+
+ /**
+ * Gets maximum cache validity for the specified SimpleDateTime
+ * conversion pattern.
+ * @param pattern conversion pattern, may not be null.
+ * @return Duration in milliseconds from an integral second
+ * that the cache will return consistent results.
+ */
+ public static int getMaximumCacheValidity(final String pattern) {
+ //
+ // If there are more "S" in the pattern than just one "SSS" then
+ // (for example, "HH:mm:ss,SSS SSS"), then set the expiration to
+ // one millisecond which should only perform duplicate request caching.
+ //
+ int firstS = pattern.indexOf('S');
+
+ if ((firstS >= 0) && (firstS != pattern.lastIndexOf("SSS"))) {
+ return 1;
+ }
+
+ return 1000;
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/ClassNamePatternConverter.java b/java/src/org/apache/log4j/pattern/ClassNamePatternConverter.java
new file mode 100644
index 0000000..7a24916
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/ClassNamePatternConverter.java
@@ -0,0 +1,66 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Formats the class name of the site of the logging request.
+ *
+ * @author Ceki Gülcü
+ */
+public final class ClassNamePatternConverter extends NamePatternConverter {
+ /**
+ * Private constructor.
+ * @param options options, may be null.
+ */
+ private ClassNamePatternConverter(
+ final String[] options) {
+ super("Class Name", "class name", options);
+ }
+
+ /**
+ * Gets an instance of ClassNamePatternConverter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static ClassNamePatternConverter newInstance(
+ final String[] options) {
+ return new ClassNamePatternConverter(options);
+ }
+
+ /**
+ * Format a logging event.
+ * @param event event to format.
+ * @param toAppendTo string buffer to which class name will be appended.
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ final int initialLength = toAppendTo.length();
+ LocationInfo li = event.getLocationInformation();
+
+ if (li == null) {
+ toAppendTo.append(LocationInfo.NA);
+ } else {
+ toAppendTo.append(li.getClassName());
+ }
+
+ abbreviate(initialLength, toAppendTo);
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/DatePatternConverter.java b/java/src/org/apache/log4j/pattern/DatePatternConverter.java
new file mode 100644
index 0000000..7162fe7
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/DatePatternConverter.java
@@ -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.
+ */
+
+package org.apache.log4j.pattern;
+
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.text.SimpleDateFormat;
+import java.text.DateFormat;
+import java.text.FieldPosition;
+import java.text.ParsePosition;
+import java.util.Date;
+import java.util.TimeZone;
+
+
+/**
+ * Convert and format the event's date in a StringBuffer.
+ *
+ * @author Ceki Gülcü
+ */
+public final class DatePatternConverter extends LoggingEventPatternConverter {
+ /**
+ * ABSOLUTE string literal.
+ */
+ private static final String ABSOLUTE_FORMAT = "ABSOLUTE";
+ /**
+ * SimpleTimePattern for ABSOLUTE.
+ */
+ private static final String ABSOLUTE_TIME_PATTERN = "HH:mm:ss,SSS";
+
+
+ /**
+ * DATE string literal.
+ */
+ private static final String DATE_AND_TIME_FORMAT = "DATE";
+ /**
+ * SimpleTimePattern for DATE.
+ */
+ private static final String DATE_AND_TIME_PATTERN = "dd MMM yyyy HH:mm:ss,SSS";
+
+ /**
+ * ISO8601 string literal.
+ */
+ private static final String ISO8601_FORMAT = "ISO8601";
+ /**
+ * SimpleTimePattern for ISO8601.
+ */
+ private static final String ISO8601_PATTERN = "yyyy-MM-dd HH:mm:ss,SSS";
+ /**
+ * Date format.
+ */
+ private final CachedDateFormat df;
+
+ /**
+ * This class wraps a DateFormat and forces the time zone to the
+ * default time zone before each format and parse request.
+ */
+ private static class DefaultZoneDateFormat extends DateFormat {
+ /**
+ * Serialization version ID.
+ */
+ private static final long serialVersionUID = 1;
+ /**
+ * Wrapped instance of DateFormat.
+ */
+ private final DateFormat dateFormat;
+
+ /**
+ * Construct new instance.
+ * @param format format, may not be null.
+ */
+ public DefaultZoneDateFormat(final DateFormat format) {
+ dateFormat = format;
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public StringBuffer format(Date date, StringBuffer toAppendTo, FieldPosition fieldPosition) {
+ dateFormat.setTimeZone(TimeZone.getDefault());
+ return dateFormat.format(date, toAppendTo, fieldPosition);
+ }
+
+ /**
+ * @{inheritDoc}
+ */
+ public Date parse(String source, ParsePosition pos) {
+ dateFormat.setTimeZone(TimeZone.getDefault());
+ return dateFormat.parse(source, pos);
+ }
+ }
+
+ /**
+ * Private constructor.
+ * @param options options, may be null.
+ */
+ private DatePatternConverter(final String[] options) {
+ super("Date", "date");
+
+ String patternOption;
+
+ if ((options == null) || (options.length == 0)) {
+ // the branch could be optimized, but here we are making explicit
+ // that null values for patternOption are allowed.
+ patternOption = null;
+ } else {
+ patternOption = options[0];
+ }
+
+ String pattern;
+
+ if (
+ (patternOption == null)
+ || patternOption.equalsIgnoreCase(ISO8601_FORMAT)) {
+ pattern = ISO8601_PATTERN;
+ } else if (patternOption.equalsIgnoreCase(ABSOLUTE_FORMAT)) {
+ pattern = ABSOLUTE_TIME_PATTERN;
+ } else if (patternOption.equalsIgnoreCase(DATE_AND_TIME_FORMAT)) {
+ pattern = DATE_AND_TIME_PATTERN;
+ } else {
+ pattern = patternOption;
+ }
+
+ int maximumCacheValidity = 1000;
+ DateFormat simpleFormat = null;
+
+ try {
+ simpleFormat = new SimpleDateFormat(pattern);
+ maximumCacheValidity = CachedDateFormat.getMaximumCacheValidity(pattern);
+ } catch (IllegalArgumentException e) {
+ LogLog.warn(
+ "Could not instantiate SimpleDateFormat with pattern "
+ + patternOption, e);
+
+ // default to the ISO8601 format
+ simpleFormat = new SimpleDateFormat(ISO8601_PATTERN);
+ }
+
+ // if the option list contains a TZ option, then set it.
+ if ((options != null) && (options.length > 1)) {
+ TimeZone tz = TimeZone.getTimeZone((String) options[1]);
+ simpleFormat.setTimeZone(tz);
+ } else {
+ simpleFormat = new DefaultZoneDateFormat(simpleFormat);
+ }
+
+ df = new CachedDateFormat(simpleFormat, maximumCacheValidity);
+ }
+
+ /**
+ * Obtains an instance of pattern converter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static DatePatternConverter newInstance(
+ final String[] options) {
+ return new DatePatternConverter(options);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer output) {
+ synchronized(this) {
+ df.format(event.timeStamp, output);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final Object obj, final StringBuffer output) {
+ if (obj instanceof Date) {
+ format((Date) obj, output);
+ }
+
+ super.format(obj, output);
+ }
+
+ /**
+ * Append formatted date to string buffer.
+ * @param date date
+ * @param toAppendTo buffer to which formatted date is appended.
+ */
+ public void format(final Date date, final StringBuffer toAppendTo) {
+ synchronized(this) {
+ df.format(date.getTime(), toAppendTo);
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/FileDatePatternConverter.java b/java/src/org/apache/log4j/pattern/FileDatePatternConverter.java
new file mode 100644
index 0000000..1e08826
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/FileDatePatternConverter.java
@@ -0,0 +1,50 @@
+/*
+ * 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.log4j.pattern;
+
+/**
+ * Formats an date by delegating to DatePatternConverter. The default
+ * date pattern for a %d specifier in a file name is different than
+ * the %d pattern in pattern layout.
+ *
+ * @author Curt Arnold
+ */
+public final class FileDatePatternConverter {
+ /**
+ * Private constructor.
+ */
+ private FileDatePatternConverter() {
+ }
+
+ /**
+ * Obtains an instance of pattern converter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static PatternConverter newInstance(
+ final String[] options) {
+ if ((options == null) || (options.length == 0)) {
+ return DatePatternConverter.newInstance(
+ new String[] {
+ "yyyy-MM-dd"
+ });
+ }
+
+ return DatePatternConverter.newInstance(options);
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/FileLocationPatternConverter.java b/java/src/org/apache/log4j/pattern/FileLocationPatternConverter.java
new file mode 100644
index 0000000..14eb31c
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/FileLocationPatternConverter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Return the event's line location information in a StringBuffer.
+ *
+ * @author Ceki Gülcü
+ */
+public final class FileLocationPatternConverter
+ extends LoggingEventPatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final FileLocationPatternConverter INSTANCE =
+ new FileLocationPatternConverter();
+
+ /**
+ * Private constructor.
+ */
+ private FileLocationPatternConverter() {
+ super("File Location", "file");
+ }
+
+ /**
+ * Obtains an instance of pattern converter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static FileLocationPatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer output) {
+ LocationInfo locationInfo = event.getLocationInformation();
+
+ if (locationInfo != null) {
+ output.append(locationInfo.getFileName());
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/FormattingInfo.java b/java/src/org/apache/log4j/pattern/FormattingInfo.java
new file mode 100644
index 0000000..8a1a12e
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/FormattingInfo.java
@@ -0,0 +1,134 @@
+/*
+ * 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.log4j.pattern;
+
+
+/**
+ * Modifies the output of a pattern converter for a specified minimum
+ * and maximum width and alignment.
+ *
+ *
+ * @author Jim Cakalic
+ * @author Ceki Gülcü
+ * @author Curt Arnold
+ *
+ */
+public final class FormattingInfo {
+ /**
+ * Array of spaces.
+ */
+ private static final char[] SPACES =
+ new char[] { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' };
+
+ /**
+ * Default instance.
+ */
+ private static final FormattingInfo DEFAULT =
+ new FormattingInfo(false, 0, Integer.MAX_VALUE);
+
+ /**
+ * Minimum length.
+ */
+ private final int minLength;
+
+ /**
+ * Maximum length.
+ */
+ private final int maxLength;
+
+ /**
+ * Alignment.
+ */
+ private final boolean leftAlign;
+
+ /**
+ * Creates new instance.
+ * @param leftAlign left align if true.
+ * @param minLength minimum length.
+ * @param maxLength maximum length.
+ */
+ public FormattingInfo(
+ final boolean leftAlign, final int minLength, final int maxLength) {
+ this.leftAlign = leftAlign;
+ this.minLength = minLength;
+ this.maxLength = maxLength;
+ }
+
+ /**
+ * Gets default instance.
+ * @return default instance.
+ */
+ public static FormattingInfo getDefault() {
+ return DEFAULT;
+ }
+
+ /**
+ * Determine if left aligned.
+ * @return true if left aligned.
+ */
+ public boolean isLeftAligned() {
+ return leftAlign;
+ }
+
+ /**
+ * Get minimum length.
+ * @return minimum length.
+ */
+ public int getMinLength() {
+ return minLength;
+ }
+
+ /**
+ * Get maximum length.
+ * @return maximum length.
+ */
+ public int getMaxLength() {
+ return maxLength;
+ }
+
+ /**
+ * Adjust the content of the buffer based on the specified lengths and alignment.
+ *
+ * @param fieldStart start of field in buffer.
+ * @param buffer buffer to be modified.
+ */
+ public void format(final int fieldStart, final StringBuffer buffer) {
+ final int rawLength = buffer.length() - fieldStart;
+
+ if (rawLength > maxLength) {
+ buffer.delete(fieldStart, buffer.length() - maxLength);
+ } else if (rawLength < minLength) {
+ if (leftAlign) {
+ final int fieldEnd = buffer.length();
+ buffer.setLength(fieldStart + minLength);
+
+ for (int i = fieldEnd; i < buffer.length(); i++) {
+ buffer.setCharAt(i, ' ');
+ }
+ } else {
+ int padLength = minLength - rawLength;
+
+ for (; padLength > 8; padLength -= 8) {
+ buffer.insert(fieldStart, SPACES);
+ }
+
+ buffer.insert(fieldStart, SPACES, 0, padLength);
+ }
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/FullLocationPatternConverter.java b/java/src/org/apache/log4j/pattern/FullLocationPatternConverter.java
new file mode 100644
index 0000000..066016c
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/FullLocationPatternConverter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Format the event's line location information.
+ *
+ * @author Ceki Gülcü
+ */
+public final class FullLocationPatternConverter
+ extends LoggingEventPatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final FullLocationPatternConverter INSTANCE =
+ new FullLocationPatternConverter();
+
+ /**
+ * Private constructor.
+ */
+ private FullLocationPatternConverter() {
+ super("Full Location", "fullLocation");
+ }
+
+ /**
+ * Obtains an instance of pattern converter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static FullLocationPatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer output) {
+ LocationInfo locationInfo = event.getLocationInformation();
+
+ if (locationInfo != null) {
+ output.append(locationInfo.fullInfo);
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/IntegerPatternConverter.java b/java/src/org/apache/log4j/pattern/IntegerPatternConverter.java
new file mode 100644
index 0000000..f50feeb
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/IntegerPatternConverter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.log4j.pattern;
+
+import java.util.Date;
+
+
+/**
+ * Formats an integer.
+ *
+ * @author Curt Arnold
+ */
+public final class IntegerPatternConverter extends PatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final IntegerPatternConverter INSTANCE =
+ new IntegerPatternConverter();
+
+ /**
+ * Private constructor.
+ */
+ private IntegerPatternConverter() {
+ super("Integer", "integer");
+ }
+
+ /**
+ * Obtains an instance of pattern converter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static IntegerPatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(Object obj, final StringBuffer toAppendTo) {
+ if (obj instanceof Integer) {
+ toAppendTo.append(obj.toString());
+ }
+
+ if (obj instanceof Date) {
+ toAppendTo.append(Long.toString(((Date) obj).getTime()));
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/LevelPatternConverter.java b/java/src/org/apache/log4j/pattern/LevelPatternConverter.java
new file mode 100644
index 0000000..3dbc6e6
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/LevelPatternConverter.java
@@ -0,0 +1,98 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Return the event's level in a StringBuffer.
+ *
+ * @author Ceki Gülcü
+ */
+public final class LevelPatternConverter extends LoggingEventPatternConverter {
+
+ /**
+ * Integer severity for Level.TRACE.
+ */
+ private static final int TRACE_INT = 5000;
+ /**
+ * Singleton.
+ */
+ private static final LevelPatternConverter INSTANCE =
+ new LevelPatternConverter();
+
+ /**
+ * Private constructor.
+ */
+ private LevelPatternConverter() {
+ super("Level", "level");
+ }
+
+ /**
+ * Obtains an instance of pattern converter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static LevelPatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer output) {
+ output.append(event.getLevel().toString());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getStyleClass(Object e) {
+ if (e instanceof LoggingEvent) {
+ int lint = ((LoggingEvent) e).getLevel().toInt();
+
+ switch (lint) {
+ case TRACE_INT:
+ return "level trace";
+
+ case Level.DEBUG_INT:
+ return "level debug";
+
+ case Level.INFO_INT:
+ return "level info";
+
+ case Level.WARN_INT:
+ return "level warn";
+
+ case Level.ERROR_INT:
+ return "level error";
+
+ case Level.FATAL_INT:
+ return "level fatal";
+
+ default:
+ return "level " + ((LoggingEvent) e).getLevel().toString();
+ }
+ }
+
+ return "level";
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/LineLocationPatternConverter.java b/java/src/org/apache/log4j/pattern/LineLocationPatternConverter.java
new file mode 100644
index 0000000..0a9dfd3
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/LineLocationPatternConverter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Return the event's line location information in a StringBuffer.
+ *
+ * @author Ceki Gülcü
+ */
+public final class LineLocationPatternConverter
+ extends LoggingEventPatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final LineLocationPatternConverter INSTANCE =
+ new LineLocationPatternConverter();
+
+ /**
+ * Private constructor.
+ */
+ private LineLocationPatternConverter() {
+ super("Line", "line");
+ }
+
+ /**
+ * Obtains an instance of pattern converter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static LineLocationPatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer output) {
+ LocationInfo locationInfo = event.getLocationInformation();
+
+ if (locationInfo != null) {
+ output.append(locationInfo.getLineNumber());
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/LineSeparatorPatternConverter.java b/java/src/org/apache/log4j/pattern/LineSeparatorPatternConverter.java
new file mode 100644
index 0000000..a859870
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/LineSeparatorPatternConverter.java
@@ -0,0 +1,73 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Formats a line separator.
+ *
+ * @author Ceki Gülcü
+ */
+public final class LineSeparatorPatternConverter
+ extends LoggingEventPatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final LineSeparatorPatternConverter INSTANCE =
+ new LineSeparatorPatternConverter();
+
+ /**
+ * Line separator.
+ */
+ private final String lineSep;
+
+ /**
+ * Private constructor.
+ */
+ private LineSeparatorPatternConverter() {
+ super("Line Sep", "lineSep");
+ lineSep = Layout.LINE_SEP;
+ }
+
+ /**
+ * Obtains an instance of pattern converter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static LineSeparatorPatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(LoggingEvent event, final StringBuffer toAppendTo) {
+ toAppendTo.append(lineSep);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final Object obj, final StringBuffer toAppendTo) {
+ toAppendTo.append(lineSep);
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/LiteralPatternConverter.java b/java/src/org/apache/log4j/pattern/LiteralPatternConverter.java
new file mode 100644
index 0000000..b88b6f7
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/LiteralPatternConverter.java
@@ -0,0 +1,57 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Formats a string literal.
+ *
+ * @author Curt Arnold
+ *
+ */
+public final class LiteralPatternConverter extends LoggingEventPatternConverter {
+ /**
+ * String literal.
+ */
+ private final String literal;
+
+ /**
+ * Create a new instance.
+ * @param literal string literal.
+ */
+ public LiteralPatternConverter(final String literal) {
+ super("Literal", "literal");
+ this.literal = literal;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ toAppendTo.append(literal);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final Object obj, final StringBuffer toAppendTo) {
+ toAppendTo.append(literal);
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/LogEvent.java b/java/src/org/apache/log4j/pattern/LogEvent.java
new file mode 100644
index 0000000..24518ec
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/LogEvent.java
@@ -0,0 +1,600 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.MDC;
+import org.apache.log4j.NDC;
+import org.apache.log4j.Priority;
+import org.apache.log4j.helpers.Loader;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableInformation;
+
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+// Contributors: Nelson Minar
+// Wolf Siberski
+// Anders Kristensen
+
+/**
+ * This class is a copy of o.a.l.spi.LoggingEvent from
+ * log4j 1.2.15 which has been renamed to keep
+ * the same overall class name length to allow a
+ * serialization written with a prior instance of o.a.l.spi.LoggingEvent
+ * to be deserialized with this class just by mangling the class name
+ * in the byte stream.
+ *
+ */
+public class LogEvent implements java.io.Serializable {
+
+ private static long startTime = System.currentTimeMillis();
+
+ /** Fully qualified name of the calling category class. */
+ transient public final String fqnOfCategoryClass;
+
+ /**
+ * The category of the logging event. This field is not serialized
+ * for performance reasons.
+ *
+ * It is set by the LoggingEvent constructor or set by a remote
+ * entity after deserialization.
+ *
+ * @deprecated This field will be marked as private or be completely
+ * removed in future releases. Please do not use it.
+ * */
+ transient private Category logger;
+
+ /**
+ *
The category (logger) name.
+ *
+ * @deprecated This field will be marked as private in future
+ * releases. Please do not access it directly. Use the {@link
+ * #getLoggerName} method instead.
+
+ * */
+ final public String categoryName;
+
+ /**
+ * Level of logging event. Level cannot be serializable because it
+ * is a flyweight. Due to its special seralization it cannot be
+ * declared final either.
+ *
+ *
This field should not be accessed directly. You shoud use the
+ * {@link #getLevel} method instead.
+ *
+ * @deprecated This field will be marked as private in future
+ * releases. Please do not access it directly. Use the {@link
+ * #getLevel} method instead.
+ * */
+ transient public Priority level;
+
+ /** The nested diagnostic context (NDC) of logging event. */
+ private String ndc;
+
+ /** The mapped diagnostic context (MDC) of logging event. */
+ private Hashtable mdcCopy;
+
+
+ /** Have we tried to do an NDC lookup? If we did, there is no need
+ * to do it again. Note that its value is always false when
+ * serialized. Thus, a receiving SocketNode will never use it's own
+ * (incorrect) NDC. See also writeObject method. */
+ private boolean ndcLookupRequired = true;
+
+
+ /** Have we tried to do an MDC lookup? If we did, there is no need
+ * to do it again. Note that its value is always false when
+ * serialized. See also the getMDC and getMDCCopy methods. */
+ private boolean mdcCopyLookupRequired = true;
+
+ /** The application supplied message of logging event. */
+ transient private Object message;
+
+ /** The application supplied message rendered through the log4j
+ objet rendering mechanism.*/
+ private String renderedMessage;
+
+ /** The name of thread in which this logging event was generated. */
+ private String threadName;
+
+
+ /** This
+ variable contains information about this event's throwable
+ */
+ private ThrowableInformation throwableInfo;
+
+ /** The number of milliseconds elapsed from 1/1/1970 until logging event
+ was created. */
+ public final long timeStamp;
+ /** Location information for the caller. */
+ private LocationInfo locationInfo;
+
+ // Serialization
+ static final long serialVersionUID = -868428216207166145L;
+
+ static final Integer[] PARAM_ARRAY = new Integer[1];
+ static final String TO_LEVEL = "toLevel";
+ static final Class[] TO_LEVEL_PARAMS = new Class[] {int.class};
+ static final Hashtable methodCache = new Hashtable(3); // use a tiny table
+
+ /**
+ Instantiate a LoggingEvent from the supplied parameters.
+
+
Except {@link #timeStamp} all the other fields of
+ LoggingEvent are filled when actually needed.
+
+ @param logger The logger generating this event.
+ @param level The level of this event.
+ @param message The message of this event.
+ @param throwable The throwable of this event. */
+ public LogEvent(String fqnOfCategoryClass, Category logger,
+ Priority level, Object message, Throwable throwable) {
+ this.fqnOfCategoryClass = fqnOfCategoryClass;
+ this.logger = logger;
+ this.categoryName = logger.getName();
+ this.level = level;
+ this.message = message;
+ if(throwable != null) {
+ this.throwableInfo = new ThrowableInformation(throwable);
+ }
+ timeStamp = System.currentTimeMillis();
+ }
+
+ /**
+ Instantiate a LoggingEvent from the supplied parameters.
+
+
Except {@link #timeStamp} all the other fields of
+ LoggingEvent are filled when actually needed.
+
+ @param logger The logger generating this event.
+ @param timeStamp the timestamp of this logging event
+ @param level The level of this event.
+ @param message The message of this event.
+ @param throwable The throwable of this event. */
+ public LogEvent(String fqnOfCategoryClass, Category logger,
+ long timeStamp, Priority level, Object message,
+ Throwable throwable) {
+ this.fqnOfCategoryClass = fqnOfCategoryClass;
+ this.logger = logger;
+ this.categoryName = logger.getName();
+ this.level = level;
+ this.message = message;
+ if(throwable != null) {
+ this.throwableInfo = new ThrowableInformation(throwable);
+ }
+
+ this.timeStamp = timeStamp;
+ }
+
+ /**
+ Create new instance.
+ @since 1.2.15
+ @param fqnOfCategoryClass Fully qualified class name
+ of Logger implementation.
+ @param logger The logger generating this event.
+ @param timeStamp the timestamp of this logging event
+ @param level The level of this event.
+ @param message The message of this event.
+ @param threadName thread name
+ @param throwable The throwable of this event.
+ @param ndc Nested diagnostic context
+ @param info Location info
+ @param properties MDC properties
+ */
+ public LogEvent(final String fqnOfCategoryClass,
+ final Logger logger,
+ final long timeStamp,
+ final Level level,
+ final Object message,
+ final String threadName,
+ final ThrowableInformation throwable,
+ final String ndc,
+ final LocationInfo info,
+ final java.util.Map properties) {
+ super();
+ this.fqnOfCategoryClass = fqnOfCategoryClass;
+ this.logger = logger;
+ if (logger != null) {
+ categoryName = logger.getName();
+ } else {
+ categoryName = null;
+ }
+ this.level = level;
+ this.message = message;
+ if(throwable != null) {
+ this.throwableInfo = throwable;
+ }
+
+ this.timeStamp = timeStamp;
+ this.threadName = threadName;
+ ndcLookupRequired = false;
+ this.ndc = ndc;
+ this.locationInfo = info;
+ mdcCopyLookupRequired = false;
+ if (properties != null) {
+ mdcCopy = new java.util.Hashtable(properties);
+ }
+ }
+
+ /**
+ Set the location information for this logging event. The collected
+ information is cached for future use.
+ */
+ public LocationInfo getLocationInformation() {
+ if(locationInfo == null) {
+ locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass);
+ }
+ return locationInfo;
+ }
+
+ /**
+ * Return the level of this event. Use this form instead of directly
+ * accessing the level field. */
+ public Level getLevel() {
+ return (Level) level;
+ }
+
+ /**
+ * Return the name of the logger. Use this form instead of directly
+ * accessing the categoryName field.
+ */
+ public String getLoggerName() {
+ return categoryName;
+ }
+
+ /**
+ Return the message for this logging event.
+
+
Before serialization, the returned object is the message
+ passed by the user to generate the logging event. After
+ serialization, the returned value equals the String form of the
+ message possibly after object rendering.
+
+ @since 1.1 */
+ public
+ Object getMessage() {
+ if(message != null) {
+ return message;
+ } else {
+ return getRenderedMessage();
+ }
+ }
+
+ /**
+ * This method returns the NDC for this event. It will return the
+ * correct content even if the event was generated in a different
+ * thread or even on a different machine. The {@link NDC#get} method
+ * should never be called directly. */
+ public
+ String getNDC() {
+ if(ndcLookupRequired) {
+ ndcLookupRequired = false;
+ ndc = NDC.get();
+ }
+ return ndc;
+ }
+
+
+ /**
+ Returns the the context corresponding to the key
+ parameter. If there is a local MDC copy, possibly because we are
+ in a logging server or running inside AsyncAppender, then we
+ search for the key in MDC copy, if a value is found it is
+ returned. Otherwise, if the search in MDC copy returns a null
+ result, then the current thread's MDC is used.
+
+
Note that both the local MDC copy and the current
+ thread's MDC are searched.
+
+ */
+ public
+ Object getMDC(String key) {
+ Object r;
+ // Note the mdcCopy is used if it exists. Otherwise we use the MDC
+ // that is associated with the thread.
+ if(mdcCopy != null) {
+ r = mdcCopy.get(key);
+ if(r != null) {
+ return r;
+ }
+ }
+ return MDC.get(key);
+ }
+
+ /**
+ Obtain a copy of this thread's MDC prior to serialization or
+ asynchronous logging.
+ */
+ public
+ void getMDCCopy() {
+ if(mdcCopyLookupRequired) {
+ mdcCopyLookupRequired = false;
+ // the clone call is required for asynchronous logging.
+ // See also bug #5932.
+ Hashtable t = (Hashtable) MDC.getContext();
+ if(t != null) {
+ mdcCopy = (Hashtable) t.clone();
+ }
+ }
+ }
+
+ public
+ String getRenderedMessage() {
+ if(renderedMessage == null && message != null) {
+ if(message instanceof String)
+ renderedMessage = (String) message;
+ else {
+ LoggerRepository repository = logger.getLoggerRepository();
+
+ if(repository instanceof RendererSupport) {
+ RendererSupport rs = (RendererSupport) repository;
+ renderedMessage= rs.getRendererMap().findAndRender(message);
+ } else {
+ renderedMessage = message.toString();
+ }
+ }
+ }
+ return renderedMessage;
+ }
+
+ /**
+ Returns the time when the application started, in milliseconds
+ elapsed since 01.01.1970. */
+ public static long getStartTime() {
+ return startTime;
+ }
+
+ public
+ String getThreadName() {
+ if(threadName == null)
+ threadName = (Thread.currentThread()).getName();
+ return threadName;
+ }
+
+ /**
+ Returns the throwable information contained within this
+ event. May be null if there is no such information.
+
+
Note that the {@link Throwable} object contained within a
+ {@link ThrowableInformation} does not survive serialization.
+
+ @since 1.1 */
+ public
+ ThrowableInformation getThrowableInformation() {
+ return throwableInfo;
+ }
+
+ /**
+ Return this event's throwable's string[] representaion.
+ */
+ public
+ String[] getThrowableStrRep() {
+
+ if(throwableInfo == null)
+ return null;
+ else
+ return throwableInfo.getThrowableStrRep();
+ }
+
+
+ private
+ void readLevel(ObjectInputStream ois)
+ throws java.io.IOException, ClassNotFoundException {
+
+ int p = ois.readInt();
+ try {
+ String className = (String) ois.readObject();
+ if(className == null) {
+ level = Level.toLevel(p);
+ } else {
+ Method m = (Method) methodCache.get(className);
+ if(m == null) {
+ Class clazz = Loader.loadClass(className);
+ // Note that we use Class.getDeclaredMethod instead of
+ // Class.getMethod. This assumes that the Level subclass
+ // implements the toLevel(int) method which is a
+ // requirement. Actually, it does not make sense for Level
+ // subclasses NOT to implement this method. Also note that
+ // only Level can be subclassed and not Priority.
+ m = clazz.getDeclaredMethod(TO_LEVEL, TO_LEVEL_PARAMS);
+ methodCache.put(className, m);
+ }
+ PARAM_ARRAY[0] = new Integer(p);
+ level = (Level) m.invoke(null, PARAM_ARRAY);
+ }
+ } catch(Exception e) {
+ LogLog.warn("Level deserialization failed, reverting to default.", e);
+ level = Level.toLevel(p);
+ }
+ }
+
+ private void readObject(ObjectInputStream ois)
+ throws java.io.IOException, ClassNotFoundException {
+ ois.defaultReadObject();
+ readLevel(ois);
+
+ // Make sure that no location info is available to Layouts
+ if(locationInfo == null)
+ locationInfo = new LocationInfo(null, null);
+ }
+
+ private
+ void writeObject(ObjectOutputStream oos) throws java.io.IOException {
+ // Aside from returning the current thread name the wgetThreadName
+ // method sets the threadName variable.
+ this.getThreadName();
+
+ // This sets the renders the message in case it wasn't up to now.
+ this.getRenderedMessage();
+
+ // This call has a side effect of setting this.ndc and
+ // setting ndcLookupRequired to false if not already false.
+ this.getNDC();
+
+ // This call has a side effect of setting this.mdcCopy and
+ // setting mdcLookupRequired to false if not already false.
+ this.getMDCCopy();
+
+ // This sets the throwable sting representation of the event throwable.
+ this.getThrowableStrRep();
+
+ oos.defaultWriteObject();
+
+ // serialize this event's level
+ writeLevel(oos);
+ }
+
+ private
+ void writeLevel(ObjectOutputStream oos) throws java.io.IOException {
+
+ oos.writeInt(level.toInt());
+
+ Class clazz = level.getClass();
+ if(clazz == Level.class) {
+ oos.writeObject(null);
+ } else {
+ // writing directly the Class object would be nicer, except that
+ // serialized a Class object can not be read back by JDK
+ // 1.1.x. We have to resort to this hack instead.
+ oos.writeObject(clazz.getName());
+ }
+ }
+
+ /**
+ * Set value for MDC property.
+ * This adds the specified MDC property to the event.
+ * Access to the MDC is not synchronized, so this
+ * method should only be called when it is known that
+ * no other threads are accessing the MDC.
+ * @since 1.2.15
+ * @param propName
+ * @param propValue
+ */
+ public final void setProperty(final String propName,
+ final String propValue) {
+ if (mdcCopy == null) {
+ getMDCCopy();
+ }
+ if (mdcCopy == null) {
+ mdcCopy = new Hashtable();
+ }
+ mdcCopy.put(propName, propValue);
+ }
+
+ /**
+ * Return a property for this event. The return value can be null.
+ *
+ * Equivalent to getMDC(String) in log4j 1.2. Provided
+ * for compatibility with log4j 1.3.
+ *
+ * @param key property name
+ * @return property value or null if property not set
+ * @since 1.2.15
+ */
+ public final String getProperty(final String key) {
+ Object value = getMDC(key);
+ String retval = null;
+ if (value != null) {
+ retval = value.toString();
+ }
+ return retval;
+ }
+
+ /**
+ * Check for the existence of location information without creating it
+ * (a byproduct of calling getLocationInformation).
+ * @return true if location information has been extracted.
+ * @since 1.2.15
+ */
+ public final boolean locationInformationExists() {
+ return (locationInfo != null);
+ }
+
+ /**
+ * Getter for the event's time stamp. The time stamp is calculated starting
+ * from 1970-01-01 GMT.
+ * @return timestamp
+ *
+ * @since 1.2.15
+ */
+ public final long getTimeStamp() {
+ return timeStamp;
+ }
+
+ /**
+ * Returns the set of the key values in the properties
+ * for the event.
+ *
+ * The returned set is unmodifiable by the caller.
+ *
+ * Provided for compatibility with log4j 1.3
+ *
+ * @return Set an unmodifiable set of the property keys.
+ * @since 1.2.15
+ */
+ public Set getPropertyKeySet() {
+ return getProperties().keySet();
+ }
+
+ /**
+ * Returns the set of properties
+ * for the event.
+ *
+ * The returned set is unmodifiable by the caller.
+ *
+ * Provided for compatibility with log4j 1.3
+ *
+ * @return Set an unmodifiable map of the properties.
+ * @since 1.2.15
+ */
+ public Map getProperties() {
+ getMDCCopy();
+ Map properties;
+ if (mdcCopy == null) {
+ properties = new HashMap();
+ } else {
+ properties = mdcCopy;
+ }
+ return Collections.unmodifiableMap(properties);
+ }
+
+ /**
+ * Get the fully qualified name of the calling logger sub-class/wrapper.
+ * Provided for compatibility with log4j 1.3
+ * @return fully qualified class name, may be null.
+ * @since 1.2.15
+ */
+ public String getFQNOfLoggerClass() {
+ return fqnOfCategoryClass;
+ }
+
+
+
+}
diff --git a/java/src/org/apache/log4j/pattern/LoggerPatternConverter.java b/java/src/org/apache/log4j/pattern/LoggerPatternConverter.java
new file mode 100644
index 0000000..7052a4e
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/LoggerPatternConverter.java
@@ -0,0 +1,66 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Formats a logger name.
+ *
+ * @author Ceki Gülcü
+ *
+ */
+public final class LoggerPatternConverter extends NamePatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final LoggerPatternConverter INSTANCE =
+ new LoggerPatternConverter(null);
+
+ /**
+ * Private constructor.
+ * @param options options, may be null.
+ */
+ private LoggerPatternConverter(final String[] options) {
+ super("Logger", "logger", options);
+ }
+
+ /**
+ * Obtains an instance of pattern converter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static LoggerPatternConverter newInstance(
+ final String[] options) {
+ if ((options == null) || (options.length == 0)) {
+ return INSTANCE;
+ }
+
+ return new LoggerPatternConverter(options);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ final int initialLength = toAppendTo.length();
+ toAppendTo.append(event.getLoggerName());
+ abbreviate(initialLength, toAppendTo);
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/LoggingEventPatternConverter.java b/java/src/org/apache/log4j/pattern/LoggingEventPatternConverter.java
new file mode 100644
index 0000000..d0fd4f4
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/LoggingEventPatternConverter.java
@@ -0,0 +1,70 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * LoggingEventPatternConverter is a base class for pattern converters
+ * that can format information from instances of LoggingEvent.
+ *
+ * @author Curt Arnold
+ *
+ */
+public abstract class LoggingEventPatternConverter extends PatternConverter {
+ /**
+ * Constructs an instance of LoggingEventPatternConverter.
+ * @param name name of converter.
+ * @param style CSS style for output.
+ */
+ protected LoggingEventPatternConverter(
+ final String name, final String style) {
+ super(name, style);
+ }
+
+ /**
+ * Formats an event into a string buffer.
+ * @param event event to format, may not be null.
+ * @param toAppendTo string buffer to which the formatted event will be appended. May not be null.
+ */
+ public abstract void format(
+ final LoggingEvent event, final StringBuffer toAppendTo);
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final Object obj, final StringBuffer output) {
+ if (obj instanceof LoggingEvent) {
+ format((LoggingEvent) obj, output);
+ }
+ }
+
+ /**
+ * Normally pattern converters are not meant to handle Exceptions although
+ * few pattern converters might.
+ *
+ * By examining the return values for this method, the containing layout will
+ * determine whether it handles throwables or not.
+
+ * @return true if this PatternConverter handles throwables
+ */
+ public boolean handlesThrowable() {
+ return false;
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/MessagePatternConverter.java b/java/src/org/apache/log4j/pattern/MessagePatternConverter.java
new file mode 100644
index 0000000..c29f64a
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/MessagePatternConverter.java
@@ -0,0 +1,58 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Return the event's rendered message in a StringBuffer.
+ *
+ * @author Ceki Gülcü
+ */
+public final class MessagePatternConverter extends LoggingEventPatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final MessagePatternConverter INSTANCE =
+ new MessagePatternConverter();
+
+ /**
+ * Private constructor.
+ */
+ private MessagePatternConverter() {
+ super("Message", "message");
+ }
+
+ /**
+ * Obtains an instance of pattern converter.
+ * @param options options, may be null.
+ * @return instance of pattern converter.
+ */
+ public static MessagePatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ toAppendTo.append(event.getRenderedMessage());
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/MethodLocationPatternConverter.java b/java/src/org/apache/log4j/pattern/MethodLocationPatternConverter.java
new file mode 100644
index 0000000..4d1b533
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/MethodLocationPatternConverter.java
@@ -0,0 +1,64 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Return the event's line location information in a StringBuffer.
+ *
+ * @author Ceki Gülcü
+ */
+public final class MethodLocationPatternConverter
+ extends LoggingEventPatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final MethodLocationPatternConverter INSTANCE =
+ new MethodLocationPatternConverter();
+
+ /**
+ * Private constructor.
+ */
+ private MethodLocationPatternConverter() {
+ super("Method", "method");
+ }
+
+ /**
+ * Obtains an instance of MethodLocationPatternConverter.
+ * @param options options, may be null.
+ * @return instance of MethodLocationPatternConverter.
+ */
+ public static MethodLocationPatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ LocationInfo locationInfo = event.getLocationInformation();
+
+ if (locationInfo != null) {
+ toAppendTo.append(locationInfo.getMethodName());
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/NDCPatternConverter.java b/java/src/org/apache/log4j/pattern/NDCPatternConverter.java
new file mode 100644
index 0000000..9788cb1
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/NDCPatternConverter.java
@@ -0,0 +1,58 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Return the event's NDC in a StringBuffer.
+ *
+ * @author Ceki Gülcü
+ */
+public final class NDCPatternConverter extends LoggingEventPatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final NDCPatternConverter INSTANCE =
+ new NDCPatternConverter();
+
+ /**
+ * Private constructor.
+ */
+ private NDCPatternConverter() {
+ super("NDC", "ndc");
+ }
+
+ /**
+ * Obtains an instance of NDCPatternConverter.
+ * @param options options, may be null.
+ * @return instance of NDCPatternConverter.
+ */
+ public static NDCPatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ toAppendTo.append(event.getNDC());
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/NameAbbreviator.java b/java/src/org/apache/log4j/pattern/NameAbbreviator.java
new file mode 100644
index 0000000..b7e89a0
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/NameAbbreviator.java
@@ -0,0 +1,350 @@
+/*
+ * 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.log4j.pattern;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+/**
+ * NameAbbreviator generates abbreviated logger and class names.
+ *
+ */
+public abstract class NameAbbreviator {
+ /**
+ * Default (no abbreviation) abbreviator.
+ */
+ private static final NameAbbreviator DEFAULT = new NOPAbbreviator();
+
+ /**
+ * Gets an abbreviator.
+ *
+ * For example, "%logger{2}" will output only 2 elements of the logger name,
+ * %logger{-2} will drop 2 elements from the logger name,
+ * "%logger{1.}" will output only the first character of the non-final elements in the name,
+ * "%logger{1~.2~} will output the first character of the first element, two characters of
+ * the second and subsequent elements and will use a tilde to indicate abbreviated characters.
+ *
+ * @param pattern abbreviation pattern.
+ * @return abbreviator, will not be null.
+ */
+ public static NameAbbreviator getAbbreviator(final String pattern) {
+ if (pattern.length() > 0) {
+ // if pattern is just spaces and numbers then
+ // use MaxElementAbbreviator
+ String trimmed = pattern.trim();
+
+ if (trimmed.length() == 0) {
+ return DEFAULT;
+ }
+
+ int i = 0;
+ if (trimmed.length() > 0) {
+ if (trimmed.charAt(0) == '-') {
+ i++;
+ }
+ for (;
+ (i < trimmed.length()) &&
+ (trimmed.charAt(i) >= '0') &&
+ (trimmed.charAt(i) <= '9');
+ i++) {
+ }
+ }
+
+
+ //
+ // if all blanks and digits
+ //
+ if (i == trimmed.length()) {
+ int elements = Integer.parseInt(trimmed);
+ if (elements >= 0) {
+ return new MaxElementAbbreviator(elements);
+ } else {
+ return new DropElementAbbreviator(-elements);
+ }
+ }
+
+ ArrayList fragments = new ArrayList(5);
+ char ellipsis;
+ int charCount;
+ int pos = 0;
+
+ while ((pos < trimmed.length()) && (pos >= 0)) {
+ int ellipsisPos = pos;
+
+ if (trimmed.charAt(pos) == '*') {
+ charCount = Integer.MAX_VALUE;
+ ellipsisPos++;
+ } else {
+ if ((trimmed.charAt(pos) >= '0') && (trimmed.charAt(pos) <= '9')) {
+ charCount = trimmed.charAt(pos) - '0';
+ ellipsisPos++;
+ } else {
+ charCount = 0;
+ }
+ }
+
+ ellipsis = '\0';
+
+ if (ellipsisPos < trimmed.length()) {
+ ellipsis = trimmed.charAt(ellipsisPos);
+
+ if (ellipsis == '.') {
+ ellipsis = '\0';
+ }
+ }
+
+ fragments.add(new PatternAbbreviatorFragment(charCount, ellipsis));
+ pos = trimmed.indexOf(".", pos);
+
+ if (pos == -1) {
+ break;
+ }
+
+ pos++;
+ }
+
+ return new PatternAbbreviator(fragments);
+ }
+
+ //
+ // no matching abbreviation, return defaultAbbreviator
+ //
+ return DEFAULT;
+ }
+
+ /**
+ * Gets default abbreviator.
+ *
+ * @return default abbreviator.
+ */
+ public static NameAbbreviator getDefaultAbbreviator() {
+ return DEFAULT;
+ }
+
+ /**
+ * Abbreviates a name in a StringBuffer.
+ *
+ * @param nameStart starting position of name in buf.
+ * @param buf buffer, may not be null.
+ */
+ public abstract void abbreviate(final int nameStart, final StringBuffer buf);
+
+ /**
+ * Abbreviator that simply appends full name to buffer.
+ */
+ private static class NOPAbbreviator extends NameAbbreviator {
+ /**
+ * Constructor.
+ */
+ public NOPAbbreviator() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ }
+ }
+
+ /**
+ * Abbreviator that drops starting path elements.
+ */
+ private static class MaxElementAbbreviator extends NameAbbreviator {
+ /**
+ * Maximum number of path elements to output.
+ */
+ private final int count;
+
+ /**
+ * Create new instance.
+ * @param count maximum number of path elements to output.
+ */
+ public MaxElementAbbreviator(final int count) {
+ this.count = count;
+ }
+
+ /**
+ * Abbreviate name.
+ * @param buf buffer to append abbreviation.
+ * @param nameStart start of name to abbreviate.
+ */
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ // We substract 1 from 'len' when assigning to 'end' to avoid out of
+ // bounds exception in return r.substring(end+1, len). This can happen if
+ // precision is 1 and the category name ends with a dot.
+ int end = buf.length() - 1;
+
+ String bufString = buf.toString();
+ for (int i = count; i > 0; i--) {
+ end = bufString.lastIndexOf(".", end - 1);
+
+ if ((end == -1) || (end < nameStart)) {
+ return;
+ }
+ }
+
+ buf.delete(nameStart, end + 1);
+ }
+ }
+
+ /**
+ * Abbreviator that drops starting path elements.
+ */
+ private static class DropElementAbbreviator extends NameAbbreviator {
+ /**
+ * Maximum number of path elements to output.
+ */
+ private final int count;
+
+ /**
+ * Create new instance.
+ * @param count maximum number of path elements to output.
+ */
+ public DropElementAbbreviator(final int count) {
+ this.count = count;
+ }
+
+ /**
+ * Abbreviate name.
+ * @param buf buffer to append abbreviation.
+ * @param nameStart start of name to abbreviate.
+ */
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ int i = count;
+ for(int pos = buf.indexOf(".", nameStart);
+ pos != -1;
+ pos = buf.indexOf(".", pos + 1)) {
+ if(--i == 0) {
+ buf.delete(nameStart, pos + 1);
+ break;
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Fragment of an pattern abbreviator.
+ *
+ */
+ private static class PatternAbbreviatorFragment {
+ /**
+ * Count of initial characters of element to output.
+ */
+ private final int charCount;
+
+ /**
+ * Character used to represent dropped characters.
+ * '\0' indicates no representation of dropped characters.
+ */
+ private final char ellipsis;
+
+ /**
+ * Creates a PatternAbbreviatorFragment.
+ * @param charCount number of initial characters to preserve.
+ * @param ellipsis character to represent elimination of characters,
+ * '\0' if no ellipsis is desired.
+ */
+ public PatternAbbreviatorFragment(
+ final int charCount, final char ellipsis) {
+ this.charCount = charCount;
+ this.ellipsis = ellipsis;
+ }
+
+ /**
+ * Abbreviate element of name.
+ * @param buf buffer to receive element.
+ * @param startPos starting index of name element.
+ * @return starting index of next element.
+ */
+ public int abbreviate(final StringBuffer buf, final int startPos) {
+ int nextDot = buf.toString().indexOf(".", startPos);
+
+ if (nextDot != -1) {
+ if ((nextDot - startPos) > charCount) {
+ buf.delete(startPos + charCount, nextDot);
+ nextDot = startPos + charCount;
+
+ if (ellipsis != '\0') {
+ buf.insert(nextDot, ellipsis);
+ nextDot++;
+ }
+ }
+
+ nextDot++;
+ }
+
+ return nextDot;
+ }
+ }
+
+ /**
+ * Pattern abbreviator.
+ *
+ *
+ */
+ private static class PatternAbbreviator extends NameAbbreviator {
+ /**
+ * Element abbreviation patterns.
+ */
+ private final PatternAbbreviatorFragment[] fragments;
+
+ /**
+ * Create PatternAbbreviator.
+ *
+ * @param fragments element abbreviation patterns.
+ */
+ public PatternAbbreviator(List fragments) {
+ if (fragments.size() == 0) {
+ throw new IllegalArgumentException(
+ "fragments must have at least one element");
+ }
+
+ this.fragments = new PatternAbbreviatorFragment[fragments.size()];
+ fragments.toArray(this.fragments);
+ }
+
+ /**
+ * Abbreviate name.
+ * @param buf buffer that abbreviated name is appended.
+ * @param nameStart start of name.
+ */
+ public void abbreviate(final int nameStart, final StringBuffer buf) {
+ //
+ // all non-terminal patterns are executed once
+ //
+ int pos = nameStart;
+
+ for (int i = 0; (i < (fragments.length - 1)) && (pos < buf.length());
+ i++) {
+ pos = fragments[i].abbreviate(buf, pos);
+ }
+
+ //
+ // last pattern in executed repeatedly
+ //
+ PatternAbbreviatorFragment terminalFragment =
+ fragments[fragments.length - 1];
+
+ while ((pos < buf.length()) && (pos >= 0)) {
+ pos = terminalFragment.abbreviate(buf, pos);
+ }
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/NamePatternConverter.java b/java/src/org/apache/log4j/pattern/NamePatternConverter.java
new file mode 100644
index 0000000..fbdd999
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/NamePatternConverter.java
@@ -0,0 +1,61 @@
+/*
+ * 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.log4j.pattern;
+
+
+/**
+ *
+ * Base class for other pattern converters which can return only parts of their name.
+ *
+ * @author Ceki Gülcü
+ * @author Curt Arnold
+ *
+ */
+public abstract class NamePatternConverter
+ extends LoggingEventPatternConverter {
+ /**
+ * Abbreviator.
+ */
+ private final NameAbbreviator abbreviator;
+
+ /**
+ * Constructor.
+ * @param name name of converter.
+ * @param style style name for associated output.
+ * @param options options, may be null, first element will be interpreted as an abbreviation pattern.
+ */
+ protected NamePatternConverter(
+ final String name, final String style, final String[] options) {
+ super(name, style);
+
+ if ((options != null) && (options.length > 0)) {
+ abbreviator = NameAbbreviator.getAbbreviator(options[0]);
+ } else {
+ abbreviator = NameAbbreviator.getDefaultAbbreviator();
+ }
+ }
+
+ /**
+ * Abbreviate name in string buffer.
+ * @param nameStart starting position of name to abbreviate.
+ * @param buf string buffer containing name.
+ */
+ protected final void abbreviate(final int nameStart, final StringBuffer buf) {
+ abbreviator.abbreviate(nameStart, buf);
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/PatternConverter.java b/java/src/org/apache/log4j/pattern/PatternConverter.java
new file mode 100644
index 0000000..21fb7cd
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/PatternConverter.java
@@ -0,0 +1,87 @@
+/*
+ * 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.log4j.pattern;
+
+
+/**
+
+
PatternConverter is an abstract class that provides the
+ formatting functionality that derived classes need.
+
+
Conversion specifiers in a conversion patterns are parsed to
+ individual PatternConverters. Each of which is responsible for
+ converting an object in a converter specific manner.
+
+ @author James P. Cakalic
+ @author Ceki Gülcü
+ @author Chris Nokes
+ @author Curt Arnold
+
+ */
+public abstract class PatternConverter {
+ /**
+ * Converter name.
+ */
+ private final String name;
+
+ /**
+ * Converter style name.
+ */
+ private final String style;
+
+ /**
+ * Create a new pattern converter.
+ * @param name name for pattern converter.
+ * @param style CSS style for formatted output.
+ */
+ protected PatternConverter(final String name, final String style) {
+ this.name = name;
+ this.style = style;
+ }
+
+ /**
+ * Formats an object into a string buffer.
+ * @param obj event to format, may not be null.
+ * @param toAppendTo string buffer to which the formatted event will be appended. May not be null.
+ */
+ public abstract void format(final Object obj, final StringBuffer toAppendTo);
+
+ /**
+ * This method returns the name of the conversion pattern.
+ *
+ * The name can be useful to certain Layouts such as HTMLLayout.
+ *
+ * @return the name of the conversion pattern
+ */
+ public final String getName() {
+ return name;
+ }
+
+ /**
+ * This method returns the CSS style class that should be applied to
+ * the LoggingEvent passed as parameter, which can be null.
+ *
+ * This information is currently used only by HTMLLayout.
+ *
+ * @param e null values are accepted
+ * @return the name of the conversion pattern
+ */
+ public String getStyleClass(Object e) {
+ return style;
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/PatternParser.java b/java/src/org/apache/log4j/pattern/PatternParser.java
new file mode 100644
index 0000000..5d19387
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/PatternParser.java
@@ -0,0 +1,683 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.helpers.Loader;
+import org.apache.log4j.helpers.LogLog;
+
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+// Contributors: Nelson Minar <(nelson@monkey.org>
+// Igor E. Poteryaev
+// Reinhard Deschler
+
+/**
+ * Most of the work of the {@link org.apache.log4j.EnhancedPatternLayout} class
+ * is delegated to the PatternParser class.
+ * It is this class that parses conversion patterns and creates
+ * a chained list of {@link PatternConverter PatternConverters}.
+ *
+ * @author James P. Cakalic
+ * @author Ceki Gülcü
+ * @author Anders Kristensen
+ * @author Paul Smith
+ * @author Curt Arnold
+ *
+*/
+public final class PatternParser {
+ /**
+ * Escape character for format specifier.
+ */
+ private static final char ESCAPE_CHAR = '%';
+
+ /**
+ * Literal state.
+ */
+ private static final int LITERAL_STATE = 0;
+
+ /**
+ * In converter name state.
+ */
+ private static final int CONVERTER_STATE = 1;
+
+ /**
+ * Dot state.
+ */
+ private static final int DOT_STATE = 3;
+
+ /**
+ * Min state.
+ */
+ private static final int MIN_STATE = 4;
+
+ /**
+ * Max state.
+ */
+ private static final int MAX_STATE = 5;
+
+ /**
+ * Standard format specifiers for EnhancedPatternLayout.
+ */
+ private static final Map PATTERN_LAYOUT_RULES;
+
+ /**
+ * Standard format specifiers for rolling file appenders.
+ */
+ private static final Map FILENAME_PATTERN_RULES;
+
+ static {
+ // We set the global rules in the static initializer of PatternParser class
+ Map rules = new HashMap(17);
+ rules.put("c", LoggerPatternConverter.class);
+ rules.put("logger", LoggerPatternConverter.class);
+
+ rules.put("C", ClassNamePatternConverter.class);
+ rules.put("class", ClassNamePatternConverter.class);
+
+ rules.put("d", DatePatternConverter.class);
+ rules.put("date", DatePatternConverter.class);
+
+ rules.put("F", FileLocationPatternConverter.class);
+ rules.put("file", FileLocationPatternConverter.class);
+
+ rules.put("l", FullLocationPatternConverter.class);
+
+ rules.put("L", LineLocationPatternConverter.class);
+ rules.put("line", LineLocationPatternConverter.class);
+
+ rules.put("m", MessagePatternConverter.class);
+ rules.put("message", MessagePatternConverter.class);
+
+ rules.put("n", LineSeparatorPatternConverter.class);
+
+ rules.put("M", MethodLocationPatternConverter.class);
+ rules.put("method", MethodLocationPatternConverter.class);
+
+ rules.put("p", LevelPatternConverter.class);
+ rules.put("level", LevelPatternConverter.class);
+
+ rules.put("r", RelativeTimePatternConverter.class);
+ rules.put("relative", RelativeTimePatternConverter.class);
+
+ rules.put("t", ThreadPatternConverter.class);
+ rules.put("thread", ThreadPatternConverter.class);
+
+ rules.put("x", NDCPatternConverter.class);
+ rules.put("ndc", NDCPatternConverter.class);
+
+ rules.put("X", PropertiesPatternConverter.class);
+ rules.put("properties", PropertiesPatternConverter.class);
+
+ rules.put("sn", SequenceNumberPatternConverter.class);
+ rules.put("sequenceNumber", SequenceNumberPatternConverter.class);
+
+ rules.put("throwable", ThrowableInformationPatternConverter.class);
+ PATTERN_LAYOUT_RULES = new ReadOnlyMap(rules);
+
+ Map fnameRules = new HashMap(4);
+ fnameRules.put("d", FileDatePatternConverter.class);
+ fnameRules.put("date", FileDatePatternConverter.class);
+ fnameRules.put("i", IntegerPatternConverter.class);
+ fnameRules.put("index", IntegerPatternConverter.class);
+
+ FILENAME_PATTERN_RULES = new ReadOnlyMap(fnameRules);
+ }
+
+ /**
+ * Private constructor.
+ */
+ private PatternParser() {
+ }
+
+ /**
+ * Get standard format specifiers for EnhancedPatternLayout.
+ * @return read-only map of format converter classes keyed by format specifier strings.
+ */
+ public static Map getPatternLayoutRules() {
+ return PATTERN_LAYOUT_RULES;
+ }
+
+ /**
+ * Get standard format specifiers for rolling file appender file specification.
+ * @return read-only map of format converter classes keyed by format specifier strings.
+ */
+ public static Map getFileNamePatternRules() {
+ return FILENAME_PATTERN_RULES;
+ }
+
+ /** Extract the converter identifier found at position i.
+ *
+ * After this function returns, the variable i will point to the
+ * first char after the end of the converter identifier.
+ *
+ * If i points to a char which is not a character acceptable at the
+ * start of a unicode identifier, the value null is returned.
+ *
+ * @param lastChar last processed character.
+ * @param pattern format string.
+ * @param i current index into pattern format.
+ * @param convBuf buffer to receive conversion specifier.
+ * @param currentLiteral literal to be output in case format specifier in unrecognized.
+ * @return position in pattern after converter.
+ */
+ private static int extractConverter(
+ char lastChar, final String pattern, int i, final StringBuffer convBuf,
+ final StringBuffer currentLiteral) {
+ convBuf.setLength(0);
+
+ // When this method is called, lastChar points to the first character of the
+ // conversion word. For example:
+ // For "%hello" lastChar = 'h'
+ // For "%-5hello" lastChar = 'h'
+ //System.out.println("lastchar is "+lastChar);
+ if (!Character.isUnicodeIdentifierStart(lastChar)) {
+ return i;
+ }
+
+ convBuf.append(lastChar);
+
+ while (
+ (i < pattern.length())
+ && Character.isUnicodeIdentifierPart(pattern.charAt(i))) {
+ convBuf.append(pattern.charAt(i));
+ currentLiteral.append(pattern.charAt(i));
+
+ //System.out.println("conv buffer is now ["+convBuf+"].");
+ i++;
+ }
+
+ return i;
+ }
+
+ /**
+ * Extract options.
+ * @param pattern conversion pattern.
+ * @param i start of options.
+ * @param options array to receive extracted options
+ * @return position in pattern after options.
+ */
+ private static int extractOptions(String pattern, int i, List options) {
+ while ((i < pattern.length()) && (pattern.charAt(i) == '{')) {
+ int end = pattern.indexOf('}', i);
+
+ if (end == -1) {
+ break;
+ }
+
+ String r = pattern.substring(i + 1, end);
+ options.add(r);
+ i = end + 1;
+ }
+
+ return i;
+ }
+
+ /**
+ * Parse a format specifier.
+ * @param pattern pattern to parse.
+ * @param patternConverters list to receive pattern converters.
+ * @param formattingInfos list to receive field specifiers corresponding to pattern converters.
+ * @param converterRegistry map of user-supported pattern converters keyed by format specifier, may be null.
+ * @param rules map of stock pattern converters keyed by format specifier.
+ */
+ public static void parse(
+ final String pattern, final List patternConverters,
+ final List formattingInfos, final Map converterRegistry, final Map rules) {
+ if (pattern == null) {
+ throw new NullPointerException("pattern");
+ }
+
+ StringBuffer currentLiteral = new StringBuffer(32);
+
+ int patternLength = pattern.length();
+ int state = LITERAL_STATE;
+ char c;
+ int i = 0;
+ FormattingInfo formattingInfo = FormattingInfo.getDefault();
+
+ while (i < patternLength) {
+ c = pattern.charAt(i++);
+
+ switch (state) {
+ case LITERAL_STATE:
+
+ // In literal state, the last char is always a literal.
+ if (i == patternLength) {
+ currentLiteral.append(c);
+
+ continue;
+ }
+
+ if (c == ESCAPE_CHAR) {
+ // peek at the next char.
+ switch (pattern.charAt(i)) {
+ case ESCAPE_CHAR:
+ currentLiteral.append(c);
+ i++; // move pointer
+
+ break;
+
+ default:
+
+ if (currentLiteral.length() != 0) {
+ patternConverters.add(
+ new LiteralPatternConverter(currentLiteral.toString()));
+ formattingInfos.add(FormattingInfo.getDefault());
+ }
+
+ currentLiteral.setLength(0);
+ currentLiteral.append(c); // append %
+ state = CONVERTER_STATE;
+ formattingInfo = FormattingInfo.getDefault();
+ }
+ } else {
+ currentLiteral.append(c);
+ }
+
+ break;
+
+ case CONVERTER_STATE:
+ currentLiteral.append(c);
+
+ switch (c) {
+ case '-':
+ formattingInfo =
+ new FormattingInfo(
+ true, formattingInfo.getMinLength(),
+ formattingInfo.getMaxLength());
+
+ break;
+
+ case '.':
+ state = DOT_STATE;
+
+ break;
+
+ default:
+
+ if ((c >= '0') && (c <= '9')) {
+ formattingInfo =
+ new FormattingInfo(
+ formattingInfo.isLeftAligned(), c - '0',
+ formattingInfo.getMaxLength());
+ state = MIN_STATE;
+ } else {
+ i = finalizeConverter(
+ c, pattern, i, currentLiteral, formattingInfo,
+ converterRegistry, rules, patternConverters, formattingInfos);
+
+ // Next pattern is assumed to be a literal.
+ state = LITERAL_STATE;
+ formattingInfo = FormattingInfo.getDefault();
+ currentLiteral.setLength(0);
+ }
+ } // switch
+
+ break;
+
+ case MIN_STATE:
+ currentLiteral.append(c);
+
+ if ((c >= '0') && (c <= '9')) {
+ formattingInfo =
+ new FormattingInfo(
+ formattingInfo.isLeftAligned(),
+ (formattingInfo.getMinLength() * 10) + (c - '0'),
+ formattingInfo.getMaxLength());
+ } else if (c == '.') {
+ state = DOT_STATE;
+ } else {
+ i = finalizeConverter(
+ c, pattern, i, currentLiteral, formattingInfo,
+ converterRegistry, rules, patternConverters, formattingInfos);
+ state = LITERAL_STATE;
+ formattingInfo = FormattingInfo.getDefault();
+ currentLiteral.setLength(0);
+ }
+
+ break;
+
+ case DOT_STATE:
+ currentLiteral.append(c);
+
+ if ((c >= '0') && (c <= '9')) {
+ formattingInfo =
+ new FormattingInfo(
+ formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
+ c - '0');
+ state = MAX_STATE;
+ } else {
+ LogLog.error(
+ "Error occured in position " + i
+ + ".\n Was expecting digit, instead got char \"" + c + "\".");
+
+ state = LITERAL_STATE;
+ }
+
+ break;
+
+ case MAX_STATE:
+ currentLiteral.append(c);
+
+ if ((c >= '0') && (c <= '9')) {
+ formattingInfo =
+ new FormattingInfo(
+ formattingInfo.isLeftAligned(), formattingInfo.getMinLength(),
+ (formattingInfo.getMaxLength() * 10) + (c - '0'));
+ } else {
+ i = finalizeConverter(
+ c, pattern, i, currentLiteral, formattingInfo,
+ converterRegistry, rules, patternConverters, formattingInfos);
+ state = LITERAL_STATE;
+ formattingInfo = FormattingInfo.getDefault();
+ currentLiteral.setLength(0);
+ }
+
+ break;
+ } // switch
+ }
+
+ // while
+ if (currentLiteral.length() != 0) {
+ patternConverters.add(
+ new LiteralPatternConverter(currentLiteral.toString()));
+ formattingInfos.add(FormattingInfo.getDefault());
+ }
+ }
+
+ /**
+ * Creates a new PatternConverter.
+ *
+ *
+ * @param converterId converterId.
+ * @param currentLiteral literal to be used if converter is unrecognized or following converter
+ * if converterId contains extra characters.
+ * @param converterRegistry map of user-supported pattern converters keyed by format specifier, may be null.
+ * @param rules map of stock pattern converters keyed by format specifier.
+ * @param options converter options.
+ * @return converter or null.
+ */
+ private static PatternConverter createConverter(
+ final String converterId, final StringBuffer currentLiteral,
+ final Map converterRegistry, final Map rules, final List options) {
+ String converterName = converterId;
+ Object converterObj = null;
+
+ for (int i = converterId.length(); (i > 0) && (converterObj == null);
+ i--) {
+ converterName = converterName.substring(0, i);
+
+ if (converterRegistry != null) {
+ converterObj = converterRegistry.get(converterName);
+ }
+
+ if ((converterObj == null) && (rules != null)) {
+ converterObj = rules.get(converterName);
+ }
+ }
+
+ if (converterObj == null) {
+ LogLog.error("Unrecognized format specifier [" + converterId + "]");
+
+ return null;
+ }
+
+ Class converterClass = null;
+
+ if (converterObj instanceof Class) {
+ converterClass = (Class) converterObj;
+ } else {
+ if (converterObj instanceof String) {
+ try {
+ converterClass = Loader.loadClass((String) converterObj);
+ } catch (ClassNotFoundException ex) {
+ LogLog.warn(
+ "Class for conversion pattern %" + converterName + " not found",
+ ex);
+
+ return null;
+ }
+ } else {
+ LogLog.warn(
+ "Bad map entry for conversion pattern %" + converterName + ".");
+
+ return null;
+ }
+ }
+
+ try {
+ Method factory =
+ converterClass.getMethod(
+ "newInstance",
+ new Class[] {
+ Class.forName("[Ljava.lang.String;")
+ });
+ String[] optionsArray = new String[options.size()];
+ optionsArray = (String[]) options.toArray(optionsArray);
+
+ Object newObj =
+ factory.invoke(null, new Object[] { optionsArray });
+
+ if (newObj instanceof PatternConverter) {
+ currentLiteral.delete(
+ 0,
+ currentLiteral.length()
+ - (converterId.length() - converterName.length()));
+
+ return (PatternConverter) newObj;
+ } else {
+ LogLog.warn(
+ "Class " + converterClass.getName()
+ + " does not extend PatternConverter.");
+ }
+ } catch (Exception ex) {
+ LogLog.error("Error creating converter for " + converterId, ex);
+
+ try {
+ //
+ // try default constructor
+ PatternConverter pc = (PatternConverter) converterClass.newInstance();
+ currentLiteral.delete(
+ 0,
+ currentLiteral.length()
+ - (converterId.length() - converterName.length()));
+
+ return pc;
+ } catch (Exception ex2) {
+ LogLog.error("Error creating converter for " + converterId, ex2);
+ }
+ }
+
+ return null;
+ }
+
+ /**
+ * Processes a format specifier sequence.
+ *
+ * @param c initial character of format specifier.
+ * @param pattern conversion pattern
+ * @param i current position in conversion pattern.
+ * @param currentLiteral current literal.
+ * @param formattingInfo current field specifier.
+ * @param converterRegistry map of user-provided pattern converters keyed by format specifier, may be null.
+ * @param rules map of stock pattern converters keyed by format specifier.
+ * @param patternConverters list to receive parsed pattern converter.
+ * @param formattingInfos list to receive corresponding field specifier.
+ * @return position after format specifier sequence.
+ */
+ private static int finalizeConverter(
+ char c, String pattern, int i,
+ final StringBuffer currentLiteral, final FormattingInfo formattingInfo,
+ final Map converterRegistry, final Map rules, final List patternConverters,
+ final List formattingInfos) {
+ StringBuffer convBuf = new StringBuffer();
+ i = extractConverter(c, pattern, i, convBuf, currentLiteral);
+
+ String converterId = convBuf.toString();
+
+ List options = new ArrayList();
+ i = extractOptions(pattern, i, options);
+
+ PatternConverter pc =
+ createConverter(
+ converterId, currentLiteral, converterRegistry, rules, options);
+
+ if (pc == null) {
+ StringBuffer msg;
+
+ if ((converterId == null) || (converterId.length() == 0)) {
+ msg =
+ new StringBuffer("Empty conversion specifier starting at position ");
+ } else {
+ msg = new StringBuffer("Unrecognized conversion specifier [");
+ msg.append(converterId);
+ msg.append("] starting at position ");
+ }
+
+ msg.append(Integer.toString(i));
+ msg.append(" in conversion pattern.");
+
+ LogLog.error(msg.toString());
+
+ patternConverters.add(
+ new LiteralPatternConverter(currentLiteral.toString()));
+ formattingInfos.add(FormattingInfo.getDefault());
+ } else {
+ patternConverters.add(pc);
+ formattingInfos.add(formattingInfo);
+
+ if (currentLiteral.length() > 0) {
+ patternConverters.add(
+ new LiteralPatternConverter(currentLiteral.toString()));
+ formattingInfos.add(FormattingInfo.getDefault());
+ }
+ }
+
+ currentLiteral.setLength(0);
+
+ return i;
+ }
+
+ /**
+ * The class wraps another Map but throws exceptions on any attempt to modify the map.
+ */
+ private static class ReadOnlyMap implements Map {
+ /**
+ * Wrapped map.
+ */
+ private final Map map;
+
+ /**
+ * Constructor
+ * @param src source map.
+ */
+ public ReadOnlyMap(Map src) {
+ map = src;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void clear() {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean containsKey(Object key) {
+ return map.containsKey(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean containsValue(Object value) {
+ return map.containsValue(value);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set entrySet() {
+ return map.entrySet();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object get(Object key) {
+ return map.get(key);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isEmpty() {
+ return map.isEmpty();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Set keySet() {
+ return map.keySet();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object put(Object key, Object value) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void putAll(Map t) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Object remove(Object key) {
+ throw new UnsupportedOperationException();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public int size() {
+ return map.size();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Collection values() {
+ return map.values();
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/PropertiesPatternConverter.java b/java/src/org/apache/log4j/pattern/PropertiesPatternConverter.java
new file mode 100644
index 0000000..a55cf97
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/PropertiesPatternConverter.java
@@ -0,0 +1,104 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.Iterator;
+import java.util.Set;
+import org.apache.log4j.helpers.*;
+
+
+/**
+ * Able to handle the contents of the LoggingEvent's Property bundle and either
+ * output the entire contents of the properties in a similar format to the
+ * java.util.Hashtable.toString(), or to output the value of a specific key
+ * within the property bundle
+ * when this pattern converter has the option set.
+ *
+ * @author Paul Smith
+ * @author Ceki Gülcü
+ */
+public final class PropertiesPatternConverter
+ extends LoggingEventPatternConverter {
+ /**
+ * Name of property to output.
+ */
+ private final String option;
+
+ /**
+ * Private constructor.
+ * @param options options, may be null.
+ */
+ private PropertiesPatternConverter(
+ final String[] options) {
+ super(
+ ((options != null) && (options.length > 0))
+ ? ("Property{" + options[0] + "}") : "Properties", "property");
+
+ if ((options != null) && (options.length > 0)) {
+ option = options[0];
+ } else {
+ option = null;
+ }
+ }
+
+ /**
+ * Obtains an instance of PropertiesPatternConverter.
+ * @param options options, may be null or first element contains name of property to format.
+ * @return instance of PropertiesPatternConverter.
+ */
+ public static PropertiesPatternConverter newInstance(
+ final String[] options) {
+ return new PropertiesPatternConverter(options);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ // if there is no additional options, we output every single
+ // Key/Value pair for the MDC in a similar format to Hashtable.toString()
+ if (option == null) {
+ toAppendTo.append("{");
+
+ try {
+ Set keySet = MDCKeySetExtractor.INSTANCE.getPropertyKeySet(event);
+ if (keySet != null) {
+ for (Iterator i = keySet.iterator(); i.hasNext();) {
+ Object item = i.next();
+ Object val = event.getMDC(item.toString());
+ toAppendTo.append("{").append(item).append(",").append(val).append(
+ "}");
+ }
+ }
+ } catch(Exception ex) {
+ LogLog.error("Unexpected exception while extracting MDC keys", ex);
+ }
+
+ toAppendTo.append("}");
+ } else {
+ // otherwise they just want a single key output
+ Object val = event.getMDC(option);
+
+ if (val != null) {
+ toAppendTo.append(val);
+ }
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/RelativeTimePatternConverter.java b/java/src/org/apache/log4j/pattern/RelativeTimePatternConverter.java
new file mode 100644
index 0000000..007a29a
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/RelativeTimePatternConverter.java
@@ -0,0 +1,106 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Return the relative time in milliseconds since loading of the LoggingEvent
+ * class.
+ *
+ * @author Ceki Gülcü
+ */
+public class RelativeTimePatternConverter extends LoggingEventPatternConverter {
+ /**
+ * Cached formatted timestamp.
+ */
+ private CachedTimestamp lastTimestamp = new CachedTimestamp(0, "");
+
+ /**
+ * Private constructor.
+ */
+ public RelativeTimePatternConverter() {
+ super("Time", "time");
+ }
+
+ /**
+ * Obtains an instance of RelativeTimePatternConverter.
+ * @param options options, currently ignored, may be null.
+ * @return instance of RelativeTimePatternConverter.
+ */
+ public static RelativeTimePatternConverter newInstance(
+ final String[] options) {
+ return new RelativeTimePatternConverter();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ long timestamp = event.timeStamp;
+
+ if (!lastTimestamp.format(timestamp, toAppendTo)) {
+ final String formatted =
+ Long.toString(timestamp - LoggingEvent.getStartTime());
+ toAppendTo.append(formatted);
+ lastTimestamp = new CachedTimestamp(timestamp, formatted);
+ }
+ }
+
+ /**
+ * Cached timestamp and formatted value.
+ */
+ private static final class CachedTimestamp {
+ /**
+ * Cached timestamp.
+ */
+ private final long timestamp;
+
+ /**
+ * Cached formatted timestamp.
+ */
+ private final String formatted;
+
+ /**
+ * Creates a new instance.
+ * @param timestamp timestamp.
+ * @param formatted formatted timestamp.
+ */
+ public CachedTimestamp(long timestamp, final String formatted) {
+ this.timestamp = timestamp;
+ this.formatted = formatted;
+ }
+
+ /**
+ * Appends the cached formatted timestamp to the buffer if timestamps match.
+ * @param newTimestamp requested timestamp.
+ * @param toAppendTo buffer to append formatted timestamp.
+ * @return true if requested timestamp matched cached timestamp.
+ */
+ public boolean format(long newTimestamp, final StringBuffer toAppendTo) {
+ if (newTimestamp == timestamp) {
+ toAppendTo.append(formatted);
+
+ return true;
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/SequenceNumberPatternConverter.java b/java/src/org/apache/log4j/pattern/SequenceNumberPatternConverter.java
new file mode 100644
index 0000000..8012682
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/SequenceNumberPatternConverter.java
@@ -0,0 +1,59 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Formats the event sequence number.
+ *
+ * @author Ceki Gülcü
+ */
+public class SequenceNumberPatternConverter
+ extends LoggingEventPatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final SequenceNumberPatternConverter INSTANCE =
+ new SequenceNumberPatternConverter();
+
+ /**
+ * Private constructor.
+ */
+ private SequenceNumberPatternConverter() {
+ super("Sequence Number", "sn");
+ }
+
+ /**
+ * Obtains an instance of SequencePatternConverter.
+ * @param options options, currently ignored, may be null.
+ * @return instance of SequencePatternConverter.
+ */
+ public static SequenceNumberPatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ toAppendTo.append("0");
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/ThreadPatternConverter.java b/java/src/org/apache/log4j/pattern/ThreadPatternConverter.java
new file mode 100644
index 0000000..6b3e6c3
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/ThreadPatternConverter.java
@@ -0,0 +1,58 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * Formats the event thread name.
+ *
+ * @author Ceki Gülcü
+ */
+public class ThreadPatternConverter extends LoggingEventPatternConverter {
+ /**
+ * Singleton.
+ */
+ private static final ThreadPatternConverter INSTANCE =
+ new ThreadPatternConverter();
+
+ /**
+ * Private constructor.
+ */
+ private ThreadPatternConverter() {
+ super("Thread", "thread");
+ }
+
+ /**
+ * Obtains an instance of ThreadPatternConverter.
+ * @param options options, currently ignored, may be null.
+ * @return instance of ThreadPatternConverter.
+ */
+ public static ThreadPatternConverter newInstance(
+ final String[] options) {
+ return INSTANCE;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ toAppendTo.append(event.getThreadName());
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java b/java/src/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java
new file mode 100644
index 0000000..bf9c4b4
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/ThrowableInformationPatternConverter.java
@@ -0,0 +1,108 @@
+/*
+ * 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.log4j.pattern;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.ThrowableInformation;
+
+
+/**
+ * Outputs the ThrowableInformation portion of the LoggingEvent.
+ * By default, outputs the full stack trace. %throwable{none}
+ * or %throwable{0} suppresses the stack trace. %throwable{short}
+ * or %throwable{1} outputs just the first line. %throwable{n}
+ * will output n lines for a positive integer or drop the last
+ * -n lines for a negative integer.
+ *
+ * @author Paul Smith
+ *
+ */
+public class ThrowableInformationPatternConverter
+ extends LoggingEventPatternConverter {
+
+ /**
+ * Maximum lines of stack trace to output.
+ */
+ private int maxLines = Integer.MAX_VALUE;
+
+ /**
+ * Private constructor.
+ * @param options options, may be null.
+ */
+ private ThrowableInformationPatternConverter(
+ final String[] options) {
+ super("Throwable", "throwable");
+
+ if ((options != null) && (options.length > 0)) {
+ if("none".equals(options[0])) {
+ maxLines = 0;
+ } else if("short".equals(options[0])) {
+ maxLines = 1;
+ } else {
+ try {
+ maxLines = Integer.parseInt(options[0]);
+ } catch(NumberFormatException ex) {
+ }
+ }
+ }
+ }
+
+ /**
+ * Gets an instance of the class.
+ * @param options pattern options, may be null. If first element is "short",
+ * only the first line of the throwable will be formatted.
+ * @return instance of class.
+ */
+ public static ThrowableInformationPatternConverter newInstance(
+ final String[] options) {
+ return new ThrowableInformationPatternConverter(options);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void format(final LoggingEvent event, final StringBuffer toAppendTo) {
+ if (maxLines != 0) {
+ ThrowableInformation information = event.getThrowableInformation();
+
+ if (information != null) {
+ String[] stringRep = information.getThrowableStrRep();
+
+ int length = stringRep.length;
+ if (maxLines < 0) {
+ length += maxLines;
+ } else if (length > maxLines) {
+ length = maxLines;
+ }
+
+ for (int i = 0; i < length; i++) {
+ String string = stringRep[i];
+ toAppendTo.append(string).append("\n");
+ }
+ }
+ }
+ }
+
+ /**
+ * This converter obviously handles throwables.
+ * @return true.
+ */
+ public boolean handlesThrowable() {
+ return true;
+ }
+}
diff --git a/java/src/org/apache/log4j/pattern/package.html b/java/src/org/apache/log4j/pattern/package.html
new file mode 100644
index 0000000..1db8283
--- /dev/null
+++ b/java/src/org/apache/log4j/pattern/package.html
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+Provides classes implementing format specifiers in conversion patterns.
+
+
+
diff --git a/java/src/org/apache/log4j/spi/AppenderAttachable.java b/java/src/org/apache/log4j/spi/AppenderAttachable.java
new file mode 100644
index 0000000..89d7ef4
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/AppenderAttachable.java
@@ -0,0 +1,75 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.Appender;
+import java.util.Enumeration;
+
+/**
+ Interface for attaching appenders to objects.
+
+ @author Ceki Gülcü
+ @since 0.9.1 */
+public interface AppenderAttachable {
+
+ /**
+ Add an appender.
+ */
+ public
+ void addAppender(Appender newAppender);
+
+ /**
+ Get all previously added appenders as an Enumeration. */
+ public
+ Enumeration getAllAppenders();
+
+ /**
+ Get an appender by name.
+ */
+ public
+ Appender getAppender(String name);
+
+
+ /**
+ Returns true if the specified appender is in list of
+ attached attached, false otherwise.
+
+ @since 1.2 */
+ public
+ boolean isAttached(Appender appender);
+
+ /**
+ Remove all previously added appenders.
+ */
+ void removeAllAppenders();
+
+
+ /**
+ Remove the appender passed as parameter from the list of appenders.
+ */
+ void removeAppender(Appender appender);
+
+
+ /**
+ Remove the appender with the name passed as parameter from the
+ list of appenders.
+ */
+ void
+ removeAppender(String name);
+}
+
diff --git a/java/src/org/apache/log4j/spi/Configurator.java b/java/src/org/apache/log4j/spi/Configurator.java
new file mode 100644
index 0000000..14fdd73
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/Configurator.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.log4j.spi;
+
+import java.net.URL;
+
+/**
+ Implemented by classes capable of configuring log4j using a URL.
+
+ @since 1.0
+ @author Anders Kristensen
+ */
+public interface Configurator {
+
+ /**
+ Special level value signifying inherited behaviour. The current
+ value of this string constant is inherited. {@link #NULL}
+ is a synonym. */
+ public static final String INHERITED = "inherited";
+
+ /**
+ Special level signifying inherited behaviour, same as {@link
+ #INHERITED}. The current value of this string constant is
+ null. */
+ public static final String NULL = "null";
+
+
+
+ /**
+ Interpret a resource pointed by a URL and set up log4j accordingly.
+
+ The configuration is done relative to the hierarchy
+ parameter.
+
+ @param url The URL to parse
+ @param repository The hierarchy to operation upon.
+ */
+ void doConfigure(URL url, LoggerRepository repository);
+}
diff --git a/java/src/org/apache/log4j/spi/DefaultRepositorySelector.java b/java/src/org/apache/log4j/spi/DefaultRepositorySelector.java
new file mode 100644
index 0000000..4b30752
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/DefaultRepositorySelector.java
@@ -0,0 +1,37 @@
+/*
+ * 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.log4j.spi;
+
+
+public class DefaultRepositorySelector implements RepositorySelector {
+
+ final LoggerRepository repository;
+
+ public
+ DefaultRepositorySelector(LoggerRepository repository) {
+ this.repository = repository;
+ }
+
+ public
+ LoggerRepository getLoggerRepository() {
+ return repository;
+ }
+}
+
diff --git a/java/src/org/apache/log4j/spi/ErrorCode.java b/java/src/org/apache/log4j/spi/ErrorCode.java
new file mode 100644
index 0000000..b0e57f1
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/ErrorCode.java
@@ -0,0 +1,36 @@
+/*
+ * 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.log4j.spi;
+
+
+/**
+ This interface defines commonly encoutered error codes.
+
+ @author Ceki Gülcü
+ @since 0.9.0
+ */
+public interface ErrorCode {
+
+ public final int GENERIC_FAILURE = 0;
+ public final int WRITE_FAILURE = 1;
+ public final int FLUSH_FAILURE = 2;
+ public final int CLOSE_FAILURE = 3;
+ public final int FILE_OPEN_FAILURE = 4;
+ public final int MISSING_LAYOUT = 5;
+ public final int ADDRESS_PARSE_FAILURE = 6;
+}
diff --git a/java/src/org/apache/log4j/spi/ErrorHandler.java b/java/src/org/apache/log4j/spi/ErrorHandler.java
new file mode 100644
index 0000000..d629a2d
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/ErrorHandler.java
@@ -0,0 +1,92 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+
+
+/**
+ Appenders may delegate their error handling to
+ ErrorHandlers.
+
+ Error handling is a particularly tedious to get right because by
+ definition errors are hard to predict and to reproduce.
+
+
+
Please take the time to contact the author in case you discover
+ that errors are not properly handled. You are most welcome to
+ suggest new error handling policies or criticize existing policies.
+
+
+ @author Ceki Gülcü
+
+*/
+public interface ErrorHandler extends OptionHandler {
+
+ /**
+ Add a reference to a logger to which the failing appender might
+ be attached to. The failing appender will be searched and
+ replaced only in the loggers you add through this method.
+
+ @param logger One of the loggers that will be searched for the failing
+ appender in view of replacement.
+
+ @since 1.2 */
+ void setLogger(Logger logger);
+
+
+ /**
+ Equivalent to the {@link #error(String, Exception, int,
+ LoggingEvent event)} with the the event parameteter set to
+ null.
+
+ */
+ void error(String message, Exception e, int errorCode);
+
+ /**
+ This method is normally used to just print the error message
+ passed as a parameter.
+ */
+ void error(String message);
+
+ /**
+ This method is invoked to handle the error.
+
+ @param message The message assoicated with the error.
+ @param e The Exption that was thrown when the error occured.
+ @param errorCode The error code associated with the error.
+ @param event The logging event that the failing appender is asked
+ to log.
+
+ @since 1.2 */
+ void error(String message, Exception e, int errorCode, LoggingEvent event);
+
+ /**
+ Set the appender for which errors are handled. This method is
+ usually called when the error handler is configured.
+
+ @since 1.2 */
+ void setAppender(Appender appender);
+
+ /**
+ Set the appender to falkback upon in case of failure.
+
+ @since 1.2 */
+ void setBackupAppender(Appender appender);
+}
diff --git a/java/src/org/apache/log4j/spi/Filter.java b/java/src/org/apache/log4j/spi/Filter.java
new file mode 100644
index 0000000..7bddbe8
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/Filter.java
@@ -0,0 +1,124 @@
+/*
+ * 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.log4j.spi;
+
+
+
+/**
+ Users should extend this class to implement customized logging
+ event filtering. Note that {@link org.apache.log4j.Category} and {@link
+ org.apache.log4j.AppenderSkeleton}, the parent class of all standard
+ appenders, have built-in filtering rules. It is suggested that you
+ first use and understand the built-in rules before rushing to write
+ your own custom filters.
+
+
This abstract class assumes and also imposes that filters be
+ organized in a linear chain. The {@link #decide
+ decide(LoggingEvent)} method of each filter is called sequentially,
+ in the order of their addition to the chain.
+
+
The {@link #decide decide(LoggingEvent)} method must return one
+ of the integer constants {@link #DENY}, {@link #NEUTRAL} or {@link
+ #ACCEPT}.
+
+
If the value {@link #DENY} is returned, then the log event is
+ dropped immediately without consulting with the remaining
+ filters.
+
+
If the value {@link #NEUTRAL} is returned, then the next filter
+ in the chain is consulted. If there are no more filters in the
+ chain, then the log event is logged. Thus, in the presence of no
+ filters, the default behaviour is to log all logging events.
+
+
If the value {@link #ACCEPT} is returned, then the log
+ event is logged without consulting the remaining filters.
+
+
The philosophy of log4j filters is largely inspired from the
+ Linux ipchains.
+
+
Note that filtering is only supported by the {@link
+ org.apache.log4j.xml.DOMConfigurator DOMConfigurator}. The {@link
+ org.apache.log4j.PropertyConfigurator PropertyConfigurator} does not
+ support filters.
+
+ @author Ceki Gülcü
+ @since 0.9.0 */
+public abstract class Filter implements OptionHandler {
+
+ /**
+ Points to the next filter in the filter chain.
+
+ @deprecated As of 1.2.12, use {@link #getNext} and {@link #setNext} instead
+ */
+ public Filter next;
+
+ /**
+ The log event must be dropped immediately without consulting
+ with the remaining filters, if any, in the chain. */
+ public static final int DENY = -1;
+
+ /**
+ This filter is neutral with respect to the log event. The
+ remaining filters, if any, should be consulted for a final decision.
+ */
+ public static final int NEUTRAL = 0;
+
+ /**
+ The log event must be logged immediately without consulting with
+ the remaining filters, if any, in the chain. */
+ public static final int ACCEPT = 1;
+
+
+ /**
+ Usually filters options become active when set. We provide a
+ default do-nothing implementation for convenience.
+ */
+ public
+ void activateOptions() {
+ }
+
+
+
+ /**
+
If the decision is DENY, then the event will be
+ dropped. If the decision is NEUTRAL, then the next
+ filter, if any, will be invoked. If the decision is ACCEPT then
+ the event will be logged without consulting with other filters in
+ the chain.
+
+ @param event The LoggingEvent to decide upon.
+ @return decision The decision of the filter. */
+ abstract
+ public
+ int decide(LoggingEvent event);
+
+ /**
+ * Set the next filter pointer.
+ */
+ public void setNext(Filter next) {
+ this.next = next;
+ }
+
+ /**
+ * Return the pointer to the next filter;
+ */
+ public Filter getNext() {
+ return next;
+ }
+
+}
diff --git a/java/src/org/apache/log4j/spi/HierarchyEventListener.java b/java/src/org/apache/log4j/spi/HierarchyEventListener.java
new file mode 100644
index 0000000..77a0efd
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/HierarchyEventListener.java
@@ -0,0 +1,45 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Category;
+
+/**
+ Listen to events occuring within a {@link
+ org.apache.log4j.Hierarchy Hierarchy}.
+
+ @author Ceki Gülcü
+ @since 1.2
+
+ */
+public interface HierarchyEventListener {
+
+
+ //public
+ //void categoryCreationEvent(Category cat);
+
+
+ public
+ void addAppenderEvent(Category cat, Appender appender);
+
+ public
+ void removeAppenderEvent(Category cat, Appender appender);
+
+
+}
diff --git a/java/src/org/apache/log4j/spi/LocationInfo.java b/java/src/org/apache/log4j/spi/LocationInfo.java
new file mode 100644
index 0000000..55ee5c3
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/LocationInfo.java
@@ -0,0 +1,391 @@
+/*
+ * 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.
+ */
+
+// Contributors: Mathias Rupprecht
+
+package org.apache.log4j.spi;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.helpers.LogLog;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.InterruptedIOException;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ The internal representation of caller location information.
+
+ @since 0.8.3
+*/
+public class LocationInfo implements java.io.Serializable {
+
+ /**
+ Caller's line number.
+ */
+ transient String lineNumber;
+ /**
+ Caller's file name.
+ */
+ transient String fileName;
+ /**
+ Caller's fully qualified class name.
+ */
+ transient String className;
+ /**
+ Caller's method name.
+ */
+ transient String methodName;
+ /**
+ All available caller information, in the format
+ fully.qualified.classname.of.caller.methodName(Filename.java:line)
+ */
+ public String fullInfo;
+
+ private static StringWriter sw = new StringWriter();
+ private static PrintWriter pw = new PrintWriter(sw);
+
+ private static Method getStackTraceMethod;
+ private static Method getClassNameMethod;
+ private static Method getMethodNameMethod;
+ private static Method getFileNameMethod;
+ private static Method getLineNumberMethod;
+
+
+ /**
+ When location information is not available the constant
+ NA is returned. Current value of this string
+ constant is ?. */
+ public final static String NA = "?";
+
+ static final long serialVersionUID = -1325822038990805636L;
+
+ /**
+ * NA_LOCATION_INFO is provided for compatibility with log4j 1.3.
+ * @since 1.2.15
+ */
+ public static final LocationInfo NA_LOCATION_INFO =
+ new LocationInfo(NA, NA, NA, NA);
+
+
+
+ // Check if we are running in IBM's visual age.
+ static boolean inVisualAge = false;
+ static {
+ try {
+ inVisualAge = Class.forName("com.ibm.uvm.tools.DebugSupport") != null;
+ LogLog.debug("Detected IBM VisualAge environment.");
+ } catch(Throwable e) {
+ // nothing to do
+ }
+ try {
+ Class[] noArgs = null;
+ getStackTraceMethod = Throwable.class.getMethod("getStackTrace", noArgs);
+ Class stackTraceElementClass = Class.forName("java.lang.StackTraceElement");
+ getClassNameMethod = stackTraceElementClass.getMethod("getClassName", noArgs);
+ getMethodNameMethod = stackTraceElementClass.getMethod("getMethodName", noArgs);
+ getFileNameMethod = stackTraceElementClass.getMethod("getFileName", noArgs);
+ getLineNumberMethod = stackTraceElementClass.getMethod("getLineNumber", noArgs);
+ } catch(ClassNotFoundException ex) {
+ LogLog.debug("LocationInfo will use pre-JDK 1.4 methods to determine location.");
+ } catch(NoSuchMethodException ex) {
+ LogLog.debug("LocationInfo will use pre-JDK 1.4 methods to determine location.");
+ }
+ }
+
+ /**
+ Instantiate location information based on a Throwable. We
+ expect the Throwable t, to be in the format
+
+
+ java.lang.Throwable
+ ...
+ at org.apache.log4j.PatternLayout.format(PatternLayout.java:413)
+ at org.apache.log4j.FileAppender.doAppend(FileAppender.java:183)
+ at org.apache.log4j.Category.callAppenders(Category.java:131)
+ at org.apache.log4j.Category.log(Category.java:512)
+ at callers.fully.qualified.className.methodName(FileName.java:74)
+ ...
+
+
+ However, we can also deal with JIT compilers that "lose" the
+ location information, especially between the parentheses.
+ @param t throwable used to determine location, may be null.
+ @param fqnOfCallingClass class name of first class considered part of
+ the logging framework. Location will be site that calls a method on this class.
+
+ */
+ public LocationInfo(Throwable t, String fqnOfCallingClass) {
+ if(t == null || fqnOfCallingClass == null)
+ return;
+ if (getLineNumberMethod != null) {
+ try {
+ Object[] noArgs = null;
+ Object[] elements = (Object[]) getStackTraceMethod.invoke(t, noArgs);
+ String prevClass = NA;
+ for(int i = elements.length - 1; i >= 0; i--) {
+ String thisClass = (String) getClassNameMethod.invoke(elements[i], noArgs);
+ if(fqnOfCallingClass.equals(thisClass)) {
+ int caller = i + 1;
+ if (caller < elements.length) {
+ className = prevClass;
+ methodName = (String) getMethodNameMethod.invoke(elements[caller], noArgs);
+ fileName = (String) getFileNameMethod.invoke(elements[caller], noArgs);
+ if (fileName == null) {
+ fileName = NA;
+ }
+ int line = ((Integer) getLineNumberMethod.invoke(elements[caller], noArgs)).intValue();
+ if (line < 0) {
+ lineNumber = NA;
+ } else {
+ lineNumber = String.valueOf(line);
+ }
+ StringBuffer buf = new StringBuffer();
+ buf.append(className);
+ buf.append(".");
+ buf.append(methodName);
+ buf.append("(");
+ buf.append(fileName);
+ buf.append(":");
+ buf.append(lineNumber);
+ buf.append(")");
+ this.fullInfo = buf.toString();
+ }
+ return;
+ }
+ prevClass = thisClass;
+ }
+ return;
+ } catch(IllegalAccessException ex) {
+ LogLog.debug("LocationInfo failed using JDK 1.4 methods", ex);
+ } catch(InvocationTargetException ex) {
+ if (ex.getTargetException() instanceof InterruptedException
+ || ex.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.debug("LocationInfo failed using JDK 1.4 methods", ex);
+ } catch(RuntimeException ex) {
+ LogLog.debug("LocationInfo failed using JDK 1.4 methods", ex);
+ }
+ }
+
+ String s;
+ // Protect against multiple access to sw.
+ synchronized(sw) {
+ t.printStackTrace(pw);
+ s = sw.toString();
+ sw.getBuffer().setLength(0);
+ }
+ //System.out.println("s is ["+s+"].");
+ int ibegin, iend;
+
+ // Given the current structure of the package, the line
+ // containing "org.apache.log4j.Category." should be printed just
+ // before the caller.
+
+ // This method of searching may not be fastest but it's safer
+ // than counting the stack depth which is not guaranteed to be
+ // constant across JVM implementations.
+ ibegin = s.lastIndexOf(fqnOfCallingClass);
+ if(ibegin == -1)
+ return;
+
+ //
+ // if the next character after the class name exists
+ // but is not a period, see if the classname is
+ // followed by a period earlier in the trace.
+ // Minimizes mistakeningly matching on a class whose
+ // name is a substring of the desired class.
+ // See bug 44888.
+ if (ibegin + fqnOfCallingClass.length() < s.length() &&
+ s.charAt(ibegin + fqnOfCallingClass.length()) != '.') {
+ int i = s.lastIndexOf(fqnOfCallingClass + ".");
+ if (i != -1) {
+ ibegin = i;
+ }
+ }
+
+
+ ibegin = s.indexOf(Layout.LINE_SEP, ibegin);
+ if(ibegin == -1)
+ return;
+ ibegin+= Layout.LINE_SEP_LEN;
+
+ // determine end of line
+ iend = s.indexOf(Layout.LINE_SEP, ibegin);
+ if(iend == -1)
+ return;
+
+ // VA has a different stack trace format which doesn't
+ // need to skip the inital 'at'
+ if(!inVisualAge) {
+ // back up to first blank character
+ ibegin = s.lastIndexOf("at ", iend);
+ if(ibegin == -1)
+ return;
+ // Add 3 to skip "at ";
+ ibegin += 3;
+ }
+ // everything between is the requested stack item
+ this.fullInfo = s.substring(ibegin, iend);
+ }
+
+ /**
+ * Appends a location fragment to a buffer to build the
+ * full location info.
+ * @param buf StringBuffer to receive content.
+ * @param fragment fragment of location (class, method, file, line),
+ * if null the value of NA will be appended.
+ * @since 1.2.15
+ */
+ private static final void appendFragment(final StringBuffer buf,
+ final String fragment) {
+ if (fragment == null) {
+ buf.append(NA);
+ } else {
+ buf.append(fragment);
+ }
+ }
+
+ /**
+ * Create new instance.
+ * @param file source file name
+ * @param classname class name
+ * @param method method
+ * @param line source line number
+ *
+ * @since 1.2.15
+ */
+ public LocationInfo(
+ final String file,
+ final String classname,
+ final String method,
+ final String line) {
+ this.fileName = file;
+ this.className = classname;
+ this.methodName = method;
+ this.lineNumber = line;
+ StringBuffer buf = new StringBuffer();
+ appendFragment(buf, classname);
+ buf.append(".");
+ appendFragment(buf, method);
+ buf.append("(");
+ appendFragment(buf, file);
+ buf.append(":");
+ appendFragment(buf, line);
+ buf.append(")");
+ this.fullInfo = buf.toString();
+ }
+
+ /**
+ Return the fully qualified class name of the caller making the
+ logging request.
+ */
+ public
+ String getClassName() {
+ if(fullInfo == null) return NA;
+ if(className == null) {
+ // Starting the search from '(' is safer because there is
+ // potentially a dot between the parentheses.
+ int iend = fullInfo.lastIndexOf('(');
+ if(iend == -1)
+ className = NA;
+ else {
+ iend =fullInfo.lastIndexOf('.', iend);
+
+ // This is because a stack trace in VisualAge looks like:
+
+ //java.lang.RuntimeException
+ // java.lang.Throwable()
+ // java.lang.Exception()
+ // java.lang.RuntimeException()
+ // void test.test.B.print()
+ // void test.test.A.printIndirect()
+ // void test.test.Run.main(java.lang.String [])
+ int ibegin = 0;
+ if (inVisualAge) {
+ ibegin = fullInfo.lastIndexOf(' ', iend)+1;
+ }
+
+ if(iend == -1)
+ className = NA;
+ else
+ className = this.fullInfo.substring(ibegin, iend);
+ }
+ }
+ return className;
+ }
+
+ /**
+ Return the file name of the caller.
+
+
This information is not always available.
+ */
+ public
+ String getFileName() {
+ if(fullInfo == null) return NA;
+
+ if(fileName == null) {
+ int iend = fullInfo.lastIndexOf(':');
+ if(iend == -1)
+ fileName = NA;
+ else {
+ int ibegin = fullInfo.lastIndexOf('(', iend - 1);
+ fileName = this.fullInfo.substring(ibegin + 1, iend);
+ }
+ }
+ return fileName;
+ }
+
+ /**
+ Returns the line number of the caller.
+
+
This information is not always available.
+ */
+ public
+ String getLineNumber() {
+ if(fullInfo == null) return NA;
+
+ if(lineNumber == null) {
+ int iend = fullInfo.lastIndexOf(')');
+ int ibegin = fullInfo.lastIndexOf(':', iend -1);
+ if(ibegin == -1)
+ lineNumber = NA;
+ else
+ lineNumber = this.fullInfo.substring(ibegin + 1, iend);
+ }
+ return lineNumber;
+ }
+
+ /**
+ Returns the method name of the caller.
+ */
+ public
+ String getMethodName() {
+ if(fullInfo == null) return NA;
+ if(methodName == null) {
+ int iend = fullInfo.lastIndexOf('(');
+ int ibegin = fullInfo.lastIndexOf('.', iend);
+ if(ibegin == -1)
+ methodName = NA;
+ else
+ methodName = this.fullInfo.substring(ibegin + 1, iend);
+ }
+ return methodName;
+ }
+}
diff --git a/java/src/org/apache/log4j/spi/LoggerFactory.java b/java/src/org/apache/log4j/spi/LoggerFactory.java
new file mode 100644
index 0000000..568c41f
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/LoggerFactory.java
@@ -0,0 +1,38 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.Logger;
+
+/**
+
+ Implement this interface to create new instances of Logger or
+ a sub-class of Logger.
+
+
See examples/subclass/MyLogger.java for an example.
+
+ @author Ceki Gülcü
+ @since version 0.8.5
+
+ */
+public interface LoggerFactory {
+
+ public
+ Logger makeNewLoggerInstance(String name);
+
+}
diff --git a/java/src/org/apache/log4j/spi/LoggerRepository.java b/java/src/org/apache/log4j/spi/LoggerRepository.java
new file mode 100644
index 0000000..9ca156b
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/LoggerRepository.java
@@ -0,0 +1,110 @@
+/*
+ * 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.log4j.spi;
+
+import java.util.Enumeration;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+/**
+ A LoggerRepository is used to create and retrieve
+ Loggers. The relation between loggers in a repository
+ depends on the repository but typically loggers are arranged in a
+ named hierarchy.
+
+
In addition to the creational methods, a
+ LoggerRepository can be queried for existing loggers,
+ can act as a point of registry for events related to loggers.
+
+ @author Ceki Gülcü
+ @since 1.2 */
+public interface LoggerRepository {
+
+ /**
+ Add a {@link HierarchyEventListener} event to the repository.
+ */
+ public
+ void addHierarchyEventListener(HierarchyEventListener listener);
+
+ /**
+ Returns whether this repository is disabled for a given
+ level. The answer depends on the repository threshold and the
+ level parameter. See also {@link #setThreshold}
+ method. */
+ boolean isDisabled(int level);
+
+ /**
+ Set the repository-wide threshold. All logging requests below the
+ threshold are immediately dropped. By default, the threshold is
+ set to Level.ALL which has the lowest possible rank. */
+ public
+ void setThreshold(Level level);
+
+ /**
+ Another form of {@link #setThreshold(Level)} accepting a string
+ parameter instead of a Level. */
+ public
+ void setThreshold(String val);
+
+ public
+ void emitNoAppenderWarning(Category cat);
+
+ /**
+ Get the repository-wide threshold. See {@link
+ #setThreshold(Level)} for an explanation. */
+ public
+ Level getThreshold();
+
+ public
+ Logger getLogger(String name);
+
+ public
+ Logger getLogger(String name, LoggerFactory factory);
+
+ public
+ Logger getRootLogger();
+
+ public
+ abstract
+ Logger exists(String name);
+
+ public
+ abstract
+ void shutdown();
+
+ public
+ Enumeration getCurrentLoggers();
+
+ /**
+ Deprecated. Please use {@link #getCurrentLoggers} instead. */
+ public
+ Enumeration getCurrentCategories();
+
+
+ public
+ abstract
+ void fireAddAppenderEvent(Category logger, Appender appender);
+
+ public
+ abstract
+ void resetConfiguration();
+
+}
diff --git a/java/src/org/apache/log4j/spi/LoggingEvent.java b/java/src/org/apache/log4j/spi/LoggingEvent.java
new file mode 100644
index 0000000..2bacb50
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/LoggingEvent.java
@@ -0,0 +1,639 @@
+/*
+ * 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.log4j.spi;
+
+import java.io.InterruptedIOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Hashtable;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.Level;
+import org.apache.log4j.MDC;
+import org.apache.log4j.NDC;
+import org.apache.log4j.Priority;
+import org.apache.log4j.helpers.Loader;
+import org.apache.log4j.helpers.LogLog;
+
+// Contributors: Nelson Minar
+// Wolf Siberski
+// Anders Kristensen
+
+/**
+ The internal representation of logging events. When an affirmative
+ decision is made to log then a LoggingEvent instance
+ is created. This instance is passed around to the different log4j
+ components.
+
+ This class is of concern to those wishing to extend log4j.
+
+ @author Ceki Gülcü
+ @author James P. Cakalic
+
+ @since 0.8.2 */
+public class LoggingEvent implements java.io.Serializable {
+
+ private static long startTime = System.currentTimeMillis();
+
+ /** Fully qualified name of the calling category class. */
+ transient public final String fqnOfCategoryClass;
+
+ /**
+ * The category of the logging event. This field is not serialized
+ * for performance reasons.
+ *
+ *
It is set by the LoggingEvent constructor or set by a remote
+ * entity after deserialization.
+ *
+ * @deprecated This field will be marked as private or be completely
+ * removed in future releases. Please do not use it.
+ * */
+ transient private Category logger;
+
+ /**
+ *
The category (logger) name.
+ *
+ * @deprecated This field will be marked as private in future
+ * releases. Please do not access it directly. Use the {@link
+ * #getLoggerName} method instead.
+
+ * */
+ final public String categoryName;
+
+ /**
+ * Level of logging event. Level cannot be serializable because it
+ * is a flyweight. Due to its special seralization it cannot be
+ * declared final either.
+ *
+ *
This field should not be accessed directly. You shoud use the
+ * {@link #getLevel} method instead.
+ *
+ * @deprecated This field will be marked as private in future
+ * releases. Please do not access it directly. Use the {@link
+ * #getLevel} method instead.
+ * */
+ transient public Priority level;
+
+ /** The nested diagnostic context (NDC) of logging event. */
+ private String ndc;
+
+ /** The mapped diagnostic context (MDC) of logging event. */
+ private Hashtable mdcCopy;
+
+
+ /** Have we tried to do an NDC lookup? If we did, there is no need
+ * to do it again. Note that its value is always false when
+ * serialized. Thus, a receiving SocketNode will never use it's own
+ * (incorrect) NDC. See also writeObject method. */
+ private boolean ndcLookupRequired = true;
+
+
+ /** Have we tried to do an MDC lookup? If we did, there is no need
+ * to do it again. Note that its value is always false when
+ * serialized. See also the getMDC and getMDCCopy methods. */
+ private boolean mdcCopyLookupRequired = true;
+
+ /** The application supplied message of logging event. */
+ transient private Object message;
+
+ /** The application supplied message rendered through the log4j
+ objet rendering mechanism.*/
+ private String renderedMessage;
+
+ /** The name of thread in which this logging event was generated. */
+ private String threadName;
+
+
+ /** This
+ variable contains information about this event's throwable
+ */
+ private ThrowableInformation throwableInfo;
+
+ /** The number of milliseconds elapsed from 1/1/1970 until logging event
+ was created. */
+ public final long timeStamp;
+ /** Location information for the caller. */
+ private LocationInfo locationInfo;
+
+ // Serialization
+ static final long serialVersionUID = -868428216207166145L;
+
+ static final Integer[] PARAM_ARRAY = new Integer[1];
+ static final String TO_LEVEL = "toLevel";
+ static final Class[] TO_LEVEL_PARAMS = new Class[] {int.class};
+ static final Hashtable methodCache = new Hashtable(3); // use a tiny table
+
+ /**
+ Instantiate a LoggingEvent from the supplied parameters.
+
+
Except {@link #timeStamp} all the other fields of
+ LoggingEvent are filled when actually needed.
+
+ @param logger The logger generating this event.
+ @param level The level of this event.
+ @param message The message of this event.
+ @param throwable The throwable of this event. */
+ public LoggingEvent(String fqnOfCategoryClass, Category logger,
+ Priority level, Object message, Throwable throwable) {
+ this.fqnOfCategoryClass = fqnOfCategoryClass;
+ this.logger = logger;
+ this.categoryName = logger.getName();
+ this.level = level;
+ this.message = message;
+ if(throwable != null) {
+ this.throwableInfo = new ThrowableInformation(throwable, logger);
+ }
+ timeStamp = System.currentTimeMillis();
+ }
+
+ /**
+ Instantiate a LoggingEvent from the supplied parameters.
+
+
Except {@link #timeStamp} all the other fields of
+ LoggingEvent are filled when actually needed.
+
+ @param logger The logger generating this event.
+ @param timeStamp the timestamp of this logging event
+ @param level The level of this event.
+ @param message The message of this event.
+ @param throwable The throwable of this event. */
+ public LoggingEvent(String fqnOfCategoryClass, Category logger,
+ long timeStamp, Priority level, Object message,
+ Throwable throwable) {
+ this.fqnOfCategoryClass = fqnOfCategoryClass;
+ this.logger = logger;
+ this.categoryName = logger.getName();
+ this.level = level;
+ this.message = message;
+ if(throwable != null) {
+ this.throwableInfo = new ThrowableInformation(throwable, logger);
+ }
+
+ this.timeStamp = timeStamp;
+ }
+
+ /**
+ Create new instance.
+ @since 1.2.15
+ @param fqnOfCategoryClass Fully qualified class name
+ of Logger implementation.
+ @param logger The logger generating this event.
+ @param timeStamp the timestamp of this logging event
+ @param level The level of this event.
+ @param message The message of this event.
+ @param threadName thread name
+ @param throwable The throwable of this event.
+ @param ndc Nested diagnostic context
+ @param info Location info
+ @param properties MDC properties
+ */
+ public LoggingEvent(final String fqnOfCategoryClass,
+ final Category logger,
+ final long timeStamp,
+ final Level level,
+ final Object message,
+ final String threadName,
+ final ThrowableInformation throwable,
+ final String ndc,
+ final LocationInfo info,
+ final java.util.Map properties) {
+ super();
+ this.fqnOfCategoryClass = fqnOfCategoryClass;
+ this.logger = logger;
+ if (logger != null) {
+ categoryName = logger.getName();
+ } else {
+ categoryName = null;
+ }
+ this.level = level;
+ this.message = message;
+ if(throwable != null) {
+ this.throwableInfo = throwable;
+ }
+
+ this.timeStamp = timeStamp;
+ this.threadName = threadName;
+ ndcLookupRequired = false;
+ this.ndc = ndc;
+ this.locationInfo = info;
+ mdcCopyLookupRequired = false;
+ if (properties != null) {
+ mdcCopy = new java.util.Hashtable(properties);
+ }
+ }
+
+
+ /**
+ Set the location information for this logging event. The collected
+ information is cached for future use.
+ */
+ public LocationInfo getLocationInformation() {
+ if(locationInfo == null) {
+ locationInfo = new LocationInfo(new Throwable(), fqnOfCategoryClass);
+ }
+ return locationInfo;
+ }
+
+ /**
+ * Return the level of this event. Use this form instead of directly
+ * accessing the level field. */
+ public Level getLevel() {
+ return (Level) level;
+ }
+
+ /**
+ * Return the name of the logger. Use this form instead of directly
+ * accessing the categoryName field.
+ */
+ public String getLoggerName() {
+ return categoryName;
+ }
+
+ /**
+ * Gets the logger of the event.
+ * Use should be restricted to cloning events.
+ * @since 1.2.15
+ */
+ public Category getLogger() {
+ return logger;
+ }
+
+ /**
+ Return the message for this logging event.
+
+
Before serialization, the returned object is the message
+ passed by the user to generate the logging event. After
+ serialization, the returned value equals the String form of the
+ message possibly after object rendering.
+
+ @since 1.1 */
+ public
+ Object getMessage() {
+ if(message != null) {
+ return message;
+ } else {
+ return getRenderedMessage();
+ }
+ }
+
+ /**
+ * This method returns the NDC for this event. It will return the
+ * correct content even if the event was generated in a different
+ * thread or even on a different machine. The {@link NDC#get} method
+ * should never be called directly. */
+ public
+ String getNDC() {
+ if(ndcLookupRequired) {
+ ndcLookupRequired = false;
+ ndc = NDC.get();
+ }
+ return ndc;
+ }
+
+
+ /**
+ Returns the the context corresponding to the key
+ parameter. If there is a local MDC copy, possibly because we are
+ in a logging server or running inside AsyncAppender, then we
+ search for the key in MDC copy, if a value is found it is
+ returned. Otherwise, if the search in MDC copy returns a null
+ result, then the current thread's MDC is used.
+
+
Note that both the local MDC copy and the current
+ thread's MDC are searched.
+
+ */
+ public
+ Object getMDC(String key) {
+ Object r;
+ // Note the mdcCopy is used if it exists. Otherwise we use the MDC
+ // that is associated with the thread.
+ if(mdcCopy != null) {
+ r = mdcCopy.get(key);
+ if(r != null) {
+ return r;
+ }
+ }
+ return MDC.get(key);
+ }
+
+ /**
+ Obtain a copy of this thread's MDC prior to serialization or
+ asynchronous logging.
+ */
+ public
+ void getMDCCopy() {
+ if(mdcCopyLookupRequired) {
+ mdcCopyLookupRequired = false;
+ // the clone call is required for asynchronous logging.
+ // See also bug #5932.
+ Hashtable t = (Hashtable) MDC.getContext();
+ if(t != null) {
+ mdcCopy = (Hashtable) t.clone();
+ }
+ }
+ }
+
+ public
+ String getRenderedMessage() {
+ if(renderedMessage == null && message != null) {
+ if(message instanceof String)
+ renderedMessage = (String) message;
+ else {
+ LoggerRepository repository = logger.getLoggerRepository();
+
+ if(repository instanceof RendererSupport) {
+ RendererSupport rs = (RendererSupport) repository;
+ renderedMessage= rs.getRendererMap().findAndRender(message);
+ } else {
+ renderedMessage = message.toString();
+ }
+ }
+ }
+ return renderedMessage;
+ }
+
+ /**
+ Returns the time when the application started, in milliseconds
+ elapsed since 01.01.1970. */
+ public static long getStartTime() {
+ return startTime;
+ }
+
+ public
+ String getThreadName() {
+ if(threadName == null)
+ threadName = (Thread.currentThread()).getName();
+ return threadName;
+ }
+
+ /**
+ Returns the throwable information contained within this
+ event. May be null if there is no such information.
+
+
Note that the {@link Throwable} object contained within a
+ {@link ThrowableInformation} does not survive serialization.
+
+ @since 1.1 */
+ public
+ ThrowableInformation getThrowableInformation() {
+ return throwableInfo;
+ }
+
+ /**
+ Return this event's throwable's string[] representaion.
+ */
+ public
+ String[] getThrowableStrRep() {
+
+ if(throwableInfo == null)
+ return null;
+ else
+ return throwableInfo.getThrowableStrRep();
+ }
+
+
+ private
+ void readLevel(ObjectInputStream ois)
+ throws java.io.IOException, ClassNotFoundException {
+
+ int p = ois.readInt();
+ try {
+ String className = (String) ois.readObject();
+ if(className == null) {
+ level = Level.toLevel(p);
+ } else {
+ Method m = (Method) methodCache.get(className);
+ if(m == null) {
+ Class clazz = Loader.loadClass(className);
+ // Note that we use Class.getDeclaredMethod instead of
+ // Class.getMethod. This assumes that the Level subclass
+ // implements the toLevel(int) method which is a
+ // requirement. Actually, it does not make sense for Level
+ // subclasses NOT to implement this method. Also note that
+ // only Level can be subclassed and not Priority.
+ m = clazz.getDeclaredMethod(TO_LEVEL, TO_LEVEL_PARAMS);
+ methodCache.put(className, m);
+ }
+ PARAM_ARRAY[0] = new Integer(p);
+ level = (Level) m.invoke(null, PARAM_ARRAY);
+ }
+ } catch(InvocationTargetException e) {
+ if (e.getTargetException() instanceof InterruptedException
+ || e.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.warn("Level deserialization failed, reverting to default.", e);
+ level = Level.toLevel(p);
+ } catch(NoSuchMethodException e) {
+ LogLog.warn("Level deserialization failed, reverting to default.", e);
+ level = Level.toLevel(p);
+ } catch(IllegalAccessException e) {
+ LogLog.warn("Level deserialization failed, reverting to default.", e);
+ level = Level.toLevel(p);
+ } catch(RuntimeException e) {
+ LogLog.warn("Level deserialization failed, reverting to default.", e);
+ level = Level.toLevel(p);
+ }
+ }
+
+ private void readObject(ObjectInputStream ois)
+ throws java.io.IOException, ClassNotFoundException {
+ ois.defaultReadObject();
+ readLevel(ois);
+
+ // Make sure that no location info is available to Layouts
+ if(locationInfo == null)
+ locationInfo = new LocationInfo(null, null);
+ }
+
+ private
+ void writeObject(ObjectOutputStream oos) throws java.io.IOException {
+ // Aside from returning the current thread name the wgetThreadName
+ // method sets the threadName variable.
+ this.getThreadName();
+
+ // This sets the renders the message in case it wasn't up to now.
+ this.getRenderedMessage();
+
+ // This call has a side effect of setting this.ndc and
+ // setting ndcLookupRequired to false if not already false.
+ this.getNDC();
+
+ // This call has a side effect of setting this.mdcCopy and
+ // setting mdcLookupRequired to false if not already false.
+ this.getMDCCopy();
+
+ // This sets the throwable sting representation of the event throwable.
+ this.getThrowableStrRep();
+
+ oos.defaultWriteObject();
+
+ // serialize this event's level
+ writeLevel(oos);
+ }
+
+ private
+ void writeLevel(ObjectOutputStream oos) throws java.io.IOException {
+
+ oos.writeInt(level.toInt());
+
+ Class clazz = level.getClass();
+ if(clazz == Level.class) {
+ oos.writeObject(null);
+ } else {
+ // writing directly the Class object would be nicer, except that
+ // serialized a Class object can not be read back by JDK
+ // 1.1.x. We have to resort to this hack instead.
+ oos.writeObject(clazz.getName());
+ }
+ }
+
+ /**
+ * Set value for MDC property.
+ * This adds the specified MDC property to the event.
+ * Access to the MDC is not synchronized, so this
+ * method should only be called when it is known that
+ * no other threads are accessing the MDC.
+ * @since 1.2.15
+ * @param propName
+ * @param propValue
+ */
+ public final void setProperty(final String propName,
+ final String propValue) {
+ if (mdcCopy == null) {
+ getMDCCopy();
+ }
+ if (mdcCopy == null) {
+ mdcCopy = new Hashtable();
+ }
+ mdcCopy.put(propName, propValue);
+ }
+
+ /**
+ * Return a property for this event. The return value can be null.
+ *
+ * Equivalent to getMDC(String) in log4j 1.2. Provided
+ * for compatibility with log4j 1.3.
+ *
+ * @param key property name
+ * @return property value or null if property not set
+ * @since 1.2.15
+ */
+ public final String getProperty(final String key) {
+ Object value = getMDC(key);
+ String retval = null;
+ if (value != null) {
+ retval = value.toString();
+ }
+ return retval;
+ }
+
+ /**
+ * Check for the existence of location information without creating it
+ * (a byproduct of calling getLocationInformation).
+ * @return true if location information has been extracted.
+ * @since 1.2.15
+ */
+ public final boolean locationInformationExists() {
+ return (locationInfo != null);
+ }
+
+ /**
+ * Getter for the event's time stamp. The time stamp is calculated starting
+ * from 1970-01-01 GMT.
+ * @return timestamp
+ *
+ * @since 1.2.15
+ */
+ public final long getTimeStamp() {
+ return timeStamp;
+ }
+
+ /**
+ * Returns the set of the key values in the properties
+ * for the event.
+ *
+ * The returned set is unmodifiable by the caller.
+ *
+ * Provided for compatibility with log4j 1.3
+ *
+ * @return Set an unmodifiable set of the property keys.
+ * @since 1.2.15
+ */
+ public Set getPropertyKeySet() {
+ return getProperties().keySet();
+ }
+
+ /**
+ * Returns the set of properties
+ * for the event.
+ *
+ * The returned set is unmodifiable by the caller.
+ *
+ * Provided for compatibility with log4j 1.3
+ *
+ * @return Set an unmodifiable map of the properties.
+ * @since 1.2.15
+ */
+ public Map getProperties() {
+ getMDCCopy();
+ Map properties;
+ if (mdcCopy == null) {
+ properties = new HashMap();
+ } else {
+ properties = mdcCopy;
+ }
+ return Collections.unmodifiableMap(properties);
+ }
+
+ /**
+ * Get the fully qualified name of the calling logger sub-class/wrapper.
+ * Provided for compatibility with log4j 1.3
+ * @return fully qualified class name, may be null.
+ * @since 1.2.15
+ */
+ public String getFQNOfLoggerClass() {
+ return fqnOfCategoryClass;
+ }
+
+
+ /**
+ * This removes the specified MDC property from the event.
+ * Access to the MDC is not synchronized, so this
+ * method should only be called when it is known that
+ * no other threads are accessing the MDC.
+ * @param propName the property name to remove
+ * @since 1.2.16
+ */
+ public Object removeProperty(String propName) {
+ if (mdcCopy == null) {
+ getMDCCopy();
+ }
+ if (mdcCopy == null) {
+ mdcCopy = new Hashtable();
+ }
+ return mdcCopy.remove(propName);
+ }
+}
diff --git a/java/src/org/apache/log4j/spi/NOPLogger.java b/java/src/org/apache/log4j/spi/NOPLogger.java
new file mode 100644
index 0000000..38b1514
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/NOPLogger.java
@@ -0,0 +1,212 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Priority;
+
+import java.util.Enumeration;
+import java.util.ResourceBundle;
+import java.util.Vector;
+
+/**
+ * No-operation implementation of Logger used by NOPLoggerRepository.
+ * @since 1.2.15
+ */
+public final class NOPLogger extends Logger {
+ /**
+ * Create instance of Logger.
+ * @param repo repository, may not be null.
+ * @param name name, may not be null, use "root" for root logger.
+ */
+ public NOPLogger(NOPLoggerRepository repo, final String name) {
+ super(name);
+ this.repository = repo;
+ this.level = Level.OFF;
+ this.parent = this;
+ }
+
+ /** {@inheritDoc} */
+ public void addAppender(final Appender newAppender) {
+ }
+
+ /** {@inheritDoc} */
+ public void assertLog(final boolean assertion, final String msg) {
+ }
+
+
+ /** {@inheritDoc} */
+ public void callAppenders(final LoggingEvent event) {
+ }
+
+ /** {@inheritDoc} */
+ void closeNestedAppenders() {
+ }
+
+ /** {@inheritDoc} */
+ public void debug(final Object message) {
+ }
+
+
+ /** {@inheritDoc} */
+ public void debug(final Object message, final Throwable t) {
+ }
+
+ /** {@inheritDoc} */
+ public void error(final Object message) {
+ }
+
+ /** {@inheritDoc} */
+ public void error(final Object message, final Throwable t) {
+ }
+
+
+ /** {@inheritDoc} */
+ public void fatal(final Object message) {
+ }
+
+ /** {@inheritDoc} */
+ public void fatal(final Object message, final Throwable t) {
+ }
+
+
+ /** {@inheritDoc} */
+ public Enumeration getAllAppenders() {
+ return new Vector().elements();
+ }
+
+ /** {@inheritDoc} */
+ public Appender getAppender(final String name) {
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ public Level getEffectiveLevel() {
+ return Level.OFF;
+ }
+
+ /** {@inheritDoc} */
+ public Priority getChainedPriority() {
+ return getEffectiveLevel();
+ }
+
+ /** {@inheritDoc} */
+ public ResourceBundle getResourceBundle() {
+ return null;
+ }
+
+
+ /** {@inheritDoc} */
+ public void info(final Object message) {
+ }
+
+ /** {@inheritDoc} */
+ public void info(final Object message, final Throwable t) {
+ }
+
+ /** {@inheritDoc} */
+ public boolean isAttached(Appender appender) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isDebugEnabled() {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isEnabledFor(final Priority level) {
+ return false;
+ }
+
+ /** {@inheritDoc} */
+ public boolean isInfoEnabled() {
+ return false;
+ }
+
+
+ /** {@inheritDoc} */
+ public void l7dlog(final Priority priority, final String key, final Throwable t) {
+ }
+
+ /** {@inheritDoc} */
+ public void l7dlog(final Priority priority, final String key, final Object[] params, final Throwable t) {
+ }
+
+ /** {@inheritDoc} */
+ public void log(final Priority priority, final Object message, final Throwable t) {
+ }
+
+ /** {@inheritDoc} */
+ public void log(final Priority priority, final Object message) {
+ }
+
+ /** {@inheritDoc} */
+ public void log(final String callerFQCN, final Priority level, final Object message, final Throwable t) {
+ }
+
+ /** {@inheritDoc} */
+ public void removeAllAppenders() {
+ }
+
+
+ /** {@inheritDoc} */
+ public void removeAppender(Appender appender) {
+ }
+
+ /** {@inheritDoc} */
+ public void removeAppender(final String name) {
+ }
+
+ /** {@inheritDoc} */
+ public void setLevel(final Level level) {
+ }
+
+
+ /** {@inheritDoc} */
+ public void setPriority(final Priority priority) {
+ }
+
+ /** {@inheritDoc} */
+ public void setResourceBundle(final ResourceBundle bundle) {
+ }
+
+ /** {@inheritDoc} */
+ public void warn(final Object message) {
+ }
+
+ /** {@inheritDoc} */
+ public void warn(final Object message, final Throwable t) {
+ }
+
+ /** {@inheritDoc} */
+ public void trace(Object message) {
+ }
+
+ /** {@inheritDoc} */
+ public void trace(Object message, Throwable t) {
+ }
+
+ /** {@inheritDoc} */
+ public boolean isTraceEnabled() {
+ return false;
+ }
+
+
+}
diff --git a/java/src/org/apache/log4j/spi/NOPLoggerRepository.java b/java/src/org/apache/log4j/spi/NOPLoggerRepository.java
new file mode 100644
index 0000000..bdb6ed9
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/NOPLoggerRepository.java
@@ -0,0 +1,131 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Category;
+import org.apache.log4j.Logger;
+import org.apache.log4j.Appender;
+
+import java.util.Enumeration;
+import java.util.Vector;
+
+/**
+ * No-operation implementation of LoggerRepository which is used when
+ * LogManager.repositorySelector is erroneously nulled during class reloading.
+ * @since 1.2.15
+ */
+public final class NOPLoggerRepository implements LoggerRepository {
+ /**
+ * {@inheritDoc}
+ */
+ public void addHierarchyEventListener(final HierarchyEventListener listener) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isDisabled(final int level) {
+ return true;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setThreshold(final Level level) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void setThreshold(final String val) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void emitNoAppenderWarning(final Category cat) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Level getThreshold() {
+ return Level.OFF;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Logger getLogger(final String name) {
+ return new NOPLogger(this, name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Logger getLogger(final String name, final LoggerFactory factory) {
+ return new NOPLogger(this, name);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Logger getRootLogger() {
+ return new NOPLogger(this, "root");
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Logger exists(final String name) {
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void shutdown() {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Enumeration getCurrentLoggers() {
+ return new Vector().elements();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Enumeration getCurrentCategories() {
+ return getCurrentLoggers();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public void fireAddAppenderEvent(Category logger, Appender appender) {
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void resetConfiguration() {
+ }
+}
diff --git a/java/src/org/apache/log4j/spi/NullWriter.java b/java/src/org/apache/log4j/spi/NullWriter.java
new file mode 100644
index 0000000..a578910
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/NullWriter.java
@@ -0,0 +1,39 @@
+/*
+ * 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.log4j.spi;
+import java.io.Writer;
+
+/**
+ * NullWriter is an obsolete class provided only for
+ * binary compatibility with earlier versions of log4j and should not be used.
+ *
+ * @deprecated
+ */
+class NullWriter extends Writer {
+
+ public void close() {
+ // blank
+ }
+
+ public void flush() {
+ // blank
+ }
+
+ public void write(char[] cbuf, int off, int len) {
+ // blank
+ }
+}
diff --git a/java/src/org/apache/log4j/spi/OptionHandler.java b/java/src/org/apache/log4j/spi/OptionHandler.java
new file mode 100644
index 0000000..2c90226
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/OptionHandler.java
@@ -0,0 +1,62 @@
+/*
+ * 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.log4j.spi;
+
+
+/**
+ A string based interface to configure package components.
+
+ @author Ceki Gülcü
+ @author Anders Kristensen
+ @since 0.8.1
+ */
+public interface OptionHandler {
+
+ /**
+ Activate the options that were previously set with calls to option
+ setters.
+
+
This allows to defer activiation of the options until all
+ options have been set. This is required for components which have
+ related options that remain ambigous until all are set.
+
+
For example, the FileAppender has the {@link
+ org.apache.log4j.FileAppender#setFile File} and {@link
+ org.apache.log4j.FileAppender#setAppend Append} options both of
+ which are ambigous until the other is also set. */
+ void activateOptions();
+
+ /**
+ Return list of strings that the OptionHandler instance recognizes.
+
+ @deprecated We now use JavaBeans style getters/setters.
+ */
+ // String[] getOptionStrings();
+
+ /**
+ Set option to value.
+
+
The handling of each option depends on the OptionHandler
+ instance. Some options may become active immediately whereas
+ other may be activated only when {@link #activateOptions} is
+ called.
+
+ @deprecated We now use JavaBeans style getters/setters.
+ */
+ //void setOption(String option, String value);
+}
diff --git a/java/src/org/apache/log4j/spi/RendererSupport.java b/java/src/org/apache/log4j/spi/RendererSupport.java
new file mode 100644
index 0000000..9d69faa
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/RendererSupport.java
@@ -0,0 +1,33 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.or.ObjectRenderer;
+import org.apache.log4j.or.RendererMap;
+
+
+public interface RendererSupport {
+
+ public
+ RendererMap getRendererMap();
+
+ public
+ void setRenderer(Class renderedClass, ObjectRenderer renderer);
+
+}
diff --git a/java/src/org/apache/log4j/spi/RepositorySelector.java b/java/src/org/apache/log4j/spi/RepositorySelector.java
new file mode 100644
index 0000000..9a70d62
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/RepositorySelector.java
@@ -0,0 +1,49 @@
+/*
+ * 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.log4j.spi;
+
+
+/**
+
+ The LogManager uses one (and only one)
+ RepositorySelector implementation to select the
+ {@link LoggerRepository} for a particular application context.
+
+
It is the responsability of the RepositorySelector
+ implementation to track the application context. Log4j makes no
+ assumptions about the application context or on its management.
+
+
See also {@link org.apache.log4j.LogManager LogManager}.
+
+ @author Ceki Gülcü
+ @since 1.2
+
+ */
+public interface RepositorySelector {
+
+ /**
+ Returns a {@link LoggerRepository} depending on the
+ context. Implementors must make sure that a valid (non-null)
+ LoggerRepository is returned.
+ */
+ public
+ LoggerRepository getLoggerRepository();
+}
+
diff --git a/java/src/org/apache/log4j/spi/RootCategory.java b/java/src/org/apache/log4j/spi/RootCategory.java
new file mode 100644
index 0000000..9954791
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/RootCategory.java
@@ -0,0 +1,76 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.LogLog;
+
+// Contibutors: Mathias Bogaert
+
+/**
+ * @deprecated Replaced by {@link RootLogger}.
+ */
+final public class RootCategory extends Logger {
+
+ /**
+ The root category names itself as "root". However, the root
+ category cannot be retrieved by name.
+ */
+ public
+ RootCategory(Level level) {
+ super("root");
+ setLevel(level);
+ }
+
+
+ /**
+ Return the assigned level value without walking the category
+ hierarchy.
+ */
+ final
+ public
+ Level getChainedLevel() {
+ return level;
+ }
+
+ /**
+ Setting a null value to the level of the root category may have catastrophic
+ results. We prevent this here.
+
+ @since 0.8.3 */
+ final
+ public
+ void setLevel(Level level) {
+ if(level == null) {
+ LogLog.error("You have tried to set a null level to root.",
+ new Throwable());
+ }
+ else {
+ this.level = level;
+ }
+ }
+
+ final
+ public
+ void setPriority(Level level) {
+ setLevel(level);
+ }
+
+
+}
diff --git a/java/src/org/apache/log4j/spi/RootLogger.java b/java/src/org/apache/log4j/spi/RootLogger.java
new file mode 100644
index 0000000..a9ffd74
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/RootLogger.java
@@ -0,0 +1,71 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.LogLog;
+
+
+// Contibutors: Mathias Bogaert
+
+/**
+ RootLogger sits at the top of the logger hierachy. It is a
+ regular logger except that it provides several guarantees.
+
+
First, it cannot be assigned a null
+ level. Second, since root logger cannot have a parent, the
+ {@link #getChainedLevel} method always returns the value of the
+ level field without walking the hierarchy.
+
+ @author Ceki Gülcü
+
+ */
+public final class RootLogger extends Logger {
+ /**
+ The root logger names itself as "root". However, the root
+ logger cannot be retrieved by name.
+ */
+ public RootLogger(Level level) {
+ super("root");
+ setLevel(level);
+ }
+
+ /**
+ Return the assigned level value without walking the logger
+ hierarchy.
+ */
+ public final Level getChainedLevel() {
+ return level;
+ }
+
+ /**
+ Setting a null value to the level of the root logger may have catastrophic
+ results. We prevent this here.
+
+ @since 0.8.3 */
+ public final void setLevel(Level level) {
+ if (level == null) {
+ LogLog.error(
+ "You have tried to set a null level to root.", new Throwable());
+ } else {
+ this.level = level;
+ }
+ }
+
+}
diff --git a/java/src/org/apache/log4j/spi/ThrowableInformation.java b/java/src/org/apache/log4j/spi/ThrowableInformation.java
new file mode 100644
index 0000000..033f18b
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/ThrowableInformation.java
@@ -0,0 +1,96 @@
+/*
+ * 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.log4j.spi;
+
+import org.apache.log4j.Category;
+import org.apache.log4j.DefaultThrowableRenderer;
+
+/**
+ * ThrowableInformation is log4j's internal representation of
+ * throwables. It essentially consists of a string array, called
+ * 'rep', where the first element, that is rep[0], represents the
+ * string representation of the throwable (i.e. the value you get
+ * when you do throwable.toString()) and subsequent elements
+ * correspond the stack trace with the top most entry of the stack
+ * corresponding to the second entry of the 'rep' array that is
+ * rep[1].
+ *
+ * @author Ceki Gülcü
+ *
+ * */
+public class ThrowableInformation implements java.io.Serializable {
+
+ static final long serialVersionUID = -4748765566864322735L;
+
+ private transient Throwable throwable;
+ private transient Category category;
+ private String[] rep;
+
+ public
+ ThrowableInformation(Throwable throwable) {
+ this.throwable = throwable;
+ }
+
+ /**
+ * Create a new instance.
+ * @param throwable throwable, may not be null.
+ * @param category category used to obtain ThrowableRenderer, may be null.
+ * @since 1.2.16
+ */
+ public ThrowableInformation(Throwable throwable, Category category) {
+ this.throwable = throwable;
+ this.category = category;
+ }
+
+ /**
+ * Create new instance.
+ * @since 1.2.15
+ * @param r String representation of throwable.
+ */
+ public ThrowableInformation(final String[] r) {
+ if (r != null) {
+ rep = (String[]) r.clone();
+ }
+ }
+
+
+ public
+ Throwable getThrowable() {
+ return throwable;
+ }
+
+ public synchronized String[] getThrowableStrRep() {
+ if(rep == null) {
+ ThrowableRenderer renderer = null;
+ if (category != null) {
+ LoggerRepository repo = category.getLoggerRepository();
+ if (repo instanceof ThrowableRendererSupport) {
+ renderer = ((ThrowableRendererSupport) repo).getThrowableRenderer();
+ }
+ }
+ if (renderer == null) {
+ rep = DefaultThrowableRenderer.render(throwable);
+ } else {
+ rep = renderer.doRender(throwable);
+ }
+ }
+ return (String[]) rep.clone();
+ }
+}
+
+
diff --git a/java/src/org/apache/log4j/spi/ThrowableRenderer.java b/java/src/org/apache/log4j/spi/ThrowableRenderer.java
new file mode 100644
index 0000000..1b83db2
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/ThrowableRenderer.java
@@ -0,0 +1,33 @@
+/*
+ * 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.log4j.spi;
+
+/**
+ * Implemented by classes that render instances of
+ * java.lang.Throwable (exceptions and errors)
+ * into a string representation.
+ *
+ * @since 1.2.16
+ */
+public interface ThrowableRenderer {
+ /**
+ * Render Throwable.
+ * @param t throwable, may not be null.
+ * @return String representation.
+ */
+ public String[] doRender(Throwable t);
+}
diff --git a/java/src/org/apache/log4j/spi/ThrowableRendererSupport.java b/java/src/org/apache/log4j/spi/ThrowableRendererSupport.java
new file mode 100644
index 0000000..08f02a0
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/ThrowableRendererSupport.java
@@ -0,0 +1,37 @@
+/*
+ * 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.log4j.spi;
+
+/**
+ * Implemented by logger repositories that support configurable
+ * rendering of Throwables.
+ *
+ * @since 1.2.16
+ */
+public interface ThrowableRendererSupport {
+ /**
+ * Get throwable renderer.
+ * @return throwable renderer, may be null.
+ */
+ ThrowableRenderer getThrowableRenderer();
+
+ /**
+ * Set throwable renderer.
+ * @param renderer renderer, may be null.
+ */
+ void setThrowableRenderer(ThrowableRenderer renderer);
+}
diff --git a/java/src/org/apache/log4j/spi/TriggeringEventEvaluator.java b/java/src/org/apache/log4j/spi/TriggeringEventEvaluator.java
new file mode 100644
index 0000000..67f4fcd
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/TriggeringEventEvaluator.java
@@ -0,0 +1,40 @@
+/*
+ * 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.log4j.spi;
+
+/**
+
+ Implementions of this interface allow certain appenders to decide
+ when to perform an appender specific action.
+
+
For example the {@link org.apache.log4j.net.SMTPAppender} sends
+ an email when the {@link #isTriggeringEvent} method returns
+ true and adds the event to an internal buffer when the
+ returned result is false.
+
+ @author Ceki Gülcü
+ @since version 1.0
+
+ */
+public interface TriggeringEventEvaluator {
+
+ /**
+ Is this the triggering event?
+ */
+ public boolean isTriggeringEvent(LoggingEvent event);
+}
diff --git a/java/src/org/apache/log4j/spi/VectorWriter.java b/java/src/org/apache/log4j/spi/VectorWriter.java
new file mode 100644
index 0000000..3e28860
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/VectorWriter.java
@@ -0,0 +1,94 @@
+/*
+ * 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.log4j.spi;
+
+import java.io.PrintWriter;
+import java.util.Vector;
+
+/**
+ * VectorWriter is an obsolete class provided only for
+ * binary compatibility with earlier versions of log4j and should not be used.
+ *
+ * @deprecated
+ */
+class VectorWriter extends PrintWriter {
+
+ private Vector v;
+
+ /**
+ * @deprecated
+ */
+ VectorWriter() {
+ super(new NullWriter());
+ v = new Vector();
+ }
+
+ public void print(Object o) {
+ v.addElement(String.valueOf(o));
+ }
+
+ public void print(char[] chars) {
+ v.addElement(new String(chars));
+ }
+
+ public void print(String s) {
+ v.addElement(s);
+ }
+
+ public void println(Object o) {
+ v.addElement(String.valueOf(o));
+ }
+
+ // JDK 1.1.x apprenly uses this form of println while in
+ // printStackTrace()
+ public
+ void println(char[] chars) {
+ v.addElement(new String(chars));
+ }
+
+ public
+ void println(String s) {
+ v.addElement(s);
+ }
+
+ public void write(char[] chars) {
+ v.addElement(new String(chars));
+ }
+
+ public void write(char[] chars, int off, int len) {
+ v.addElement(new String(chars, off, len));
+ }
+
+ public void write(String s, int off, int len) {
+ v.addElement(s.substring(off, off+len));
+ }
+
+ public void write(String s) {
+ v.addElement(s);
+ }
+
+ public String[] toStringArray() {
+ int len = v.size();
+ String[] sa = new String[len];
+ for(int i = 0; i < len; i++) {
+ sa[i] = (String) v.elementAt(i);
+ }
+ return sa;
+ }
+
+}
+
diff --git a/java/src/org/apache/log4j/spi/package.html b/java/src/org/apache/log4j/spi/package.html
new file mode 100644
index 0000000..1205833
--- /dev/null
+++ b/java/src/org/apache/log4j/spi/package.html
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+Contains part of the System Programming Interface (SPI) needed to
+extend log4j.
+
+
+
+
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/varia/DenyAllFilter.java b/java/src/org/apache/log4j/varia/DenyAllFilter.java
new file mode 100644
index 0000000..6c9e949
--- /dev/null
+++ b/java/src/org/apache/log4j/varia/DenyAllFilter.java
@@ -0,0 +1,72 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ This filter drops all logging events.
+
+ You can add this filter to the end of a filter chain to
+ switch from the default "accept all unless instructed otherwise"
+ filtering behaviour to a "deny all unless instructed otherwise"
+ behaviour.
+
+
+ @author Ceki Gülcü
+
+ @since 0.9.0 */
+public class DenyAllFilter extends Filter {
+
+ /**
+ Returns null as there are no options.
+
+ @deprecated We now use JavaBeans introspection to configure
+ components. Options strings are no longer needed.
+ */
+ public
+ String[] getOptionStrings() {
+ return null;
+ }
+
+
+ /**
+ No options to set.
+
+ @deprecated Use the setter method for the option directly instead
+ of the generic setOption method.
+ */
+ public
+ void setOption(String key, String value) {
+ }
+
+ /**
+ Always returns the integer constant {@link Filter#DENY}
+ regardless of the {@link LoggingEvent} parameter.
+
+ @param event The LoggingEvent to filter.
+ @return Always returns {@link Filter#DENY}.
+ */
+ public
+ int decide(LoggingEvent event) {
+ return Filter.DENY;
+ }
+}
+
diff --git a/java/src/org/apache/log4j/varia/ExternallyRolledFileAppender.java b/java/src/org/apache/log4j/varia/ExternallyRolledFileAppender.java
new file mode 100644
index 0000000..26e7d84
--- /dev/null
+++ b/java/src/org/apache/log4j/varia/ExternallyRolledFileAppender.java
@@ -0,0 +1,189 @@
+/*
+ * 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.log4j.varia;
+
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.ServerSocket;
+import java.net.Socket;
+
+import org.apache.log4j.RollingFileAppender;
+import org.apache.log4j.helpers.LogLog;
+
+/**
+ This appender listens on a socket on the port specified by the
+ Port property for a "RollOver" message. When such a message
+ is received, the underlying log file is rolled over and an
+ acknowledgment message is sent back to the process initiating the
+ roll over.
+
+
This method of triggering roll over has the advantage of being
+ operating system independent, fast and reliable.
+
+
A simple application {@link Roller} is provided to initiate the
+ roll over.
+
+
Note that the initiator is not authenticated. Anyone can trigger
+ a rollover. In production environments, it is recommended that you
+ add some form of protection to prevent undesired rollovers.
+
+
+ @author Ceki Gülcü
+ @since version 0.9.0 */
+public class ExternallyRolledFileAppender extends RollingFileAppender {
+
+ /**
+ The string constant sent to initiate a roll over. Current value of
+ this string constant is RollOver.
+ */
+ static final public String ROLL_OVER = "RollOver";
+
+ /**
+ The string constant sent to acknowledge a roll over. Current value of
+ this string constant is OK.
+ */
+ static final public String OK = "OK";
+
+ int port = 0;
+ HUP hup;
+
+ /**
+ The default constructor does nothing but calls its super-class
+ constructor. */
+ public
+ ExternallyRolledFileAppender() {
+ }
+
+ /**
+ The Port [roperty is used for setting the port for
+ listening to external roll over messages.
+ */
+ public
+ void setPort(int port) {
+ this.port = port;
+ }
+
+ /**
+ Returns value of the Port option.
+ */
+ public
+ int getPort() {
+ return port;
+ }
+
+ /**
+ Start listening on the port specified by a preceding call to
+ {@link #setPort}. */
+ public
+ void activateOptions() {
+ super.activateOptions();
+ if(port != 0) {
+ if(hup != null) {
+ hup.interrupt();
+ }
+ hup = new HUP(this, port);
+ hup.setDaemon(true);
+ hup.start();
+ }
+ }
+}
+
+
+class HUP extends Thread {
+
+ int port;
+ ExternallyRolledFileAppender er;
+
+ HUP(ExternallyRolledFileAppender er, int port) {
+ this.er = er;
+ this.port = port;
+ }
+
+ public
+ void run() {
+ while(!isInterrupted()) {
+ try {
+ ServerSocket serverSocket = new ServerSocket(port);
+ while(true) {
+ Socket socket = serverSocket.accept();
+ LogLog.debug("Connected to client at " + socket.getInetAddress());
+ new Thread(new HUPNode(socket, er), "ExternallyRolledFileAppender-HUP").start();
+ }
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ e.printStackTrace();
+ } catch(IOException e) {
+ e.printStackTrace();
+ } catch(RuntimeException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
+
+class HUPNode implements Runnable {
+
+ Socket socket;
+ DataInputStream dis;
+ DataOutputStream dos;
+ ExternallyRolledFileAppender er;
+
+ public
+ HUPNode(Socket socket, ExternallyRolledFileAppender er) {
+ this.socket = socket;
+ this.er = er;
+ try {
+ dis = new DataInputStream(socket.getInputStream());
+ dos = new DataOutputStream(socket.getOutputStream());
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ e.printStackTrace();
+ } catch(IOException e) {
+ e.printStackTrace();
+ } catch(RuntimeException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public void run() {
+ try {
+ String line = dis.readUTF();
+ LogLog.debug("Got external roll over signal.");
+ if(ExternallyRolledFileAppender.ROLL_OVER.equals(line)) {
+ synchronized(er) {
+ er.rollOver();
+ }
+ dos.writeUTF(ExternallyRolledFileAppender.OK);
+ }
+ else {
+ dos.writeUTF("Expecting [RollOver] string.");
+ }
+ dos.close();
+ } catch(InterruptedIOException e) {
+ Thread.currentThread().interrupt();
+ LogLog.error("Unexpected exception. Exiting HUPNode.", e);
+ } catch(IOException e) {
+ LogLog.error("Unexpected exception. Exiting HUPNode.", e);
+ } catch(RuntimeException e) {
+ LogLog.error("Unexpected exception. Exiting HUPNode.", e);
+ }
+ }
+}
+
diff --git a/java/src/org/apache/log4j/varia/FallbackErrorHandler.java b/java/src/org/apache/log4j/varia/FallbackErrorHandler.java
new file mode 100644
index 0000000..7cbc87d
--- /dev/null
+++ b/java/src/org/apache/log4j/varia/FallbackErrorHandler.java
@@ -0,0 +1,138 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.Appender;
+import org.apache.log4j.Logger;
+import org.apache.log4j.helpers.LogLog;
+import java.util.Vector;
+import java.io.InterruptedIOException;
+
+/**
+ *
+ * The FallbackErrorHandler implements the ErrorHandler
+ * interface such that a secondary appender may be specified. This
+ * secondary appender takes over if the primary appender fails for
+ * whatever reason.
+ *
+ *
The error message is printed on System.err, and
+ * logged in the new secondary appender.
+ *
+ * @author Ceki Gücü
+ * */
+public class FallbackErrorHandler implements ErrorHandler {
+
+
+ Appender backup;
+ Appender primary;
+ Vector loggers;
+
+ public FallbackErrorHandler() {
+ }
+
+
+ /**
+ Adds the logger passed as parameter to the list of
+ loggers that we need to search for in case of appender failure.
+ */
+ public
+ void setLogger(Logger logger) {
+ LogLog.debug("FB: Adding logger [" + logger.getName() + "].");
+ if(loggers == null) {
+ loggers = new Vector();
+ }
+ loggers.addElement(logger);
+ }
+
+
+ /**
+ No options to activate.
+ */
+ public
+ void activateOptions() {
+ }
+
+
+ /**
+ Prints the message and the stack trace of the exception on
+ System.err. */
+ public
+ void error(String message, Exception e, int errorCode) {
+ error(message, e, errorCode, null);
+ }
+
+ /**
+ Prints the message and the stack trace of the exception on
+ System.err.
+ */
+ public
+ void error(String message, Exception e, int errorCode, LoggingEvent event) {
+ if (e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.debug("FB: The following error reported: " + message, e);
+ LogLog.debug("FB: INITIATING FALLBACK PROCEDURE.");
+ if (loggers != null) {
+ for(int i = 0; i < loggers.size(); i++) {
+ Logger l = (Logger) loggers.elementAt(i);
+ LogLog.debug("FB: Searching for ["+primary.getName()+"] in logger ["
+ +l.getName() + "].");
+ LogLog.debug("FB: Replacing ["+primary.getName()+"] by ["
+ + backup.getName() + "] in logger ["+ l.getName() +"].");
+ l.removeAppender(primary);
+ LogLog.debug("FB: Adding appender ["+backup.getName()+"] to logger "
+ + l.getName());
+ l.addAppender(backup);
+ }
+ }
+ }
+
+
+ /**
+ Print a the error message passed as parameter on
+ System.err.
+ */
+ public
+ void error(String message) {
+ //if(firstTime) {
+ //LogLog.error(message);
+ //firstTime = false;
+ //}
+ }
+
+ /**
+ The appender to which this error handler is attached.
+ */
+ public
+ void setAppender(Appender primary) {
+ LogLog.debug("FB: Setting primary appender to [" + primary.getName() + "].");
+ this.primary = primary;
+ }
+
+ /**
+ Set the backup appender.
+ */
+ public
+ void setBackupAppender(Appender backup) {
+ LogLog.debug("FB: Setting backup appender to [" + backup.getName() + "].");
+ this.backup = backup;
+ }
+
+}
diff --git a/java/src/org/apache/log4j/varia/LevelMatchFilter.java b/java/src/org/apache/log4j/varia/LevelMatchFilter.java
new file mode 100644
index 0000000..832ca29
--- /dev/null
+++ b/java/src/org/apache/log4j/varia/LevelMatchFilter.java
@@ -0,0 +1,104 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.helpers.OptionConverter;
+
+/**
+ This is a very simple filter based on level matching.
+
+
The filter admits two options LevelToMatch and
+ AcceptOnMatch. If there is an exact match between the value
+ of the LevelToMatch option and the level of the {@link
+ LoggingEvent}, then the {@link #decide} method returns {@link
+ Filter#ACCEPT} in case the AcceptOnMatch option value is set
+ to true, if it is false then {@link
+ Filter#DENY} is returned. If there is no match, {@link
+ Filter#NEUTRAL} is returned.
+
+ @author Ceki Gülcü
+
+ @since 1.2 */
+public class LevelMatchFilter extends Filter {
+
+ /**
+ Do we return ACCEPT when a match occurs. Default is
+ true. */
+ boolean acceptOnMatch = true;
+
+ /**
+ */
+ Level levelToMatch;
+
+
+ public
+ void setLevelToMatch(String level) {
+ levelToMatch = OptionConverter.toLevel(level, null);
+ }
+
+ public
+ String getLevelToMatch() {
+ return levelToMatch == null ? null : levelToMatch.toString();
+ }
+
+ public
+ void setAcceptOnMatch(boolean acceptOnMatch) {
+ this.acceptOnMatch = acceptOnMatch;
+ }
+
+ public
+ boolean getAcceptOnMatch() {
+ return acceptOnMatch;
+ }
+
+
+ /**
+ Return the decision of this filter.
+
+ Returns {@link Filter#NEUTRAL} if the LevelToMatch option
+ is not set or if there is not match. Otherwise, if there is a
+ match, then the returned decision is {@link Filter#ACCEPT} if the
+ AcceptOnMatch property is set to true. The
+ returned decision is {@link Filter#DENY} if the
+ AcceptOnMatch property is set to false.
+
+ */
+ public
+ int decide(LoggingEvent event) {
+ if(this.levelToMatch == null) {
+ return Filter.NEUTRAL;
+ }
+
+ boolean matchOccured = false;
+ if(this.levelToMatch.equals(event.getLevel())) {
+ matchOccured = true;
+ }
+
+ if(matchOccured) {
+ if(this.acceptOnMatch)
+ return Filter.ACCEPT;
+ else
+ return Filter.DENY;
+ } else {
+ return Filter.NEUTRAL;
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/varia/LevelRangeFilter.java b/java/src/org/apache/log4j/varia/LevelRangeFilter.java
new file mode 100644
index 0000000..9dfacb1
--- /dev/null
+++ b/java/src/org/apache/log4j/varia/LevelRangeFilter.java
@@ -0,0 +1,144 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ This is a very simple filter based on level matching, which can be
+ used to reject messages with priorities outside a certain range.
+
+
The filter admits three options LevelMin, LevelMax
+ and AcceptOnMatch.
+
+
If the level of the {@link LoggingEvent} is not between Min and Max
+ (inclusive), then {@link Filter#DENY} is returned.
+
+
If the Logging event level is within the specified range, then if
+ AcceptOnMatch is true, {@link Filter#ACCEPT} is returned, and if
+ AcceptOnMatch is false, {@link Filter#NEUTRAL} is returned.
+
+
If LevelMinw is not defined, then there is no
+ minimum acceptable level (ie a level is never rejected for
+ being too "low"/unimportant). If LevelMax is not
+ defined, then there is no maximum acceptable level (ie a
+ level is never rejected for beeing too "high"/important).
+
+
Refer to the {@link
+ org.apache.log4j.AppenderSkeleton#setThreshold setThreshold} method
+ available to all appenders extending {@link
+ org.apache.log4j.AppenderSkeleton} for a more convenient way to
+ filter out events by level.
+
+ @author Simon Kitching
+ @author based on code by Ceki Gülcü
+*/
+public class LevelRangeFilter extends Filter {
+
+ /**
+ Do we return ACCEPT when a match occurs. Default is
+ false, so that later filters get run by default */
+ boolean acceptOnMatch = false;
+
+ Level levelMin;
+ Level levelMax;
+
+
+ /**
+ Return the decision of this filter.
+ */
+ public
+ int decide(LoggingEvent event) {
+ if(this.levelMin != null) {
+ if (event.getLevel().isGreaterOrEqual(levelMin) == false) {
+ // level of event is less than minimum
+ return Filter.DENY;
+ }
+ }
+
+ if(this.levelMax != null) {
+ if (event.getLevel().toInt() > levelMax.toInt()) {
+ // level of event is greater than maximum
+ // Alas, there is no Level.isGreater method. and using
+ // a combo of isGreaterOrEqual && !Equal seems worse than
+ // checking the int values of the level objects..
+ return Filter.DENY;
+ }
+ }
+
+ if (acceptOnMatch) {
+ // this filter set up to bypass later filters and always return
+ // accept if level in range
+ return Filter.ACCEPT;
+ }
+ else {
+ // event is ok for this filter; allow later filters to have a look..
+ return Filter.NEUTRAL;
+ }
+ }
+
+ /**
+ Get the value of the LevelMax option. */
+ public
+ Level getLevelMax() {
+ return levelMax;
+ }
+
+
+ /**
+ Get the value of the LevelMin option. */
+ public
+ Level getLevelMin() {
+ return levelMin;
+ }
+
+ /**
+ Get the value of the AcceptOnMatch option.
+ */
+ public
+ boolean getAcceptOnMatch() {
+ return acceptOnMatch;
+ }
+
+ /**
+ Set the LevelMax option.
+ */
+ public
+ void setLevelMax(Level levelMax) {
+ this.levelMax = levelMax;
+ }
+
+ /**
+ Set the LevelMin option.
+ */
+ public
+ void setLevelMin(Level levelMin) {
+ this.levelMin = levelMin;
+ }
+
+ /**
+ Set the AcceptOnMatch option.
+ */
+ public
+ void setAcceptOnMatch(boolean acceptOnMatch) {
+ this.acceptOnMatch = acceptOnMatch;
+ }
+}
+
diff --git a/java/src/org/apache/log4j/varia/NullAppender.java b/java/src/org/apache/log4j/varia/NullAppender.java
new file mode 100644
index 0000000..778b024
--- /dev/null
+++ b/java/src/org/apache/log4j/varia/NullAppender.java
@@ -0,0 +1,79 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.AppenderSkeleton;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * A NullAppender merely exists, it never outputs a message to any
+ * device.
+ * @author Ceki Gülc¨
+ */
+public class NullAppender extends AppenderSkeleton {
+
+ private static NullAppender instance = new NullAppender();
+
+ public NullAppender() {
+ }
+
+ /**
+ * There are no options to acticate.
+ * */
+ public void activateOptions() {
+ }
+
+ /**
+ * Whenever you can, use this method to retreive an instance instead
+ * of instantiating a new one with new.
+ * @deprecated Use getNullAppender instead. getInstance should have been static.
+ * */
+ public NullAppender getInstance() {
+ return instance;
+ }
+
+ /**
+ * Whenever you can, use this method to retreive an instance instead
+ * of instantiating a new one with new.
+ * */
+ public static NullAppender getNullAppender() {
+ return instance;
+ }
+
+ public void close() {
+ }
+
+ /**
+ * Does not do anything.
+ * */
+ public void doAppend(LoggingEvent event) {
+ }
+
+ /**
+ * Does not do anything.
+ * */
+ protected void append(LoggingEvent event) {
+ }
+
+ /**
+ * NullAppenders do not need a layout.
+ * */
+ public boolean requiresLayout() {
+ return false;
+ }
+}
diff --git a/java/src/org/apache/log4j/varia/ReloadingPropertyConfigurator.java b/java/src/org/apache/log4j/varia/ReloadingPropertyConfigurator.java
new file mode 100644
index 0000000..b29e847
--- /dev/null
+++ b/java/src/org/apache/log4j/varia/ReloadingPropertyConfigurator.java
@@ -0,0 +1,38 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.PropertyConfigurator;
+import org.apache.log4j.spi.Configurator;
+import java.net.URL;
+import org.apache.log4j.spi.LoggerRepository;
+
+public class ReloadingPropertyConfigurator implements Configurator {
+
+
+ PropertyConfigurator delegate = new PropertyConfigurator();
+
+
+ public ReloadingPropertyConfigurator() {
+ }
+
+ public
+ void doConfigure(URL url, LoggerRepository repository) {
+ }
+
+}
diff --git a/java/src/org/apache/log4j/varia/Roller.java b/java/src/org/apache/log4j/varia/Roller.java
new file mode 100644
index 0000000..7ef3aee
--- /dev/null
+++ b/java/src/org/apache/log4j/varia/Roller.java
@@ -0,0 +1,111 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.Logger;
+import org.apache.log4j.BasicConfigurator;
+
+import java.io.IOException;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.net.Socket;
+
+/**
+ A simple application to send roll over messages to a potentially
+ remote {@link ExternallyRolledFileAppender}.
+
+
It takes two arguments, the host_name and
+ port_number where the
+ ExternallyRolledFileAppender is listening.
+
+
+ @author Ceki Gülcü
+ @since version 0.9.0 */
+public class Roller {
+
+ static Logger cat = Logger.getLogger(Roller.class);
+
+
+ static String host;
+ static int port;
+
+ // Static class.
+ Roller() {
+ }
+
+ /**
+ Send a "RollOver" message to
+ ExternallyRolledFileAppender on host
+ and port.
+
+ */
+ public
+ static
+ void main(String argv[]) {
+
+ BasicConfigurator.configure();
+
+ if(argv.length == 2)
+ init(argv[0], argv[1]);
+ else
+ usage("Wrong number of arguments.");
+
+ roll();
+ }
+
+ static
+ void usage(String msg) {
+ System.err.println(msg);
+ System.err.println( "Usage: java " + Roller.class.getName() +
+ "host_name port_number");
+ System.exit(1);
+ }
+
+ static
+ void init(String hostArg, String portArg) {
+ host = hostArg;
+ try {
+ port = Integer.parseInt(portArg);
+ }
+ catch(java.lang.NumberFormatException e) {
+ usage("Second argument "+portArg+" is not a valid integer.");
+ }
+ }
+
+ static
+ void roll() {
+ try {
+ Socket socket = new Socket(host, port);
+ DataOutputStream dos = new DataOutputStream(socket.getOutputStream());
+ DataInputStream dis = new DataInputStream(socket.getInputStream());
+ dos.writeUTF(ExternallyRolledFileAppender.ROLL_OVER);
+ String rc = dis.readUTF();
+ if(ExternallyRolledFileAppender.OK.equals(rc)) {
+ cat.info("Roll over signal acknowledged by remote appender.");
+ } else {
+ cat.warn("Unexpected return code "+rc+" from remote entity.");
+ System.exit(2);
+ }
+ } catch(IOException e) {
+ cat.error("Could not send roll signal on host "+host+" port "+port+" .",
+ e);
+ System.exit(2);
+ }
+ System.exit(0);
+ }
+}
diff --git a/java/src/org/apache/log4j/varia/StringMatchFilter.java b/java/src/org/apache/log4j/varia/StringMatchFilter.java
new file mode 100644
index 0000000..6ee7ec4
--- /dev/null
+++ b/java/src/org/apache/log4j/varia/StringMatchFilter.java
@@ -0,0 +1,121 @@
+/*
+ * 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.log4j.varia;
+
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.helpers.OptionConverter;
+
+/**
+ * This is a very simple filter based on string matching.
+ *
+ *
The filter admits two options StringToMatch and
+ * AcceptOnMatch. If there is a match between the value of the
+ * StringToMatch option and the message of the {@link org.apache.log4j.spi.LoggingEvent},
+ * then the {@link #decide(LoggingEvent)} method returns {@link org.apache.log4j.spi.Filter#ACCEPT} if
+ * the AcceptOnMatch option value is true, if it is false then
+ * {@link org.apache.log4j.spi.Filter#DENY} is returned. If there is no match, {@link
+ * org.apache.log4j.spi.Filter#NEUTRAL} is returned.
+ *
+ * @author Ceki Gülcü
+ * @since 0.9.0
+ */
+public class StringMatchFilter extends Filter {
+
+ /**
+ @deprecated Options are now handled using the JavaBeans paradigm.
+ This constant is not longer needed and will be removed in the
+ near term.
+ */
+ public static final String STRING_TO_MATCH_OPTION = "StringToMatch";
+
+ /**
+ @deprecated Options are now handled using the JavaBeans paradigm.
+ This constant is not longer needed and will be removed in the
+ near term.
+ */
+ public static final String ACCEPT_ON_MATCH_OPTION = "AcceptOnMatch";
+
+ boolean acceptOnMatch = true;
+ String stringToMatch;
+
+ /**
+ @deprecated We now use JavaBeans introspection to configure
+ components. Options strings are no longer needed.
+ */
+ public
+ String[] getOptionStrings() {
+ return new String[] {STRING_TO_MATCH_OPTION, ACCEPT_ON_MATCH_OPTION};
+ }
+
+ /**
+ @deprecated Use the setter method for the option directly instead
+ of the generic setOption method.
+ */
+ public
+ void setOption(String key, String value) {
+
+ if(key.equalsIgnoreCase(STRING_TO_MATCH_OPTION)) {
+ stringToMatch = value;
+ } else if (key.equalsIgnoreCase(ACCEPT_ON_MATCH_OPTION)) {
+ acceptOnMatch = OptionConverter.toBoolean(value, acceptOnMatch);
+ }
+ }
+
+ public
+ void setStringToMatch(String s) {
+ stringToMatch = s;
+ }
+
+ public
+ String getStringToMatch() {
+ return stringToMatch;
+ }
+
+ public
+ void setAcceptOnMatch(boolean acceptOnMatch) {
+ this.acceptOnMatch = acceptOnMatch;
+ }
+
+ public
+ boolean getAcceptOnMatch() {
+ return acceptOnMatch;
+ }
+
+ /**
+ Returns {@link Filter#NEUTRAL} is there is no string match.
+ */
+ public
+ int decide(LoggingEvent event) {
+ String msg = event.getRenderedMessage();
+
+ if(msg == null || stringToMatch == null)
+ return Filter.NEUTRAL;
+
+
+ if( msg.indexOf(stringToMatch) == -1 ) {
+ return Filter.NEUTRAL;
+ } else { // we've got a match
+ if(acceptOnMatch) {
+ return Filter.ACCEPT;
+ } else {
+ return Filter.DENY;
+ }
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/varia/package.html b/java/src/org/apache/log4j/varia/package.html
new file mode 100644
index 0000000..3495db2
--- /dev/null
+++ b/java/src/org/apache/log4j/varia/package.html
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+Contains various appenders, filters and other odds and ends.
+
+
+
+
+Last modified: Tue Mar 21 20:28:14 MET 2000
+
+
diff --git a/java/src/org/apache/log4j/xml/DOMConfigurator.java b/java/src/org/apache/log4j/xml/DOMConfigurator.java
new file mode 100644
index 0000000..de2963d
--- /dev/null
+++ b/java/src/org/apache/log4j/xml/DOMConfigurator.java
@@ -0,0 +1,1123 @@
+/*
+ * 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.log4j.xml;
+
+import org.apache.log4j.Appender;
+import org.apache.log4j.Layout;
+import org.apache.log4j.Level;
+import org.apache.log4j.LogManager;
+import org.apache.log4j.Logger;
+import org.apache.log4j.config.PropertySetter;
+import org.apache.log4j.helpers.FileWatchdog;
+import org.apache.log4j.helpers.Loader;
+import org.apache.log4j.helpers.LogLog;
+import org.apache.log4j.helpers.OptionConverter;
+import org.apache.log4j.or.RendererMap;
+import org.apache.log4j.spi.AppenderAttachable;
+import org.apache.log4j.spi.Configurator;
+import org.apache.log4j.spi.ErrorHandler;
+import org.apache.log4j.spi.Filter;
+import org.apache.log4j.spi.LoggerFactory;
+import org.apache.log4j.spi.LoggerRepository;
+import org.apache.log4j.spi.RendererSupport;
+import org.apache.log4j.spi.ThrowableRenderer;
+import org.apache.log4j.spi.ThrowableRendererSupport;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+import org.xml.sax.SAXException;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+import javax.xml.parsers.FactoryConfigurationError;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InterruptedIOException;
+import java.io.Reader;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+import java.net.URL;
+import java.net.URLConnection;
+import java.util.Hashtable;
+import java.util.Properties;
+
+// Contributors: Mark Womack
+// Arun Katkere
+
+/**
+ Use this class to initialize the log4j environment using a DOM tree.
+
+ The DTD is specified in log4j.dtd.
+
+
Sometimes it is useful to see how log4j is reading configuration
+ files. You can enable log4j internal logging by defining the
+ log4j.debug variable on the java command
+ line. Alternatively, set the debug attribute in the
+ log4j:configuration element. As in
+
+ <log4j:configuration debug="true" xmlns:log4j="http://jakarta.apache.org/log4j/">
+ ...
+ </log4j:configuration>
+
+
+ There are sample XML files included in the package.
+
+ @author Christopher Taylor
+ @author Ceki Gülcü
+ @author Anders Kristensen
+
+ @since 0.8.3 */
+public class DOMConfigurator implements Configurator {
+
+ static final String CONFIGURATION_TAG = "log4j:configuration";
+ static final String OLD_CONFIGURATION_TAG = "configuration";
+ static final String RENDERER_TAG = "renderer";
+ private static final String THROWABLE_RENDERER_TAG = "throwableRenderer";
+ static final String APPENDER_TAG = "appender";
+ static final String APPENDER_REF_TAG = "appender-ref";
+ static final String PARAM_TAG = "param";
+ static final String LAYOUT_TAG = "layout";
+ static final String CATEGORY = "category";
+ static final String LOGGER = "logger";
+ static final String LOGGER_REF = "logger-ref";
+ static final String CATEGORY_FACTORY_TAG = "categoryFactory";
+ static final String LOGGER_FACTORY_TAG = "loggerFactory";
+ static final String NAME_ATTR = "name";
+ static final String CLASS_ATTR = "class";
+ static final String VALUE_ATTR = "value";
+ static final String ROOT_TAG = "root";
+ static final String ROOT_REF = "root-ref";
+ static final String LEVEL_TAG = "level";
+ static final String PRIORITY_TAG = "priority";
+ static final String FILTER_TAG = "filter";
+ static final String ERROR_HANDLER_TAG = "errorHandler";
+ static final String REF_ATTR = "ref";
+ static final String ADDITIVITY_ATTR = "additivity";
+ static final String THRESHOLD_ATTR = "threshold";
+ static final String CONFIG_DEBUG_ATTR = "configDebug";
+ static final String INTERNAL_DEBUG_ATTR = "debug";
+ private static final String RESET_ATTR = "reset";
+ static final String RENDERING_CLASS_ATTR = "renderingClass";
+ static final String RENDERED_CLASS_ATTR = "renderedClass";
+
+ static final String EMPTY_STR = "";
+ static final Class[] ONE_STRING_PARAM = new Class[] {String.class};
+
+ final static String dbfKey = "javax.xml.parsers.DocumentBuilderFactory";
+
+
+ // key: appenderName, value: appender
+ Hashtable appenderBag;
+
+ Properties props;
+ LoggerRepository repository;
+
+ protected LoggerFactory catFactory = null;
+
+ /**
+ No argument constructor.
+ */
+ public
+ DOMConfigurator () {
+ appenderBag = new Hashtable();
+ }
+
+ /**
+ Used internally to parse appenders by IDREF name.
+ */
+ protected
+ Appender findAppenderByName(Document doc, String appenderName) {
+ Appender appender = (Appender) appenderBag.get(appenderName);
+
+ if(appender != null) {
+ return appender;
+ } else {
+ // Doesn't work on DOM Level 1 :
+ // Element element = doc.getElementById(appenderName);
+
+ // Endre's hack:
+ Element element = null;
+ NodeList list = doc.getElementsByTagName("appender");
+ for (int t=0; t < list.getLength(); t++) {
+ Node node = list.item(t);
+ NamedNodeMap map= node.getAttributes();
+ Node attrNode = map.getNamedItem("name");
+ if (appenderName.equals(attrNode.getNodeValue())) {
+ element = (Element) node;
+ break;
+ }
+ }
+ // Hack finished.
+
+ if(element == null) {
+ LogLog.error("No appender named ["+appenderName+"] could be found.");
+ return null;
+ } else {
+ appender = parseAppender(element);
+ if (appender != null) {
+ appenderBag.put(appenderName, appender);
+ }
+ return appender;
+ }
+ }
+ }
+ /**
+ Used internally to parse appenders by IDREF element.
+ */
+ protected
+ Appender findAppenderByReference(Element appenderRef) {
+ String appenderName = subst(appenderRef.getAttribute(REF_ATTR));
+ Document doc = appenderRef.getOwnerDocument();
+ return findAppenderByName(doc, appenderName);
+ }
+
+ /**
+ * Delegates unrecognized content to created instance if
+ * it supports UnrecognizedElementParser.
+ * @since 1.2.15
+ * @param instance instance, may be null.
+ * @param element element, may not be null.
+ * @param props properties
+ * @throws IOException thrown if configuration of owner object
+ * should be abandoned.
+ */
+ private static void parseUnrecognizedElement(final Object instance,
+ final Element element,
+ final Properties props) throws Exception {
+ boolean recognized = false;
+ if (instance instanceof UnrecognizedElementHandler) {
+ recognized = ((UnrecognizedElementHandler) instance).parseUnrecognizedElement(
+ element, props);
+ }
+ if (!recognized) {
+ LogLog.warn("Unrecognized element " + element.getNodeName());
+ }
+ }
+
+ /**
+ * Delegates unrecognized content to created instance if
+ * it supports UnrecognizedElementParser and catches and
+ * logs any exception.
+ * @since 1.2.15
+ * @param instance instance, may be null.
+ * @param element element, may not be null.
+ * @param props properties
+ */
+ private static void quietParseUnrecognizedElement(final Object instance,
+ final Element element,
+ final Properties props) {
+ try {
+ parseUnrecognizedElement(instance, element, props);
+ } catch (Exception ex) {
+ if (ex instanceof InterruptedException || ex instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Error in extension content: ", ex);
+ }
+ }
+
+ /**
+ Used internally to parse an appender element.
+ */
+ protected
+ Appender parseAppender (Element appenderElement) {
+ String className = subst(appenderElement.getAttribute(CLASS_ATTR));
+ LogLog.debug("Class name: [" + className+']');
+ try {
+ Object instance = Loader.loadClass(className).newInstance();
+ Appender appender = (Appender)instance;
+ PropertySetter propSetter = new PropertySetter(appender);
+
+ appender.setName(subst(appenderElement.getAttribute(NAME_ATTR)));
+
+ NodeList children = appenderElement.getChildNodes();
+ final int length = children.getLength();
+
+ for (int loop = 0; loop < length; loop++) {
+ Node currentNode = children.item(loop);
+
+ /* We're only interested in Elements */
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element currentElement = (Element)currentNode;
+
+ // Parse appender parameters
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ }
+ // Set appender layout
+ else if (currentElement.getTagName().equals(LAYOUT_TAG)) {
+ appender.setLayout(parseLayout(currentElement));
+ }
+ // Add filters
+ else if (currentElement.getTagName().equals(FILTER_TAG)) {
+ parseFilters(currentElement, appender);
+ }
+ else if (currentElement.getTagName().equals(ERROR_HANDLER_TAG)) {
+ parseErrorHandler(currentElement, appender);
+ }
+ else if (currentElement.getTagName().equals(APPENDER_REF_TAG)) {
+ String refName = subst(currentElement.getAttribute(REF_ATTR));
+ if(appender instanceof AppenderAttachable) {
+ AppenderAttachable aa = (AppenderAttachable) appender;
+ LogLog.debug("Attaching appender named ["+ refName+
+ "] to appender named ["+ appender.getName()+"].");
+ aa.addAppender(findAppenderByReference(currentElement));
+ } else {
+ LogLog.error("Requesting attachment of appender named ["+
+ refName+ "] to appender named ["+ appender.getName()+
+ "] which does not implement org.apache.log4j.spi.AppenderAttachable.");
+ }
+ } else {
+ parseUnrecognizedElement(instance, currentElement, props);
+ }
+ }
+ }
+ propSetter.activate();
+ return appender;
+ }
+ /* Yes, it's ugly. But all of these exceptions point to the same
+ problem: we can't create an Appender */
+ catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not create an Appender. Reported error follows.",
+ oops);
+ return null;
+ }
+ }
+
+ /**
+ Used internally to parse an {@link ErrorHandler} element.
+ */
+ protected
+ void parseErrorHandler(Element element, Appender appender) {
+ ErrorHandler eh = (ErrorHandler) OptionConverter.instantiateByClassName(
+ subst(element.getAttribute(CLASS_ATTR)),
+ org.apache.log4j.spi.ErrorHandler.class,
+ null);
+
+ if(eh != null) {
+ eh.setAppender(appender);
+
+ PropertySetter propSetter = new PropertySetter(eh);
+ NodeList children = element.getChildNodes();
+ final int length = children.getLength();
+
+ for (int loop = 0; loop < length; loop++) {
+ Node currentNode = children.item(loop);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element currentElement = (Element) currentNode;
+ String tagName = currentElement.getTagName();
+ if(tagName.equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ } else if(tagName.equals(APPENDER_REF_TAG)) {
+ eh.setBackupAppender(findAppenderByReference(currentElement));
+ } else if(tagName.equals(LOGGER_REF)) {
+ String loggerName = currentElement.getAttribute(REF_ATTR);
+ Logger logger = (catFactory == null) ? repository.getLogger(loggerName)
+ : repository.getLogger(loggerName, catFactory);
+ eh.setLogger(logger);
+ } else if(tagName.equals(ROOT_REF)) {
+ Logger root = repository.getRootLogger();
+ eh.setLogger(root);
+ } else {
+ quietParseUnrecognizedElement(eh, currentElement, props);
+ }
+ }
+ }
+ propSetter.activate();
+ appender.setErrorHandler(eh);
+ }
+ }
+
+ /**
+ Used internally to parse a filter element.
+ */
+ protected
+ void parseFilters(Element element, Appender appender) {
+ String clazz = subst(element.getAttribute(CLASS_ATTR));
+ Filter filter = (Filter) OptionConverter.instantiateByClassName(clazz,
+ Filter.class, null);
+
+ if(filter != null) {
+ PropertySetter propSetter = new PropertySetter(filter);
+ NodeList children = element.getChildNodes();
+ final int length = children.getLength();
+
+ for (int loop = 0; loop < length; loop++) {
+ Node currentNode = children.item(loop);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element currentElement = (Element) currentNode;
+ String tagName = currentElement.getTagName();
+ if(tagName.equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ } else {
+ quietParseUnrecognizedElement(filter, currentElement, props);
+ }
+ }
+ }
+ propSetter.activate();
+ LogLog.debug("Adding filter of type ["+filter.getClass()
+ +"] to appender named ["+appender.getName()+"].");
+ appender.addFilter(filter);
+ }
+ }
+
+ /**
+ Used internally to parse an category element.
+ */
+ protected
+ void parseCategory (Element loggerElement) {
+ // Create a new org.apache.log4j.Category object from the element.
+ String catName = subst(loggerElement.getAttribute(NAME_ATTR));
+
+ Logger cat;
+
+ String className = subst(loggerElement.getAttribute(CLASS_ATTR));
+
+
+ if(EMPTY_STR.equals(className)) {
+ LogLog.debug("Retreiving an instance of org.apache.log4j.Logger.");
+ cat = (catFactory == null) ? repository.getLogger(catName) : repository.getLogger(catName, catFactory);
+ }
+ else {
+ LogLog.debug("Desired logger sub-class: ["+className+']');
+ try {
+ Class clazz = Loader.loadClass(className);
+ Method getInstanceMethod = clazz.getMethod("getLogger",
+ ONE_STRING_PARAM);
+ cat = (Logger) getInstanceMethod.invoke(null, new Object[] {catName});
+ } catch (InvocationTargetException oops) {
+ if (oops.getTargetException() instanceof InterruptedException
+ || oops.getTargetException() instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not retrieve category ["+catName+
+ "]. Reported error follows.", oops);
+ return;
+ } catch (Exception oops) {
+ LogLog.error("Could not retrieve category ["+catName+
+ "]. Reported error follows.", oops);
+ return;
+ }
+ }
+
+ // Setting up a category needs to be an atomic operation, in order
+ // to protect potential log operations while category
+ // configuration is in progress.
+ synchronized(cat) {
+ boolean additivity = OptionConverter.toBoolean(
+ subst(loggerElement.getAttribute(ADDITIVITY_ATTR)),
+ true);
+
+ LogLog.debug("Setting ["+cat.getName()+"] additivity to ["+additivity+"].");
+ cat.setAdditivity(additivity);
+ parseChildrenOfLoggerElement(loggerElement, cat, false);
+ }
+ }
+
+
+ /**
+ Used internally to parse the category factory element.
+ */
+ protected
+ void parseCategoryFactory(Element factoryElement) {
+ String className = subst(factoryElement.getAttribute(CLASS_ATTR));
+
+ if(EMPTY_STR.equals(className)) {
+ LogLog.error("Category Factory tag " + CLASS_ATTR + " attribute not found.");
+ LogLog.debug("No Category Factory configured.");
+ }
+ else {
+ LogLog.debug("Desired category factory: ["+className+']');
+ Object factory = OptionConverter.instantiateByClassName(className,
+ LoggerFactory.class,
+ null);
+ if (factory instanceof LoggerFactory) {
+ catFactory = (LoggerFactory) factory;
+ } else {
+ LogLog.error("Category Factory class " + className + " does not implement org.apache.log4j.LoggerFactory");
+ }
+ PropertySetter propSetter = new PropertySetter(factory);
+
+ Element currentElement = null;
+ Node currentNode = null;
+ NodeList children = factoryElement.getChildNodes();
+ final int length = children.getLength();
+
+ for (int loop=0; loop < length; loop++) {
+ currentNode = children.item(loop);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ currentElement = (Element)currentNode;
+ if (currentElement.getTagName().equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ } else {
+ quietParseUnrecognizedElement(factory, currentElement, props);
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ Used internally to parse the roor category element.
+ */
+ protected
+ void parseRoot (Element rootElement) {
+ Logger root = repository.getRootLogger();
+ // category configuration needs to be atomic
+ synchronized(root) {
+ parseChildrenOfLoggerElement(rootElement, root, true);
+ }
+ }
+
+
+ /**
+ Used internally to parse the children of a category element.
+ */
+ protected
+ void parseChildrenOfLoggerElement(Element catElement,
+ Logger cat, boolean isRoot) {
+
+ PropertySetter propSetter = new PropertySetter(cat);
+
+ // Remove all existing appenders from cat. They will be
+ // reconstructed if need be.
+ cat.removeAllAppenders();
+
+
+ NodeList children = catElement.getChildNodes();
+ final int length = children.getLength();
+
+ for (int loop = 0; loop < length; loop++) {
+ Node currentNode = children.item(loop);
+
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element currentElement = (Element) currentNode;
+ String tagName = currentElement.getTagName();
+
+ if (tagName.equals(APPENDER_REF_TAG)) {
+ Element appenderRef = (Element) currentNode;
+ Appender appender = findAppenderByReference(appenderRef);
+ String refName = subst(appenderRef.getAttribute(REF_ATTR));
+ if(appender != null)
+ LogLog.debug("Adding appender named ["+ refName+
+ "] to category ["+cat.getName()+"].");
+ else
+ LogLog.debug("Appender named ["+ refName + "] not found.");
+
+ cat.addAppender(appender);
+
+ } else if(tagName.equals(LEVEL_TAG)) {
+ parseLevel(currentElement, cat, isRoot);
+ } else if(tagName.equals(PRIORITY_TAG)) {
+ parseLevel(currentElement, cat, isRoot);
+ } else if(tagName.equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ } else {
+ quietParseUnrecognizedElement(cat, currentElement, props);
+ }
+ }
+ }
+ propSetter.activate();
+ }
+
+ /**
+ Used internally to parse a layout element.
+ */
+ protected
+ Layout parseLayout (Element layout_element) {
+ String className = subst(layout_element.getAttribute(CLASS_ATTR));
+ LogLog.debug("Parsing layout of class: \""+className+"\"");
+ try {
+ Object instance = Loader.loadClass(className).newInstance();
+ Layout layout = (Layout)instance;
+ PropertySetter propSetter = new PropertySetter(layout);
+
+ NodeList params = layout_element.getChildNodes();
+ final int length = params.getLength();
+
+ for (int loop = 0; loop < length; loop++) {
+ Node currentNode = (Node)params.item(loop);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element currentElement = (Element) currentNode;
+ String tagName = currentElement.getTagName();
+ if(tagName.equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ } else {
+ parseUnrecognizedElement(instance, currentElement, props);
+ }
+ }
+ }
+
+ propSetter.activate();
+ return layout;
+ }
+ catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not create the Layout. Reported error follows.",
+ oops);
+ return null;
+ }
+ }
+
+ protected
+ void parseRenderer(Element element) {
+ String renderingClass = subst(element.getAttribute(RENDERING_CLASS_ATTR));
+ String renderedClass = subst(element.getAttribute(RENDERED_CLASS_ATTR));
+ if(repository instanceof RendererSupport) {
+ RendererMap.addRenderer((RendererSupport) repository, renderedClass,
+ renderingClass);
+ }
+ }
+
+ /**
+ * Parses throwable renderer.
+ * @param element throwableRenderer element.
+ * @return configured throwable renderer.
+ * @since 1.2.16.
+ */
+ protected ThrowableRenderer parseThrowableRenderer(final Element element) {
+ String className = subst(element.getAttribute(CLASS_ATTR));
+ LogLog.debug("Parsing throwableRenderer of class: \""+className+"\"");
+ try {
+ Object instance = Loader.loadClass(className).newInstance();
+ ThrowableRenderer tr = (ThrowableRenderer)instance;
+ PropertySetter propSetter = new PropertySetter(tr);
+
+ NodeList params = element.getChildNodes();
+ final int length = params.getLength();
+
+ for (int loop = 0; loop < length; loop++) {
+ Node currentNode = (Node)params.item(loop);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element currentElement = (Element) currentNode;
+ String tagName = currentElement.getTagName();
+ if(tagName.equals(PARAM_TAG)) {
+ setParameter(currentElement, propSetter);
+ } else {
+ parseUnrecognizedElement(instance, currentElement, props);
+ }
+ }
+ }
+
+ propSetter.activate();
+ return tr;
+ }
+ catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not create the ThrowableRenderer. Reported error follows.",
+ oops);
+ return null;
+ }
+ }
+
+ /**
+ Used internally to parse a level element.
+ */
+ protected
+ void parseLevel(Element element, Logger logger, boolean isRoot) {
+ String catName = logger.getName();
+ if(isRoot) {
+ catName = "root";
+ }
+
+ String priStr = subst(element.getAttribute(VALUE_ATTR));
+ LogLog.debug("Level value for "+catName+" is ["+priStr+"].");
+
+ if(INHERITED.equalsIgnoreCase(priStr) || NULL.equalsIgnoreCase(priStr)) {
+ if(isRoot) {
+ LogLog.error("Root level cannot be inherited. Ignoring directive.");
+ } else {
+ logger.setLevel(null);
+ }
+ } else {
+ String className = subst(element.getAttribute(CLASS_ATTR));
+ if(EMPTY_STR.equals(className)) {
+ logger.setLevel(OptionConverter.toLevel(priStr, Level.DEBUG));
+ } else {
+ LogLog.debug("Desired Level sub-class: ["+className+']');
+ try {
+ Class clazz = Loader.loadClass(className);
+ Method toLevelMethod = clazz.getMethod("toLevel",
+ ONE_STRING_PARAM);
+ Level pri = (Level) toLevelMethod.invoke(null,
+ new Object[] {priStr});
+ logger.setLevel(pri);
+ } catch (Exception oops) {
+ if (oops instanceof InterruptedException || oops instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ LogLog.error("Could not create level ["+priStr+
+ "]. Reported error follows.", oops);
+ return;
+ }
+ }
+ }
+ LogLog.debug(catName + " level set to " + logger.getLevel());
+ }
+
+ protected
+ void setParameter(Element elem, PropertySetter propSetter) {
+ String name = subst(elem.getAttribute(NAME_ATTR));
+ String value = (elem.getAttribute(VALUE_ATTR));
+ value = subst(OptionConverter.convertSpecialChars(value));
+ propSetter.setProperty(name, value);
+ }
+
+
+ /**
+ Configure log4j using a configuration element as
+ defined in the log4j.dtd.
+
+ */
+ static
+ public
+ void configure (Element element) {
+ DOMConfigurator configurator = new DOMConfigurator();
+ configurator.doConfigure(element, LogManager.getLoggerRepository());
+ }
+
+ /**
+ Like {@link #configureAndWatch(String, long)} except that the
+ default delay as defined by {@link FileWatchdog#DEFAULT_DELAY} is
+ used.
+
+ @param configFilename A log4j configuration file in XML format.
+
+ */
+ static
+ public
+ void configureAndWatch(String configFilename) {
+ configureAndWatch(configFilename, FileWatchdog.DEFAULT_DELAY);
+ }
+
+ /**
+ Read the configuration file configFilename if it
+ exists. Moreover, a thread will be created that will periodically
+ check if configFilename has been created or
+ modified. The period is determined by the delay
+ argument. If a change or file creation is detected, then
+ configFilename is read to configure log4j.
+
+ @param configFilename A log4j configuration file in XML format.
+ @param delay The delay in milliseconds to wait between each check.
+ */
+ static
+ public
+ void configureAndWatch(String configFilename, long delay) {
+ XMLWatchdog xdog = new XMLWatchdog(configFilename);
+ xdog.setDelay(delay);
+ xdog.start();
+ }
+
+ private interface ParseAction {
+ Document parse(final DocumentBuilder parser) throws SAXException, IOException;
+ }
+
+
+ public
+ void doConfigure(final String filename, LoggerRepository repository) {
+ ParseAction action = new ParseAction() {
+ public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+ return parser.parse(new File(filename));
+ }
+ public String toString() {
+ return "file [" + filename + "]";
+ }
+ };
+ doConfigure(action, repository);
+ }
+
+
+ public
+ void doConfigure(final URL url, LoggerRepository repository) {
+ ParseAction action = new ParseAction() {
+ public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+ URLConnection uConn = url.openConnection();
+ uConn.setUseCaches(false);
+ InputSource src = new InputSource(uConn.getInputStream());
+ src.setSystemId(url.toString());
+ return parser.parse(src);
+ }
+ public String toString() {
+ return "url [" + url.toString() + "]";
+ }
+ };
+ doConfigure(action, repository);
+ }
+
+ /**
+ Configure log4j by reading in a log4j.dtd compliant XML
+ configuration file.
+
+ */
+ public
+ void doConfigure(final InputStream inputStream, LoggerRepository repository)
+ throws FactoryConfigurationError {
+ ParseAction action = new ParseAction() {
+ public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+ InputSource inputSource = new InputSource(inputStream);
+ inputSource.setSystemId("dummy://log4j.dtd");
+ return parser.parse(inputSource);
+ }
+ public String toString() {
+ return "input stream [" + inputStream.toString() + "]";
+ }
+ };
+ doConfigure(action, repository);
+ }
+
+ /**
+ Configure log4j by reading in a log4j.dtd compliant XML
+ configuration file.
+
+ */
+ public
+ void doConfigure(final Reader reader, LoggerRepository repository)
+ throws FactoryConfigurationError {
+ ParseAction action = new ParseAction() {
+ public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+ InputSource inputSource = new InputSource(reader);
+ inputSource.setSystemId("dummy://log4j.dtd");
+ return parser.parse(inputSource);
+ }
+ public String toString() {
+ return "reader [" + reader.toString() + "]";
+ }
+ };
+ doConfigure(action, repository);
+ }
+
+ /**
+ Configure log4j by reading in a log4j.dtd compliant XML
+ configuration file.
+
+ */
+ protected
+ void doConfigure(final InputSource inputSource, LoggerRepository repository)
+ throws FactoryConfigurationError {
+ if (inputSource.getSystemId() == null) {
+ inputSource.setSystemId("dummy://log4j.dtd");
+ }
+ ParseAction action = new ParseAction() {
+ public Document parse(final DocumentBuilder parser) throws SAXException, IOException {
+ return parser.parse(inputSource);
+ }
+ public String toString() {
+ return "input source [" + inputSource.toString() + "]";
+ }
+ };
+ doConfigure(action, repository);
+ }
+
+
+ private final void doConfigure(final ParseAction action, final LoggerRepository repository)
+ throws FactoryConfigurationError {
+ DocumentBuilderFactory dbf = null;
+ this.repository = repository;
+ try {
+ LogLog.debug("System property is :"+
+ OptionConverter.getSystemProperty(dbfKey,
+ null));
+ dbf = DocumentBuilderFactory.newInstance();
+ LogLog.debug("Standard DocumentBuilderFactory search succeded.");
+ LogLog.debug("DocumentBuilderFactory is: "+dbf.getClass().getName());
+ } catch(FactoryConfigurationError fce) {
+ Exception e = fce.getException();
+ LogLog.debug("Could not instantiate a DocumentBuilderFactory.", e);
+ throw fce;
+ }
+
+ try {
+ dbf.setValidating(true);
+
+ DocumentBuilder docBuilder = dbf.newDocumentBuilder();
+
+ docBuilder.setErrorHandler(new SAXErrorHandler());
+ docBuilder.setEntityResolver(new Log4jEntityResolver());
+
+ Document doc = action.parse(docBuilder);
+ parse(doc.getDocumentElement());
+ } catch (Exception e) {
+ if (e instanceof InterruptedException || e instanceof InterruptedIOException) {
+ Thread.currentThread().interrupt();
+ }
+ // I know this is miserable...
+ LogLog.error("Could not parse "+ action.toString() + ".", e);
+ }
+ }
+
+ /**
+ Configure by taking in an DOM element.
+ */
+ public void doConfigure(Element element, LoggerRepository repository) {
+ this.repository = repository;
+ parse(element);
+ }
+
+
+ /**
+ A static version of {@link #doConfigure(String, LoggerRepository)}. */
+ static
+ public
+ void configure(String filename) throws FactoryConfigurationError {
+ new DOMConfigurator().doConfigure(filename,
+ LogManager.getLoggerRepository());
+ }
+
+ /**
+ A static version of {@link #doConfigure(URL, LoggerRepository)}.
+ */
+ static
+ public
+ void configure(URL url) throws FactoryConfigurationError {
+ new DOMConfigurator().doConfigure(url, LogManager.getLoggerRepository());
+ }
+
+ /**
+ Used internally to configure the log4j framework by parsing a DOM
+ tree of XML elements based on log4j.dtd.
+
+ */
+ protected
+ void parse(Element element) {
+
+ String rootElementName = element.getTagName();
+
+ if (!rootElementName.equals(CONFIGURATION_TAG)) {
+ if(rootElementName.equals(OLD_CONFIGURATION_TAG)) {
+ LogLog.warn("The <"+OLD_CONFIGURATION_TAG+
+ "> element has been deprecated.");
+ LogLog.warn("Use the <"+CONFIGURATION_TAG+"> element instead.");
+ } else {
+ LogLog.error("DOM element is - not a <"+CONFIGURATION_TAG+"> element.");
+ return;
+ }
+ }
+
+
+ String debugAttrib = subst(element.getAttribute(INTERNAL_DEBUG_ATTR));
+
+ LogLog.debug("debug attribute= \"" + debugAttrib +"\".");
+ // if the log4j.dtd is not specified in the XML file, then the
+ // "debug" attribute is returned as the empty string.
+ if(!debugAttrib.equals("") && !debugAttrib.equals("null")) {
+ LogLog.setInternalDebugging(OptionConverter.toBoolean(debugAttrib, true));
+ } else {
+ LogLog.debug("Ignoring " + INTERNAL_DEBUG_ATTR + " attribute.");
+ }
+
+ //
+ // reset repository before configuration if reset="true"
+ // on configuration element.
+ //
+ String resetAttrib = subst(element.getAttribute(RESET_ATTR));
+ LogLog.debug("reset attribute= \"" + resetAttrib +"\".");
+ if(!("".equals(resetAttrib))) {
+ if (OptionConverter.toBoolean(resetAttrib, false)) {
+ repository.resetConfiguration();
+ }
+ }
+
+
+
+ String confDebug = subst(element.getAttribute(CONFIG_DEBUG_ATTR));
+ if(!confDebug.equals("") && !confDebug.equals("null")) {
+ LogLog.warn("The \""+CONFIG_DEBUG_ATTR+"\" attribute is deprecated.");
+ LogLog.warn("Use the \""+INTERNAL_DEBUG_ATTR+"\" attribute instead.");
+ LogLog.setInternalDebugging(OptionConverter.toBoolean(confDebug, true));
+ }
+
+ String thresholdStr = subst(element.getAttribute(THRESHOLD_ATTR));
+ LogLog.debug("Threshold =\"" + thresholdStr +"\".");
+ if(!"".equals(thresholdStr) && !"null".equals(thresholdStr)) {
+ repository.setThreshold(thresholdStr);
+ }
+
+ //Hashtable appenderBag = new Hashtable(11);
+
+ /* Building Appender objects, placing them in a local namespace
+ for future reference */
+
+ // First configure each category factory under the root element.
+ // Category factories need to be configured before any of
+ // categories they support.
+ //
+ String tagName = null;
+ Element currentElement = null;
+ Node currentNode = null;
+ NodeList children = element.getChildNodes();
+ final int length = children.getLength();
+
+ for (int loop = 0; loop < length; loop++) {
+ currentNode = children.item(loop);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ currentElement = (Element) currentNode;
+ tagName = currentElement.getTagName();
+
+ if (tagName.equals(CATEGORY_FACTORY_TAG) || tagName.equals(LOGGER_FACTORY_TAG)) {
+ parseCategoryFactory(currentElement);
+ }
+ }
+ }
+
+ for (int loop = 0; loop < length; loop++) {
+ currentNode = children.item(loop);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ currentElement = (Element) currentNode;
+ tagName = currentElement.getTagName();
+
+ if (tagName.equals(CATEGORY) || tagName.equals(LOGGER)) {
+ parseCategory(currentElement);
+ } else if (tagName.equals(ROOT_TAG)) {
+ parseRoot(currentElement);
+ } else if(tagName.equals(RENDERER_TAG)) {
+ parseRenderer(currentElement);
+ } else if(tagName.equals(THROWABLE_RENDERER_TAG)) {
+ if (repository instanceof ThrowableRendererSupport) {
+ ThrowableRenderer tr = parseThrowableRenderer(currentElement);
+ if (tr != null) {
+ ((ThrowableRendererSupport) repository).setThrowableRenderer(tr);
+ }
+ }
+ } else if (!(tagName.equals(APPENDER_TAG)
+ || tagName.equals(CATEGORY_FACTORY_TAG)
+ || tagName.equals(LOGGER_FACTORY_TAG))) {
+ quietParseUnrecognizedElement(repository, currentElement, props);
+ }
+ }
+ }
+ }
+
+
+ protected
+ String subst(final String value) {
+ return subst(value, props);
+ }
+
+ /**
+ * Substitutes property value for any references in expression.
+ *
+ * @param value value from configuration file, may contain
+ * literal text, property references or both
+ * @param props properties.
+ * @return evaluated expression, may still contain expressions
+ * if unable to expand.
+ * @since 1.2.15
+ */
+ public static String subst(final String value, final Properties props) {
+ try {
+ return OptionConverter.substVars(value, props);
+ } catch (IllegalArgumentException e) {
+ LogLog.warn("Could not perform variable substitution.", e);
+ return value;
+ }
+ }
+
+
+ /**
+ * Sets a parameter based from configuration file content.
+ *
+ * @param elem param element, may not be null.
+ * @param propSetter property setter, may not be null.
+ * @param props properties
+ * @since 1.2.15
+ */
+ public static void setParameter(final Element elem,
+ final PropertySetter propSetter,
+ final Properties props) {
+ String name = subst(elem.getAttribute("name"), props);
+ String value = (elem.getAttribute("value"));
+ value = subst(OptionConverter.convertSpecialChars(value), props);
+ propSetter.setProperty(name, value);
+ }
+
+ /**
+ * Creates an object and processes any nested param elements
+ * but does not call activateOptions. If the class also supports
+ * UnrecognizedElementParser, the parseUnrecognizedElement method
+ * will be call for any child elements other than param.
+ *
+ * @param element element, may not be null.
+ * @param props properties
+ * @param expectedClass interface or class expected to be implemented
+ * by created class
+ * @return created class or null.
+ * @throws Exception thrown if the contain object should be abandoned.
+ * @since 1.2.15
+ */
+ public static Object parseElement(final Element element,
+ final Properties props,
+ final Class expectedClass) throws Exception {
+ String clazz = subst(element.getAttribute("class"), props);
+ Object instance = OptionConverter.instantiateByClassName(clazz,
+ expectedClass, null);
+
+ if (instance != null) {
+ PropertySetter propSetter = new PropertySetter(instance);
+ NodeList children = element.getChildNodes();
+ final int length = children.getLength();
+
+ for (int loop = 0; loop < length; loop++) {
+ Node currentNode = children.item(loop);
+ if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element currentElement = (Element) currentNode;
+ String tagName = currentElement.getTagName();
+ if (tagName.equals("param")) {
+ setParameter(currentElement, propSetter, props);
+ } else {
+ parseUnrecognizedElement(instance, currentElement, props);
+ }
+ }
+ }
+ return instance;
+ }
+ return null;
+ }
+
+}
+
+
+class XMLWatchdog extends FileWatchdog {
+
+ XMLWatchdog(String filename) {
+ super(filename);
+ }
+
+ /**
+ Call {@link DOMConfigurator#configure(String)} with the
+ filename to reconfigure log4j. */
+ public
+ void doOnChange() {
+ new DOMConfigurator().doConfigure(filename,
+ LogManager.getLoggerRepository());
+ }
+}
diff --git a/java/src/org/apache/log4j/xml/Log4jEntityResolver.java b/java/src/org/apache/log4j/xml/Log4jEntityResolver.java
new file mode 100644
index 0000000..94125a6
--- /dev/null
+++ b/java/src/org/apache/log4j/xml/Log4jEntityResolver.java
@@ -0,0 +1,51 @@
+/*
+ * 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.log4j.xml;
+
+import org.apache.log4j.helpers.LogLog;
+import org.xml.sax.EntityResolver;
+import org.xml.sax.InputSource;
+
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+
+/**
+ * An {@link EntityResolver} specifically designed to return
+ * log4j.dtd which is embedded within the log4j jar
+ * file.
+ *
+ * @author Paul Austin
+ * */
+public class Log4jEntityResolver implements EntityResolver {
+ private static final String PUBLIC_ID = "-//APACHE//DTD LOG4J 1.2//EN";
+
+ public InputSource resolveEntity (String publicId, String systemId) {
+ if (systemId.endsWith("log4j.dtd") || PUBLIC_ID.equals(publicId)) {
+ Class clazz = getClass();
+ InputStream in = clazz.getResourceAsStream("/org/apache/log4j/xml/log4j.dtd");
+ if (in == null) {
+ LogLog.warn("Could not find [log4j.dtd] using [" + clazz.getClassLoader()
+ + "] class loader, parsed without DTD.");
+ in = new ByteArrayInputStream(new byte[0]);
+ }
+ return new InputSource(in);
+ } else {
+ return null;
+ }
+ }
+}
diff --git a/java/src/org/apache/log4j/xml/SAXErrorHandler.java b/java/src/org/apache/log4j/xml/SAXErrorHandler.java
new file mode 100644
index 0000000..43e851b
--- /dev/null
+++ b/java/src/org/apache/log4j/xml/SAXErrorHandler.java
@@ -0,0 +1,46 @@
+/*
+ * 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.log4j.xml;
+
+import org.xml.sax.ErrorHandler;
+import org.xml.sax.SAXParseException;
+import org.apache.log4j.helpers.LogLog;
+
+public class SAXErrorHandler implements ErrorHandler {
+
+ public
+ void error(final SAXParseException ex) {
+ emitMessage("Continuable parsing error ", ex);
+ }
+
+ public
+ void fatalError(final SAXParseException ex) {
+ emitMessage("Fatal parsing error ", ex);
+ }
+
+ public
+ void warning(final SAXParseException ex) {
+ emitMessage("Parsing warning ", ex);
+ }
+
+ private static void emitMessage(final String msg, final SAXParseException ex) {
+ LogLog.warn(msg +ex.getLineNumber()+" and column "
+ +ex.getColumnNumber());
+ LogLog.warn(ex.getMessage(), ex.getException());
+ }
+}
diff --git a/java/src/org/apache/log4j/xml/UnrecognizedElementHandler.java b/java/src/org/apache/log4j/xml/UnrecognizedElementHandler.java
new file mode 100644
index 0000000..463d5d9
--- /dev/null
+++ b/java/src/org/apache/log4j/xml/UnrecognizedElementHandler.java
@@ -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.
+ */
+package org.apache.log4j.xml;
+
+import org.w3c.dom.Element;
+import java.util.Properties;
+
+/**
+ * When implemented by an object configured by DOMConfigurator,
+ * the handle method will be called when an unrecognized child
+ * element is encountered. Unrecognized child elements of
+ * the log4j:configuration element will be dispatched to
+ * the logger repository if it supports this interface.
+ *
+ * @since 1.2.15
+ */
+public interface UnrecognizedElementHandler {
+ /**
+ * Called to inform a configured object when
+ * an unrecognized child element is encountered.
+ * @param element element, may not be null.
+ * @param props properties in force, may be null.
+ * @return true if configured object recognized the element
+ * @throws Exception throw an exception to prevent activation
+ * of the configured object.
+ */
+ boolean parseUnrecognizedElement(Element element, Properties props) throws Exception;
+}
\ No newline at end of file
diff --git a/java/src/org/apache/log4j/xml/XMLLayout.java b/java/src/org/apache/log4j/xml/XMLLayout.java
new file mode 100644
index 0000000..2062a80
--- /dev/null
+++ b/java/src/org/apache/log4j/xml/XMLLayout.java
@@ -0,0 +1,216 @@
+/*
+ * 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.
+ */
+
+// Contributors: Mathias Bogaert
+
+package org.apache.log4j.xml;
+
+import org.apache.log4j.Layout;
+import org.apache.log4j.helpers.Transform;
+import org.apache.log4j.spi.LocationInfo;
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.Set;
+import java.util.Arrays;
+
+/**
+ * The output of the XMLLayout consists of a series of log4j:event
+ * elements as defined in the log4j.dtd. It does not output a
+ * complete well-formed XML file. The output is designed to be
+ * included as an external entity in a separate file to form
+ * a correct XML file.
+ *
+ * For example, if abc is the name of the file where
+ * the XMLLayout ouput goes, then a well-formed XML file would be:
+ *
+
+ <?xml version="1.0" ?>
+
+ <!DOCTYPE log4j:eventSet PUBLIC "-//APACHE//DTD LOG4J 1.2//EN" "log4j.dtd" [<!ENTITY data SYSTEM "abc">]>
+
+ <log4j:eventSet version="1.2" xmlns:log4j="http://jakarta.apache.org/log4j/">
+ &data;
+ </log4j:eventSet>
+
+
+ * This approach enforces the independence of the XMLLayout and the
+ * appender where it is embedded.
+ *
+ *
The version attribute helps components to correctly
+ * intrepret output generated by XMLLayout. The value of this
+ * attribute should be "1.1" for output generated by log4j versions
+ * prior to log4j 1.2 (final release) and "1.2" for relase 1.2 and
+ * later.
+ *
+ * Appenders using this layout should have their encoding
+ * set to UTF-8 or UTF-16, otherwise events containing
+ * non ASCII characters could result in corrupted
+ * log files.
+ *
+ * @author Ceki Gülcü
+ * @since 0.9.0
+ * */
+public class XMLLayout extends Layout {
+
+ private final int DEFAULT_SIZE = 256;
+ private final int UPPER_LIMIT = 2048;
+
+ private StringBuffer buf = new StringBuffer(DEFAULT_SIZE);
+ private boolean locationInfo = false;
+ private boolean properties = false;
+
+ /**
+ * The LocationInfo option takes a boolean value. By default,
+ * it is set to false which means there will be no location
+ * information output by this layout. If the the option is set to
+ * true, then the file name and line number of the statement at the
+ * origin of the log statement will be output.
+ *
+ *
If you are embedding this layout within an {@link
+ * org.apache.log4j.net.SMTPAppender} then make sure to set the
+ * LocationInfo option of that appender as well.
+ * */
+ public void setLocationInfo(boolean flag) {
+ locationInfo = flag;
+ }
+
+ /**
+ Returns the current value of the LocationInfo option.
+ */
+ public boolean getLocationInfo() {
+ return locationInfo;
+ }
+
+ /**
+ * Sets whether MDC key-value pairs should be output, default false.
+ * @param flag new value.
+ * @since 1.2.15
+ */
+ public void setProperties(final boolean flag) {
+ properties = flag;
+ }
+
+ /**
+ * Gets whether MDC key-value pairs should be output.
+ * @return true if MDC key-value pairs are output.
+ * @since 1.2.15
+ */
+ public boolean getProperties() {
+ return properties;
+ }
+
+ /** No options to activate. */
+ public void activateOptions() {
+ }
+
+
+ /**
+ * Formats a {@link org.apache.log4j.spi.LoggingEvent} in conformance with the log4j.dtd.
+ * */
+ public String format(final LoggingEvent event) {
+
+ // Reset working buffer. If the buffer is too large, then we need a new
+ // one in order to avoid the penalty of creating a large array.
+ if(buf.capacity() > UPPER_LIMIT) {
+ buf = new StringBuffer(DEFAULT_SIZE);
+ } else {
+ buf.setLength(0);
+ }
+
+ // We yield to the \r\n heresy.
+
+ buf.append("\r\n");
+
+ buf.append("\r\n");
+
+ String ndc = event.getNDC();
+ if(ndc != null) {
+ buf.append("\r\n");
+ }
+
+ String[] s = event.getThrowableStrRep();
+ if(s != null) {
+ buf.append("\r\n");
+ }
+
+ if(locationInfo) {
+ LocationInfo locationInfo = event.getLocationInformation();
+ buf.append("\r\n");
+ }
+
+ if (properties) {
+ Set keySet = event.getPropertyKeySet();
+ if (keySet.size() > 0) {
+ buf.append("\r\n");
+ Object[] keys = keySet.toArray();
+ Arrays.sort(keys);
+ for (int i = 0; i < keys.length; i++) {
+ String key = keys[i].toString();
+ Object val = event.getMDC(key);
+ if (val != null) {
+ buf.append("\r\n");
+ }
+ }
+ buf.append("\r\n");
+ }
+ }
+
+ buf.append("\r\n\r\n");
+
+ return buf.toString();
+ }
+
+ /**
+ The XMLLayout prints and does not ignore exceptions. Hence the
+ return value false.
+ */
+ public boolean ignoresThrowable() {
+ return false;
+ }
+}
diff --git a/java/src/org/apache/log4j/xml/package.html b/java/src/org/apache/log4j/xml/package.html
new file mode 100644
index 0000000..8a9ed6e
--- /dev/null
+++ b/java/src/org/apache/log4j/xml/package.html
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+XML based components.
+
+
+
+
+
+Last modified: Mon Mar 27 21:17:13 MDT 2000
+
+