Merge into master from pull request #352:
Java bundle improvements (https://github.com/floodlight/loxigen/pull/352)
diff --git a/java_gen/java_model.py b/java_gen/java_model.py
index b3c3df0..79cea7a 100644
--- a/java_gen/java_model.py
+++ b/java_gen/java_model.py
@@ -413,6 +413,9 @@
                 reply_name = m.group(1) + "Reply"
                 if model.interface_by_name(reply_name):
                     return ["OFRequest<%s>" % reply_name ]
+            elif self.name == "OFBundleCtrlMsg":
+                reply_name = "OFBundleCtrlMsg"
+                return ["OFRequest<%s>" % reply_name ]
         return []
 
 
diff --git a/java_gen/java_type.py b/java_gen/java_type.py
index 79a1db3..3f3a5f2 100644
--- a/java_gen/java_type.py
+++ b/java_gen/java_type.py
@@ -470,6 +470,10 @@
 
 port_speed = JType("PortSpeed")
 error_type = JType("OFErrorType")
+of_message = JType("OFMessage")\
+            .op(read="OFMessageVer$version.READER.readFrom(bb)",
+                write="$name.writeTo(bb)")
+
 of_type = JType("OFType", 'byte') \
             .op(read='bb.readByte()', write='bb.writeByte($name)')
 action_type= gen_enum_jtype("OFActionType")\
@@ -514,6 +518,10 @@
         .op(read='GenTableId.read2Bytes(bb)',
             write='$name.write2Bytes(bb)',
            )
+bundle_id = JType("BundleId") \
+        .op(read='BundleId.read4Bytes(bb)',
+            write='$name.write4Bytes(bb)',
+           )
 udf = JType("UDF") \
          .op(version=ANY, read="UDF.read4Bytes(bb)", write="$name.write4Bytes(bb)", default="UDF.ZERO")
 error_cause_data = JType("OFErrorCauseData") \
@@ -725,6 +733,8 @@
         'of_bsn_log': { 'data': var_string },
 
         'of_features_reply' : { 'auxiliary_id' : of_aux_id},
+
+        'of_bundle_add_msg' : { 'data' : of_message },
 }
 
 
@@ -789,6 +799,8 @@
         return action_type_set
     elif field_name == "table_id" and re.match(r'of_bsn_gentable.*', obj_name):
         return gen_table_id
