package er.distribution.common; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import org.apache.log4j.Logger; import com.webobjects.eocontrol.EOKeyValueQualifier; import com.webobjects.eocontrol.EOQualifier; import com.webobjects.eodistribution.common.ERDistributionUtils; import com.webobjects.eodistribution.common._EOReferenceRecordingCoder; import com.webobjects.foundation.NSData; import com.webobjects.foundation.NSSelector; /** * Add DEBUG logging for messages that fail to be decoded * Fix _classForNumber slightly * Fix deserialization of EOKeyValueQualifier to create an _EOKnownSelector when appropriate; otherwise, in-memory evaluation of the qualifier does not work. * */ public class ERReferenceRecordingCoder extends _EOReferenceRecordingCoder { /** * Declared to gain access to the protected method: operatorSelectorForSelectorNamed */ private static class FriendlyQualifier extends EOKeyValueQualifier { /** * Do I need to update serialVersionUID? * See section 5.6 <cite>Type Changes Affecting Serialization</cite> on page 51 of the * <a href="http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf">Java Object Serialization Spec</a> */ private static final long serialVersionUID = 1L; public FriendlyQualifier() { super("fake", EOQualifier.QualifierOperatorEqual, "qual"); } public static NSSelector operatorSelectorForSelectorNamed(String name) { return EOQualifier.operatorSelectorForSelectorNamed(name); } } private static final Logger log = Logger.getLogger(ERReferenceRecordingCoder.class); private NSData sourceMessage; private int sourceSize; public ERReferenceRecordingCoder(boolean parity) { super(parity); } /** * Fix to return null if 'number' is less than zero */ @Override protected Class _classForNumber(short number) { if (number < 0) { return null; } else { return super._classForNumber(number); } } /** * Save a copy of the whole original message so we can log it later if needed */ @Override public void prepareForReading(InputStream stream) { sourceSize = -1; if ((stream instanceof ByteArrayInputStream)) { try { sourceSize = stream.available(); } catch (IOException e) {} } if (log.isDebugEnabled()) { saveSourceMessage(stream); } super.prepareForReading(stream); } /** * Add DEBUG logging for messages that fail to be decoded */ @Override public Object decodeObject() { try { Object result = super.decodeObject(); if (result != null && result.getClass().equals(EOKeyValueQualifier.class)) { EOKeyValueQualifier qual = (EOKeyValueQualifier) result; NSSelector<?> selector = qual.selector(); NSSelector<?> newSelector = FriendlyQualifier.operatorSelectorForSelectorNamed(selector.name()); EOKeyValueQualifier newResult = new EOKeyValueQualifier(qual.key(), newSelector, qual.value()); return newResult; } return result; } catch (RuntimeException e) { if (log.isDebugEnabled()) { try { String filename = File.createTempFile("decodeError", "").getCanonicalPath(); FileOutputStream fileOutputStream = new FileOutputStream(filename); sourceMessage.writeToStream(fileOutputStream); fileOutputStream.close(); log.warn("Wrote message that caused exception to file: " + filename); } catch (java.io.IOException exception) { log.warn("Unable to save message that caused exception: " + exception); } } log.error(e.getMessage() + ". Response was " + sourceSize + " bytes long."); if (ERDistributionUtils.isTemporaryLockingFailure(e)) { throw e; } else { throw new MalformedResponseException("The response from the server was invalid" + (sourceSize == 0 ? " (empty)" : "") + "."); } } } private void saveSourceMessage(InputStream stream) { if (!(stream instanceof ByteArrayInputStream)) { sourceMessage = new NSData(); return; } ByteArrayInputStream byteStream = (ByteArrayInputStream)stream; byte[] bytes = new byte[byteStream.available()]; try { byteStream.read(bytes); sourceMessage = new NSData(bytes); } catch (IOException e) { log.error(e.getMessage(), e); } try { stream.reset(); } catch (IOException e) { log.error(e.getMessage(), e); } } }