package er.distribution; import java.lang.reflect.Field; import org.apache.log4j.Logger; import com.webobjects.appserver.WOSession; import com.webobjects.eocontrol.EOEditingContext; import com.webobjects.eocontrol.EOEnterpriseObject; import com.webobjects.eodistribution.EODistributionContext; import com.webobjects.eodistribution.common.ERDistributionUtils; import com.webobjects.eodistribution.common._EONotificationCarrier; import com.webobjects.eodistribution.common._EOReferenceRecordingCoder; import com.webobjects.eodistribution.common._EOServerInvocation; import com.webobjects.eodistribution.common._EOServerReturnValue; import com.webobjects.foundation.NSArray; import com.webobjects.foundation.NSData; import com.webobjects.foundation.NSLog; import com.webobjects.foundation.NSMutableArray; import er.distribution.common.ERReferenceRecordingCoder; @SuppressWarnings("deprecation") public class ERDistributionContext extends EODistributionContext { public static final Logger log = Logger.getLogger(ERDistributionContext.class); public ERDistributionContext(WOSession session) { super(session); try { _EOReferenceRecordingCoder newCoder = new ERReferenceRecordingCoder(true); newCoder.setDelegate(this); Field coderField = EODistributionContext.class.getDeclaredField("_coder"); coderField.setAccessible(true); coderField.set(this, newCoder); } catch (Exception e) { e.printStackTrace(); } } public ERDistributionContext(WOSession session, EOEditingContext editingContext) { super(session, editingContext); } public static long[] DEBUG_GROUPS = { NSLog.DebugGroupApplicationGeneration, NSLog.DebugGroupArchiving, NSLog.DebugGroupAssociations, NSLog.DebugGroupComponentBindings, NSLog.DebugGroupComponents, NSLog.DebugGroupControllers, NSLog.DebugGroupDatabaseAccess, NSLog.DebugGroupDeployment, NSLog.DebugGroupEnterpriseObjects, NSLog.DebugGroupFormatting, NSLog.DebugGroupIO, NSLog.DebugGroupJSPServlets, NSLog.DebugGroupKeyValueCoding, NSLog.DebugGroupModel, NSLog.DebugGroupMultithreading, NSLog.DebugGroupParsing, NSLog.DebugGroupQualifiers, NSLog.DebugGroupReflection, NSLog.DebugGroupRequestHandling, NSLog.DebugGroupResources, NSLog.DebugGroupRules, NSLog.DebugGroupSQLGeneration, NSLog.DebugGroupTiming, NSLog.DebugGroupUserInterface, NSLog.DebugGroupValidation, NSLog.DebugGroupWebObjects, NSLog.DebugGroupWebServices }; public static long allowedDebugGroups() { long result = 0; for (Long group : DEBUG_GROUPS) { if (NSLog.debugLoggingAllowedForGroups(group)) { result |= group; } } return result; } /** * Enables NSLog logging so errors are not silently ignored */ @Override public NSData responseToClientMessage(NSData message) { int savedLogLevel = -2; long savedDebugGroups = 0; if (!NSLog.debugLoggingAllowedForLevelAndGroups(NSLog.DebugLevelInformational, NSLog.DebugGroupIO)) { // force IO debugging on so that server exceptions are always logged savedLogLevel = NSLog.allowedDebugLevel(); savedDebugGroups = allowedDebugGroups(); NSLog.setAllowedDebugLevel(NSLog.DebugLevelInformational); NSLog.allowDebugLoggingForGroups(allowedDebugGroups() | NSLog.DebugGroupIO); } NSData result = super.responseToClientMessage(message); if (savedLogLevel != -2) { NSLog.setAllowedDebugLevel(savedLogLevel); NSLog.allowDebugLoggingForGroups(savedDebugGroups); } return result; } /** * Adds request and response logging */ @Override public NSArray<_EOServerReturnValue> _processClientRequest(@SuppressWarnings("rawtypes") NSArray invocations) { int count = invocations.count(); NSMutableArray<_EOServerReturnValue> results = new NSMutableArray<_EOServerReturnValue>(count); for (int i = 0; i < count; ++i) { _EOServerInvocation invocation = (_EOServerInvocation)invocations.objectAtIndex(i); if (!"clientSideRequestGetNotifications".equals(ERDistributionUtils.method(invocation))) { log.debug("request: " + ERDistributionUtils.invocationToString(invocation).replace("\n", "")); } _EOServerReturnValue result = null; try { result = invocation.doInvokeWithTarget(this); if (result == null) { log.error("result was null"); } else if (result.holdsServerException()) { log.error(result.serverExceptionClassName() + ": " + result.serverExceptionMessage()); } else if (log.isDebugEnabled()) { logReturnValue(result); } } catch (RuntimeException e) { log.error(e.getMessage(), e); throw e; } results.addObject(result); } return results; } /** * Looks inside otherwise opaque _EOServerReturnValue object and logs the response in greater detail */ protected void logReturnValue(_EOServerReturnValue result) { String message = messageForReturnValue(result); if (message != null) { log.debug(message); } } /** * Looks inside otherwise opaque _EOServerReturnValue object and logs the response in greater detail */ protected String messageForReturnValue(_EOServerReturnValue result) { if (result.returnValue() instanceof NSArray<?>) { // probably a fetch specification StringBuilder message = new StringBuilder(); NSArray<Object> arrayResult = (NSArray<Object>)result.returnValue(); for (Object object : arrayResult) { if (object instanceof EOEnterpriseObject) { EOEnterpriseObject eo = (EOEnterpriseObject)object; message.append(eo.toString()).append(" | ").append(eo.snapshot()).append('\n'); } else { message.append(object); } } return "response: \n" + message.toString().trim(); } else if (result.returnValue() instanceof EOEnterpriseObject) { // probably a fault return "response: \n" + result.returnValue().toString() + " | " + ((EOEnterpriseObject)result.returnValue()).snapshot(); } else if (result.returnValue() instanceof _EONotificationCarrier) { // most often this is the EOObjectsChangedInObjectStoreNotification, which contains updated values for changed records _EONotificationCarrier notificationCarrier = (_EONotificationCarrier) result.returnValue(); NSArray notifications = notificationCarrier.notifications(); if (!notifications.isEmpty() && notificationCarrier.propertySnapshots() != null) { StringBuilder message = new StringBuilder(); for (Object gid : notificationCarrier.propertySnapshots().keySet()) { NSArray snapshot = (NSArray) notificationCarrier.propertySnapshots().get(gid); message.append(gid.toString()).append(" = ").append(snapshot).append('\n'); } message.append("This also contains GIDs for all the toMany relationship related to the objects."); return "response: " + result.returnValue().toString().replace("\n", "") + "\n" + message.toString().trim(); } else { return "response: " + result.returnValue().toString().replace("\n", ""); } } else if (result.returnValue() != null) { return "response: " + result.returnValue().toString().replace("\n", ""); } return null; } }