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. + +

+ + + + + + + + + + + + + + + + + + + + + + + +
DatePatternRollover scheduleExample
'.'yyyy-MM + Rollover at the beginning of each monthAt 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 CharacterEffect
cUsed 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". + +

CUsed 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. +
FUsed 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. + +

lUsed 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. + +

LUsed 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. + +

mUsed to output the application supplied message associated with + the logging event.
MUsed 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. + +

nOutputs 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. + + +

pUsed to output the priority of the logging event.
rUsed to output the number of milliseconds elapsed since the construction + of the layout until the creation of the logging event.
tUsed to output the name of the thread that generated the + logging event.
xUsed 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 + +
%20cfalse20noneLeft 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. + +
%.30cNAnone30Truncate from the beginning if the category name is longer than 30 + characters. + +
%20.30cfalse2030Left 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.30ctrue2030Right 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("" + Layout.LINE_SEP); + sbuf.append("" + Layout.LINE_SEP); + sbuf.append("" + Layout.LINE_SEP); + sbuf.append("" + Layout.LINE_SEP); + if(locationInfo) { + sbuf.append("" + Layout.LINE_SEP); + } + sbuf.append("" + 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("
TimeThreadLevelCategoryFile:LineMessage
" + 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 CharacterEffect
cUsed 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". + +

CUsed 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}. +

FUsed 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. + +

lUsed 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. + +

LUsed 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. + +

mUsed to output the application supplied message associated with + the logging event.
MUsed 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. + +

nOutputs 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. + + +

pUsed to output the priority of the logging event.
rUsed to output the number of milliseconds elapsed from the construction + of the layout until the creation of the logging event.
tUsed to output the name of the thread that generated the + logging event.
xUsed 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 + +
%20cfalse20noneLeft 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. + +
%.30cNAnone30Truncate from the beginning if the category name is longer than 30 + characters. + +
%20.30cfalse2030Left 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.30ctrue2030Right 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:

+ +

Screen shot of chainsaw.

+ +

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:

+ + + + + + + + + + + +
PropertyDescription
chainsaw.portIndicates 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: + +

    + +

  1. 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. + +

  2. 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: + + + + + + + + + + + + + + +
NameRequirementDescriptionSample Value
PortoptionalThis 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 renderersValue 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 + +