+    elif field_name == "bundle_id" and re.match(r'of_bundle_.*', obj_name):
+        return bundle_id
     elif c_type in default_mtype_to_jtype_convert_map:
         return default_mtype_to_jtype_convert_map[c_type]
     elif re.match(r'list\(of_([a-zA-Z_]+)_t\)', c_type):
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerator.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerator.java
new file mode 100644
index 0000000..2cb3583
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerator.java
@@ -0,0 +1,7 @@
+package org.projectfloodlight.openflow.protocol;
+
+import org.projectfloodlight.openflow.types.BundleId;
+
+public interface BundleIdGenerator {
+    BundleId nextBundleId();
+}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerators.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerators.java
new file mode 100644
index 0000000..997e0cd
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/protocol/BundleIdGenerators.java
@@ -0,0 +1,28 @@
+package org.projectfloodlight.openflow.protocol;
+
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.projectfloodlight.openflow.types.BundleId;
+
+public class BundleIdGenerators {
+    private static final BundleIdGenerator GLOBAL_BUNDLE_ID_GENERATOR = create();
+
+    public static BundleIdGenerator create() {
+        return new StandardBundleIdGenerator();
+    }
+
+    public static BundleIdGenerator global() {
+        return GLOBAL_BUNDLE_ID_GENERATOR;
+    }
+}
+
+class StandardBundleIdGenerator implements BundleIdGenerator {
+
+    private final AtomicInteger idGen = new AtomicInteger();
+
+    @Override
+    public BundleId nextBundleId() {
+        return BundleId.of(idGen.incrementAndGet());
+    }
+
+}
\ No newline at end of file
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/BundleId.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/BundleId.java
new file mode 100644
index 0000000..cccf67e
--- /dev/null
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/BundleId.java
@@ -0,0 +1,92 @@
+package org.projectfloodlight.openflow.types;
+
+import javax.annotation.concurrent.Immutable;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+
+import com.google.common.hash.PrimitiveSink;
+import com.google.common.primitives.UnsignedInts;
+
+@Immutable
+public class BundleId implements OFValueType<BundleId> {
+    static final int LENGTH = 4;
+
+    private final static int NONE_VAL = 0;
+    public final static BundleId NONE = new BundleId(NONE_VAL);
+
+    private final static int NO_MASK_VAL = 0xFFFFFFFF;
+    public final static BundleId NO_MASK = new BundleId(NO_MASK_VAL);
+    public final static BundleId FULL_MASK = NONE;
+
+    private final int rawValue;
+
+    private BundleId(final int rawValue) {
+        this.rawValue = rawValue;
+    }
+
+    public static BundleId of(final int raw) {
+        if(raw == NONE_VAL)
+            return NONE;
+        else if(raw == NO_MASK_VAL)
+            return NO_MASK;
+        return new BundleId(raw);
+    }
+
+    public int getInt() {
+        return rawValue;
+    }
+
+    @Override
+    public int getLength() {
+        return LENGTH;
+    }
+
+    @Override
+    public String toString() {
+        return UnsignedInts.toString(rawValue);
+    }
+
+    @Override
+    public BundleId applyMask(BundleId mask) {
+        return BundleId.of(rawValue & mask.rawValue);    }
+
+    @Override
+    public int hashCode() {
+        final int prime = 31;
+        int result = 1;
+        result = prime * result + rawValue;
+        return result;
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BundleId other = (BundleId) obj;
+        if (rawValue != other.rawValue)
+            return false;
+        return true;
+    }
+
+    public void write4Bytes(ChannelBuffer c) {
+        c.writeInt(rawValue);
+    }
+
+    public static BundleId read4Bytes(ChannelBuffer c) {
+        return BundleId.of(c.readInt());
+    }
+
+    @Override
+    public int compareTo(BundleId o) {
+        return UnsignedInts.compare(rawValue, rawValue);
+    }
+
+    @Override
+    public void putTo(PrimitiveSink sink) {
+        sink.putInt(rawValue);
+    }
+}
diff --git a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java
index 7d7c38e..98c1253 100644
--- a/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java
+++ b/java_gen/pre-written/src/main/java/org/projectfloodlight/openflow/types/ClassId.java
@@ -43,7 +43,7 @@
 
     @Override
     public String toString() {
-        return Integer.toString(rawValue);
+        return UnsignedInts.toString(rawValue);
     }
 
     @Override
diff --git a/java_gen/templates/of_class.java b/java_gen/templates/of_class.java
index 071f177..0b032ac 100644
--- a/java_gen/templates/of_class.java
+++ b/java_gen/templates/of_class.java
@@ -74,6 +74,13 @@
     ${impl_class}(${
         ", ".join("%s %s" %(prop.java_type.public_type, prop.name) for prop in msg.data_members) }) {
 //:: for prop in msg.data_members:
+//::   if not prop.java_type.is_primitive and (not prop.default_value or prop.default_value != "null"):
+        if(${prop.name} == null) {
+            throw new NullPointerException("${msg.name}: property ${prop.name} cannot be null");
+        }
+//::   #endif
+//:: #endfor
+//:: for prop in msg.data_members:
         this.${prop.name} = ${prop.name};
 //:: #endfor
     }
diff --git a/java_gen/templates/of_factories.java b/java_gen/templates/of_factories.java
index f9ec015..ba73286 100644
--- a/java_gen/templates/of_factories.java
+++ b/java_gen/templates/of_factories.java
@@ -51,7 +51,9 @@
 
     private static class GenericReader implements OFMessageReader<OFMessage> {
         public OFMessage readFrom(ChannelBuffer bb) throws OFParseError {
-            short wireVersion = U8.f(bb.getByte(0));
+            if(!bb.readable())
+                return null;
+            short wireVersion = U8.f(bb.getByte(bb.readerIndex()));
             OFFactory factory;
             switch (wireVersion) {
             //:: for v in versions: