I’m getting some strange behaviour in Java while using SAX. Values are being set, but then reverting to their default values in the toString method on output. This seems really unusual. I’ve looked through the code and output the memory addresses of the variables, and everything happens in the right order and is seemingly done right. The other deserialised XML elements are fine, so I can’t work it out.
My system works like so: there are classes with abstract methods where the user defines attribute helpers and element handlers. The element handlers handle the generation of subclasses based on sub-XML elements, while the attribute handers take in attribute lists from the constructors and use this, through the attribute helpers, to set the fields via implemented abstract methods in the attribute helpers.
Here is the problematic class in question:
package kokuks.flowmon;
import kokuks.flowmon.FlowmonParser.Handler;
import org.xml.sax.Attributes;
public class FlowProbeFlowStats extends FlowmonParserElement {
protected final FlowProbe parent;
protected int flowId = 0;
protected int packets = 0;
protected int bytes = 0;
protected long delayFromFirstProbeSum = 0;
protected boolean set = false;
/**
* @param handler
* @param parent
* @param uri
* @param localName
* @param qName
* @param attributes
*/
FlowProbeFlowStats(Handler handler, FlowProbe parent, String uri, String localName, String qName, Attributes attributes) {
super(handler, parent, uri, localName, qName, attributes);
this.parent = parent;
}
/**
* @return the parent
*/
public FlowProbe getParent() {
return parent;
}
/* (non-Javadoc)
* @see kokuks.flowmon.FlowmonParserElement#getXMLName()
*/
@Override
protected String getXMLName() {
return "FlowStats";
}
/* (non-Javadoc)
* @see kokuks.flowmon.FlowmonParserElement#createAttributeHandlers()
*/
@Override
protected IAttributeHandler[] createAttributeHandlers() {
return new IAttributeHandler[] {
new AttributeHandler("flowId") {
@Override
public void perform(String argValue) {
System.out.println("AH flowId: " + argValue);
flowId = Integer.parseInt(argValue);
set = true;
}
},
new AttributeHandler("packets") {
@Override
public void perform(String argValue) {
System.out.println("AH packets: " + argValue);
packets = Integer.parseInt(argValue);
set = true;
}
},
new AttributeHandler("bytes") {
@Override
public void perform(String argValue) {
System.out.println("AH bytes: " + argValue);
bytes = Integer.parseInt(argValue);
set = true;
}
},
new AttributeHandler("delayFromFirstProbeSum") {
@Override
public void perform(String argValue) {
System.out.println("AH delayFromFirstProbeSum: " + argValue);
delayFromFirstProbeSum = Long.parseLong(argValue.substring(0, argValue.length() - 2));
set = true;
}
}
};
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "FlowStats/" + super.toString() + "[flowId: " + flowId + ", packets: " + packets +
", bytes: " + bytes + ", delayFromFirstProbeSum: " + delayFromFirstProbeSum + "]";
}
}
I also have a few debugging outputs where the attributes are looked up and the relevant implemented methods called.
if (this instanceof FlowProbeFlowStats) {
System.out.println("ahaaaa1:: " + this.toString());
}
IAttributeHandler[] ahandlers = createAttributeHandlers();
if (ahandlers != null) {
Map<String, IAttributeHandler> attributeHandlers = new HashMap<String, IAttributeHandler>(ahandlers.length);
for (IAttributeHandler ah : ahandlers) {
attributeHandlers.put(ah.getName(), ah);
}
for (int i = 0; i < attributes.getLength(); i++) {
IAttributeHandler ah = attributeHandlers.get(attributes.getQName(i));
if (ah == null) {
throw new IllegalStateException("Attribute helper not found in qName: " + qName + " for attrib with localName: " + attributes.getLocalName(i) + ", qname*: " + attributes.getQName(i));
}
try {
ah.perform(attributes.getValue(i));
} catch (Exception e) {
e.printStackTrace();
}
}
}
if (this instanceof FlowProbeFlowStats) {
System.out.println("ahaaaa2:: " + this.toString());
}
Anyway, here is the output:
ahaaaa1:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@89cd21[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]
AH flowId: 1
AH packets: 5
AH bytes: 333
AH delayFromFirstProbeSum: 50274395ns
ahaaaa2:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@89cd21[flowId: 1, packets: 5, bytes: 333, delayFromFirstProbeSum: 50274395]
ahaaaa1:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@849f16[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]
AH flowId: 2
AH packets: 5
AH bytes: 333
AH delayFromFirstProbeSum: 50274395ns
ahaaaa2:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@849f16[flowId: 2, packets: 5, bytes: 333, delayFromFirstProbeSum: 50274395]
ahaaaa1:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@825c9d[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]
AH flowId: 3
AH packets: 5
AH bytes: 1082
AH delayFromFirstProbeSum: 0ns
ahaaaa2:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@825c9d[flowId: 3, packets: 5, bytes: 1082, delayFromFirstProbeSum: 0]
ahaaaa1:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@1577d9b[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]
AH flowId: 4
AH packets: 4
AH bytes: 762
AH delayFromFirstProbeSum: 0ns
ahaaaa2:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@1577d9b[flowId: 4, packets: 4, bytes: 762, delayFromFirstProbeSum: 0]
ahaaaa1:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@8097e5[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]
AH flowId: 2
AH packets: 5
AH bytes: 333
AH delayFromFirstProbeSum: 0ns
ahaaaa2:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@8097e5[flowId: 2, packets: 5, bytes: 333, delayFromFirstProbeSum: 0]
ahaaaa1:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@745c78[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]
AH flowId: 4
AH packets: 4
AH bytes: 762
AH delayFromFirstProbeSum: 40615996ns
ahaaaa2:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@745c78[flowId: 4, packets: 4, bytes: 762, delayFromFirstProbeSum: 40615996]
ahaaaa1:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@162e843[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]
AH flowId: 1
AH packets: 5
AH bytes: 333
AH delayFromFirstProbeSum: 0ns
ahaaaa2:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@162e843[flowId: 1, packets: 5, bytes: 333, delayFromFirstProbeSum: 0]
ahaaaa1:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@7e9dfc[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]
AH flowId: 3
AH packets: 5
AH bytes: 1082
AH delayFromFirstProbeSum: 51335996ns
ahaaaa2:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@7e9dfc[flowId: 3, packets: 5, bytes: 1082, delayFromFirstProbeSum: 51335996]
[kks-j~KokuKS~/Names] fmxml:FlowMonitor[FlowStats[Flow[flowID: 1,timeFirstTxPacket: 1011735011,timeLastTxPacket: 1343457103,timeFirstRxPacket: 1021768610,timeLastRxPacket: 1353490702,delaySum: 50274395,jitterSum: 212800,lastDelay: 10033599,txBytes: 333, rxBytes: 333,txPackets: 5,rxPackets: 5,lostPackets: 0,timesForwarded: 0],Flow[flowID: 2,timeFirstTxPacket: 1015259481,timeLastTxPacket: 1543226881,timeFirstRxPacket: 1025293080,timeLastRxPacket: 1553260480,delaySum: 50274395,jitterSum: 212800,lastDelay: 10033599,txBytes: 333, rxBytes: 333,txPackets: 5,rxPackets: 5,lostPackets: 0,timesForwarded: 0],Flow[flowID: 3,timeFirstTxPacket: 1021768610,timeLastTxPacket: 1332941904,timeFirstRxPacket: 1031802209,timeLastRxPacket: 1343457103,delaySum: 51335996,jitterSum: 481600,lastDelay: 10515199,txBytes: 1082, rxBytes: 1082,txPackets: 5,rxPackets: 5,lostPackets: 0,timesForwarded: 0],Flow[flowID: 4,timeFirstTxPacket: 1025293080,timeLastTxPacket: 1332969282,timeFirstRxPacket: 1035326679,timeLastRxPacket: 1343226881,delaySum: 40615996,jitterSum: 291200,lastDelay: 10257599,txBytes: 762, rxBytes: 762,txPackets: 4,rxPackets: 4,lostPackets: 0,timesForwarded: 0]],Ipv4FlowClassifier[Flow[flowId: 4, sourceAddress: 10.1.0.1, destinationAddress: 10.1.0.2, protocol: 6],Flow[flowId: 3, sourceAddress: 10.1.0.1, destinationAddress: 10.1.1.2, protocol: 6],Flow[flowId: 2, sourceAddress: 10.1.0.2, destinationAddress: 10.1.0.1, protocol: 6],Flow[flowId: 1, sourceAddress: 10.1.1.2, destinationAddress: 10.1.0.1, protocol: 6]],FlowProbes[FlowProbe[index: 0, stats: <FlowStats/kokuks.flowmon.FlowProbeFlowStats@89cd21[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0],FlowStats/kokuks.flowmon.FlowProbeFlowStats@849f16[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0],FlowStats/kokuks.flowmon.FlowProbeFlowStats@825c9d[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0],FlowStats/kokuks.flowmon.FlowProbeFlowStats@1577d9b[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]>],FlowProbe[index: 1, stats: <FlowStats/kokuks.flowmon.FlowProbeFlowStats@8097e5[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0],FlowStats/kokuks.flowmon.FlowProbeFlowStats@745c78[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]>],FlowProbe[index: 2, stats: <FlowStats/kokuks.flowmon.FlowProbeFlowStats@162e843[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0],FlowStats/kokuks.flowmon.FlowProbeFlowStats@7e9dfc[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]>]]]
Note the memory values — it’s quite bizarre. The entire toString output (from converted root XML element) is on the last line.
Anyway, thanks for any help,
Chris
Edit: I set some different default values for the variables rather than 0:
protected final FlowProbe parent;
protected int flowId = 1000;
protected int packets = 1000;
protected int bytes = 1000;
protected long delayFromFirstProbeSum = 1000;
protected boolean set = false;
And here is the result during setting:
ahaaaa1:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@1156508[flowId: 0, packets: 0, bytes: 0, delayFromFirstProbeSum: 0]
kokuks.flowmon.FlowProbeFlowStats@1156508: AH flowId: 4
kokuks.flowmon.FlowProbeFlowStats@1156508: AH packets: 5
kokuks.flowmon.FlowProbeFlowStats@1156508: AH bytes: 1082
kokuks.flowmon.FlowProbeFlowStats@1156508: AH delayFromFirstProbeSum: 0ns
ahaaaa2:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@1156508[flowId: 4, packets: 5, bytes: 1082, delayFromFirstProbeSum: 0]
So something weird is going on initially, the values are 0 rather than 1000. Very odd. When it comes to the final output, the object being output has the values of 1000 rather than 0 (the initial values from the field defaults).
Edit2: I now have the following in the header:
protected int countdown = 4;
which is decremented when an attribute is set. An example is:
new AttributeHandler("packets") {
@Override
public void perform(String argValue) {
synchronized (FlowProbeFlowStats.this) {
System.out.println(FlowProbeFlowStats.super.toString() + ": AH packets: " + argValue);
packets = Integer.parseInt(argValue);
System.out.println("packets set to " + packets);
set = true;
countdown--;
}
}
},
The oString statement is now:
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return "FlowStats/" + super.toString() + "[flowId: " + flowId + ", packets: " + packets +
", bytes: " + bytes + ", delayFromFirstProbeSum: " + delayFromFirstProbeSum +
", countdown: " + countdown + "]";
}
and the print outs read:
ahaaaa2:: FlowStats/kokuks.flowmon.FlowProbeFlowStats@1b27882[flowId: 1, packets: 5, bytes: 333, delayFromFirstProbeSum: 0, countdown: -4]
...
[kks-j~KokuKS~/Names] fmxml:FlowMonitor[FlowStats[Flow[flowID: 1,timeFirstTxPacket: 1007829288,timeLastTxPacket: 1524157706,timeFirstRxPacket: 1017862887,timeLastRxPacket: 1534191305,delaySum: 50274395,jitterSum: 212800,lastDelay: 10033599,txBytes: 333, rxBytes: 333,txPackets: 5,rxPackets: 5,lostPackets: 0,timesForwarded: 0],Flow[flowID: 2,timeFirstTxPacket: 1010210025,timeLastTxPacket: 1324387511,timeFirstRxPacket: 1020243624,timeLastRxPacket: 1334421110,delaySum: 50274395,jitterSum: 212800,lastDelay: 10033599,txBytes: 333, rxBytes: 333,txPackets: 5,rxPackets: 5,lostPackets: 0,timesForwarded: 0],Flow[flowID: 3,timeFirstTxPacket: 1017862887,timeLastTxPacket: 1313900107,timeFirstRxPacket: 1027896486,timeLastRxPacket: 1324157706,delaySum: 40615996,jitterSum: 291200,lastDelay: 10257599,txBytes: 762, rxBytes: 762,txPackets: 4,rxPackets: 4,lostPackets: 0,timesForwarded: 0],Flow[flowID: 4,timeFirstTxPacket: 1020243624,timeLastTxPacket: 1313872312,timeFirstRxPacket: 1030277223,timeLastRxPacket: 1324387511,delaySum: 51335996,jitterSum: 481600,lastDelay: 10515199,txBytes: 1082, rxBytes: 1082,txPackets: 5,rxPackets: 5,lostPackets: 0,timesForwarded: 0]],Ipv4FlowClassifier[Flow[flowId: 4, sourceAddress: 10.1.0.1, destinationAddress: 10.1.0.2, protocol: 6],Flow[flowId: 3, sourceAddress: 10.1.0.1, destinationAddress: 10.1.1.2, protocol: 6],Flow[flowId: 2, sourceAddress: 10.1.0.2, destinationAddress: 10.1.0.1, protocol: 6],Flow[flowId: 1, sourceAddress: 10.1.1.2, destinationAddress: 10.1.0.1, protocol: 6]],FlowProbes[FlowProbe[index: 0, stats: <FlowStats/kokuks.flowmon.FlowProbeFlowStats@1b0d2d0[flowId: 1000, packets: 1000, bytes: 1000, delayFromFirstProbeSum: 1000, countdown: 4],FlowStats/kokuks.flowmon.FlowProbeFlowStats@83df14[flowId: 1000, packets: 1000, bytes: 1000, delayFromFirstProbeSum: 1000, countdown: 4],FlowStats/kokuks.flowmon.FlowProbeFlowStats@1aabc29[flowId: 1000, packets: 1000, bytes: 1000, delayFromFirstProbeSum: 1000, countdown: 4],FlowStats/kokuks.flowmon.FlowProbeFlowStats@2758d0[flowId: 1000, packets: 1000, bytes: 1000, delayFromFirstProbeSum: 1000, countdown: 4]>],FlowProbe[index: 1, stats: <FlowStats/kokuks.flowmon.FlowProbeFlowStats@137e19e[flowId: 1000, packets: 1000, bytes: 1000, delayFromFirstProbeSum: 1000, countdown: 4],FlowStats/kokuks.flowmon.FlowProbeFlowStats@1bd5f28[flowId: 1000, packets: 1000, bytes: 1000, delayFromFirstProbeSum: 1000, countdown: 4]>],FlowProbe[index: 2, stats: <FlowStats/kokuks.flowmon.FlowProbeFlowStats@1b27882[flowId: 1000, packets: 1000, bytes: 1000, delayFromFirstProbeSum: 1000, countdown: 4],FlowStats/kokuks.flowmon.FlowProbeFlowStats@5b84b[flowId: 1000, packets: 1000, bytes: 1000, delayFromFirstProbeSum: 1000, countdown: 4]>]]]
The synchronized statement in the attribute setter and toString was here to check for JVM bugs but it didn’t do anything. Odd eh?
Edit 3:
I changed the fields to Atomic*. I have to seriously put this down to a JVM bug, seeing as the atomic fields are final and set by default values internally and I’m getting a NullPointerException.
protected final AtomicInteger flowId = new AtomicInteger();
protected final AtomicInteger packets = new AtomicInteger();
protected final AtomicInteger bytes = new AtomicInteger();
protected final AtomicLong delayFromFirstProbeSum = new AtomicLong();
Exception:
Exception in thread "FlowMonitor Updater" java.lang.NullPointerException
at kokuks.flowmon.FlowProbeFlowStats.toString(FlowProbeFlowStats.java:95)
Edit 4: I changed from JDK1.7 to a stable version of 1.6, same problem.
I’ve just thought on, maybe the fields -are- unset during the constructor and then set later on. I’m not sure about the order of initalisation, but maybe it’s messed up in this case. Anyway, I’ll put it in an init method and see if that does anything. Bizarre that it doesn’t work in the case of a single class, though. It works for the others.
Fixed the problem by moving the setup to an init method, turned out that there was some weirdness going on with doing everything in the constructor. Hence why the Atomic* fields were null when I tried to access them. This happened because they hadn’t been set yet since the super call to the parent constructor comes first. Why the other elements were fine is beyond me, but that fixed it.