// // Copyright (c)1998-2011 Pearson Education, Inc. or its affiliate(s). // All rights reserved. // package openadk.library.log; //////////////////////////////////////////////////////////////////////////////// // // Copyright (c)2001-2007 Edustructures LLC // All rights reserved. // // This software is the confidential and proprietary information of // Edustructures LLC ("Confidential Information"). You shall not disclose // such Confidential Information and shall use it only in accordance with the // terms of the license agreement you entered into with Edustructures. // import java.io.StringWriter; import java.util.*; import openadk.library.*; import openadk.library.infra.*; /** * Provides access to the Zone Integration Server log.<p> * * ServerLog functionality can be customized and extended by adding one or * more <i>ServerLogModule</i> implementations to the chain of loggers. The * logging chain is hierarchical, comprised of ServerLog instances at the ADK, * Agent, and Zone levels. Whenever a server-side logging operation is * performed on a zone, it is delegated to the <i>ServerLogModule</i> instances * at each level in the hierarchy, beginning with the Zone.<p> * * To customize server-side logging on a global basis, call the * <code>ADK.getServerLog</code> static function and use the methods below to * manipulate <i>ServerLogModule</i> instances at the root level of the * hierarchy: * * <ul> * <li><code>addLogger</code></li> * <li><code>removeLogger</code></li> * <li><code>clearLoggers</code></li> * <li><code>getLoggers</code></li> * </ul> * * * Similarly, to customize logging on an agent-global basis or per-zone basis, * call the <code>Agent.getServerLog</code> or <code>Agent.getServerLog( Zone )</code> * methods to obtain the ServerLog for the Agent or a Zone, respectively.<p> * * */ public class ServerLog { /** * Global registry of ServerLog instances keyed by ID */ private static HashMap<String, ServerLog> sInstances = new HashMap<String, ServerLog>(); /** * The parent ServerLog */ private ServerLog fParent; /** * The ID */ private String fID; /** * The zone to which log entries will be reported */ private Zone fZone; /** * ServerLogModule objects to which log information will be posted */ private List<ServerLogModule> fLoggers = new Vector<ServerLogModule>(); /** * Protected constructor; clients must call <code>getInstance</code> */ private ServerLog( String id, Zone zone ) { fID = id; fZone = zone; // Determine the parent if( id.equals("ADK") ) fParent = null; else if( id.equals("ADK.Agent") ) fParent = getInstance( "ADK", null ); else fParent = getInstance( "ADK.Agent", null ); } /** * Returns the ID of this logger instance * @return the ID of this logger instance */ public String getID() { return fID; } /** * Get a ServerLog instance with the specified ID.<p> * * This method is intended to be called internally by the ADK. You * should call the <code>ADK.getServerLog</code>, <code>Agent.getServerLog</code>, * or <code>Zone.getServerLog</code> methods to obtain a ServerLog instance * rather than directly calling this method.<p> * * @param id The ID identifying the ServerLog to return * @param zone The zone that the ServerLog is attached to * * @return A ServerLog instance */ public static ServerLog getInstance( String id, Zone zone ) { if( id == null ) throw new IllegalArgumentException( "ID cannot be null" ); ServerLog log = sInstances.get( id ); if( log == null ) { log = new ServerLog( id, zone ); sInstances.put( id, log ); } return log; } /** * Adda a ServerLogModule to the chain of loggers.<p> * * @param logger A <i>ServerLogModule</i> implementation * * @see #removeLogger * @see #clearLoggers * @see #getLoggers * * @since ADK 1.5 */ public void addLogger( ServerLogModule logger ) { synchronized( fLoggers ) { if( !fLoggers.contains( logger ) ) fLoggers.add( logger ); } } /** * Remove a ServerLogModule from the chain of loggers.<p> * * @param logger A <i>ServerLogModule</i> implementation * * @see #addLogger * @see #clearLoggers * @see #getLoggers * * @since ADK 1.5 */ public void removeLogger( ServerLogModule logger ) { synchronized( fLoggers ) { fLoggers.remove( logger ); } } /** * Clear all ServerLogModule modules from the chain of loggers.<p> * * @see #addLogger * @see #removeLogger * @see #getLoggers * * @since ADK 1.5 */ public void clearLoggers() { synchronized( fLoggers ) { fLoggers.clear(); } } /** * Get a list of all registered ServerLogModule modules that comprise the * chain of loggers.<p> * @return An array of ServerLogModules * * @see #addLogger * @see #removeLogger * @see #clearLoggers * * @since ADK 1.5 */ public ServerLogModule[] getLoggers() { synchronized( fLoggers ) { ServerLogModule[] arr = new ServerLogModule[ fLoggers.size() ]; fLoggers.toArray( arr ); return arr; } } /** * Copies all registered ServerLogModules for this ServerLog into the * supplied Vector.<p> * @since ADK 1.5 */ protected void getLoggersInto( List<ServerLogModule> target ) { synchronized( fLoggers ) { for( ServerLogModule logger : fLoggers ) { target.add( logger ); } } } /** * Post a SIF_LogEntry to the server.<p> * * Use this form of the <code>log</code> method to post a simple * informative message to the server.<p> * * @param message A textual description of the error */ public void log( String message ) { log( LogLevel.INFO, message, null, null, -1, -1, null, (SIFDataObject[])null ); } /** * Post a SIF_LogEntry to the server.<p> * * Use this form of the <code>log</code> method to post an * error, warning, or informative message to the server with an * description, extended description, and optional application-defined * error code.<p> * * @param level The LogLevel to assign to this log entry * @param desc A textual description of the error * @param extDesc Extended error description, or <code>null</code> if no * value is to be assigned to the SIF_LogEntry/SIF_ExtDesc element * @param appCode Error code specific to the application posting the log * entry, or <code>null</code> if no value is to be assigned to the * SIF_LogEntry/SIF_ApplicationCode element */ public void log( LogLevel level, String desc, String extDesc, String appCode ) { log( level, desc, extDesc, appCode, -1, -1, null, (SIFDataObject[])null ); } /** * Post a SIF_LogEntry to the server.<p> * * Use this form of the <code>log</code> method to post an * error, warning, or informative message to the server with an category * and code enumerated by the SIF Specification.<p> * * @param level The LogLevel to assign to this log entry * @param desc A textual description of the error * @param extDesc Extended error description, or <code>null</code> if no * value is to be assigned to the SIF_LogEntry/SIF_ExtDesc element * @param appCode Error code specific to the application posting the log * entry, or <code>null</code> if no value is to be assigned to the * SIF_LogEntry/SIF_ApplicationCode element * @param category The SIF_Category value to assign to this log entry, as * defined by the SIF Specification * @param code The SIF_Code value to assign to this log entry, as defined * by the SIF Specification */ public void log( LogLevel level, String desc, String extDesc, String appCode, int category, int code ) { log( level, desc, extDesc, appCode, category, code, null, (SIFDataObject[])null ); } /** * Post a SIF_LogEntry to the server.<p> * * Use this form of the <code>log</code> method to post a simple * error, warning, or informative message to the server that references a * SIF Message and optionally a set of SIF Data Objects previously received * by the agent.<p> * * @param level The LogLevel to assign to this log entry * @param message A textual description of the error * @param info The <i>SIFMessageInfo</i> instance from the ADK message * handler implementation identifying a SIF Message received by the agent * @param objects One or more SIFDataObject instances received in the message * identified by the <i>info</i> parameter */ public void log( LogLevel level, String message, SIFMessageInfo info, SIFDataObject[] objects ) { log( level, message, null, null, -1, -1, info, objects ); } /** * Post a SIF_LogEntry to the server.<p> * * Use this form of the <code>log</code> method to post an * error, warning, or informative message to the server that references a * SIF Message and optionally a set of SIF Data Objects previously received * by the agent. The log entry can also have an extended error description * and application-defined error code.<p> * * @param level The LogLevel to assign to this log entry * @param desc A textual description of the error * @param extDesc Extended error description, or <code>null</code> if no * value is to be assigned to the SIF_LogEntry/SIF_ExtDesc element * @param appCode Error code specific to the application posting the log * entry, or <code>null</code> if no value is to be assigned to the * SIF_LogEntry/SIF_ApplicationCode element * @param info The <i>SIFMessageInfo</i> instance from the ADK message * handler implementation identifying a SIF Message received by the agent * @param objects One or more SIFDataObject instances received in the message * identified by the <i>info</i> parameter */ public void log( LogLevel level, String desc, String extDesc, String appCode, SIFMessageInfo info, SIFDataObject[] objects ) { log( level, desc, extDesc, appCode, -1, -1, info, objects ); } /** * Post a SIF_LogEntry to the server.<p> * * Use this form of the <code>log</code> method to post an error, warning, * or informative message to the server that references a * SIF Message and optionally a set of SIF Data Objects previously received * by the agent. The log entry is assigned a category and code defined by * the SIF Specification, and may have an extended error description and * optional application-defined error code.<p> * * @param level The LogLevel to assign to this log entry * @param desc A textual description of the error * @param extDesc Extended error description, or <code>null</code> if no * value is to be assigned to the SIF_LogEntry/SIF_ExtDesc element * @param appCode Error code specific to the application posting the log * entry, or <code>null</code> if no value is to be assigned to the * SIF_LogEntry/SIF_ApplicationCode element * @param category The SIF_Category value to assign to this log entry, as * defined by the SIF Specification * @param code The SIF_Code value to assign to this log entry, as * defined by the SIF Specification * @param info The <i>SIFMessageInfo</i> instance from the ADK message * handler implementation identifying a SIF Message received by the agent * @param objects One or more SIFDataObject instances received in the message * identified by the <i>info</i> parameter. This argument may be specified * as an array or sequence of SIFDataObjects */ public void log( LogLevel level, String desc, String extDesc, String appCode, int category, int code, SIFMessageInfo info, SIFDataObject... objects ) { if( fZone == null ){ throw new IllegalStateException( "ServerLog.log can only be called on a zone's ServerLog instance" ); } String msg = null; SIF_LogEntry le = null; if( ADK.getSIFVersion().compareTo( SIFVersion.SIF15r1 ) >= 0 ) { // Create a SIF_LogEntry le = new SIF_LogEntry(); le.setSource( LogSource.AGENT ); le.setLogLevel( LogLevel.wrap( level == null ? "Unknown" : level.toString() ) ); if( desc != null ) le.setSIF_Desc( desc ); if( extDesc != null ) le.setSIF_ExtendedDesc( extDesc ); if( appCode != null ) le.setSIF_ApplicationCode( appCode ); if( category != -1 ) le.setSIF_Category( String.valueOf( category ) ); if( code != -1 ) le.setSIF_Code( code ); // Reference a SIF_Message? if( info != null ) { try { SIF_Header headerCopy = (SIF_Header)info.getSIFHeader().clone(); SIF_LogEntryHeader sleh = new SIF_LogEntryHeader(); sleh.setSIF_Header( headerCopy ); le.setSIF_OriginalHeader( sleh ); } catch( CloneNotSupportedException cnse ){ fZone.getLog().warn( "Unable to clone SIF_Header for SIF_LogEntry event:" + cnse.getMessage(), cnse ); } } if( objects != null ) { SIF_LogObjects slos = new SIF_LogObjects(); le.setSIF_LogObjects( slos ); for( int i = 0; i < objects.length; i++ ) { if( objects[i] == null ) continue; // Package into a SIF_LogObject and add to the repeatable list // of SIF_LogEntry/SIF_LogObjects // TODO: This code needs to change so that it will work properly // Also, it needs to be tested SIF_LogObject lo = new SIF_LogObject(); lo.setObjectName( objects[i].getObjectType().tag( info.getSIFVersion() ) ); try{ lo.addChild( (SIFElement)objects[i].clone() ); } catch( CloneNotSupportedException cnse ){ le.setSIF_ExtendedDesc( le.getSIF_ExtendedDesc() + " ERROR cloning object\r\n" + cnse ); } slos.add( lo ); } } } else { // When running in SIF 1.1 or earlier, there is no // SIF_LogEntry support. Build a string that can be // written to the local zone log, including as much // information from the would-be SIF_LogEntry as // possible. StringBuffer b = new StringBuffer(); b.append( "Server Log [Level=" ); b.append( level == null ? "Unknown" : level.toString() ); if( category != -1 && code != -1 ) { b.append( ", Category=" ); b.append( category ); b.append( ", Code=" ); b.append( code ); } if( appCode != null ) { b.append( ", AppCode=" ); b.append( appCode ); } b.append( "] " ); if( desc != null ) b.append( desc ); if( extDesc != null ) b.append( ". " + extDesc ); msg = b.toString(); } // Post the the server ServerLogModule[] chain = _getLogChain( fZone ); for( int i = 0; i < chain.length; i++ ) { if( le != null ) chain[i].log( fZone, le ); else chain[i].log( fZone, msg ); } } private ServerLogModule[] _getLogChain( Zone zone ) { Vector<ServerLogModule> v = new Vector<ServerLogModule>(); ServerLog parent = this; while( parent != null ) { parent.getLoggersInto( v ); parent = parent.fParent; } ServerLogModule[] arr = new ServerLogModule[ v.size() ]; v.copyInto( arr ); return arr; } }