Performacne improvements via adding a netty-based openflowj and openflow plugin;...
[controller.git] / third-party / openflowj_netty / src / main / java / org / openflow / protocol / factory / BasicFactory.java
diff --git a/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/BasicFactory.java b/third-party/openflowj_netty/src/main/java/org/openflow/protocol/factory/BasicFactory.java
new file mode 100644 (file)
index 0000000..b61aa72
--- /dev/null
@@ -0,0 +1,344 @@
+/**
+*    Copyright (c) 2008 The Board of Trustees of The Leland Stanford Junior
+*    University
+*
+*    Licensed under the Apache License, Version 2.0 (the "License"); you may
+*    not use this file except in compliance with the License. You may obtain
+*    a copy of the License at
+*
+*         http://www.apache.org/licenses/LICENSE-2.0
+*
+*    Unless required by applicable law or agreed to in writing, software
+*    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+*    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+*    License for the specific language governing permissions and limitations
+*    under the License.
+**/
+
+package org.openflow.protocol.factory;
+
+import java.nio.ByteBuffer;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.openflow.protocol.OFMessage;
+import org.openflow.protocol.OFType;
+import org.openflow.protocol.action.OFAction;
+import org.openflow.protocol.action.OFActionType;
+import org.openflow.protocol.action.OFActionVendor;
+import org.openflow.protocol.statistics.OFStatistics;
+import org.openflow.protocol.statistics.OFStatisticsType;
+import org.openflow.protocol.statistics.OFVendorStatistics;
+import org.openflow.protocol.vendor.OFByteArrayVendorData;
+import org.openflow.protocol.vendor.OFVendorData;
+import org.openflow.protocol.vendor.OFVendorDataType;
+import org.openflow.protocol.vendor.OFVendorId;
+
+
+/**
+ * A basic OpenFlow factory that supports naive creation of both Messages and
+ * Actions.
+ *
+ * @author David Erickson (daviderickson@cs.stanford.edu)
+ * @author Rob Sherwood (rob.sherwood@stanford.edu)
+ *
+ */
+public class BasicFactory implements OFMessageFactory, OFActionFactory,
+        OFStatisticsFactory, OFVendorDataFactory {
+
+    private final OFVendorActionRegistry vendorActionRegistry;
+
+    public BasicFactory() {
+        vendorActionRegistry = OFVendorActionRegistry.getInstance();
+    }
+
+    /**
+     * create and return a new instance of a message for OFType t. Also injects
+     * factories for those message types that implement the *FactoryAware
+     * interfaces.
+     *
+     * @return a newly created instance that may be modified / used freely by
+     *         the caller
+     */
+    @Override
+    public OFMessage getMessage(OFType t) {
+        OFMessage message = t.newInstance();
+        injectFactories(message);
+        return message;
+    }
+
+    @Override
+    public List<OFMessage> parseMessage(ChannelBuffer data) throws MessageParseException {
+        List<OFMessage> msglist = new ArrayList<OFMessage>();
+        OFMessage msg = null;
+
+        while (data.readableBytes() >= OFMessage.MINIMUM_LENGTH) {
+            data.markReaderIndex();
+            msg = this.parseMessageOne(data);
+            if (msg == null) {
+                data.resetReaderIndex();
+                break;
+            }
+            else {
+                msglist.add(msg);
+            }
+        }
+
+        if (msglist.size() == 0) {
+            return null;
+        }
+        return msglist;
+
+    }
+
+    public OFMessage parseMessageOne(ChannelBuffer data) throws MessageParseException {
+        try {
+            OFMessage demux = new OFMessage();
+            OFMessage ofm = null;
+
+            if (data.readableBytes() < OFMessage.MINIMUM_LENGTH)
+                return ofm;
+
+            data.markReaderIndex();
+            demux.readFrom(data);
+            data.resetReaderIndex();
+
+            if (demux.getLengthU() > data.readableBytes())
+                return ofm;
+
+            ofm = getMessage(demux.getType());
+            if (ofm == null)
+                return null;
+
+            injectFactories(ofm);
+            ofm.readFrom(data);
+            if (OFMessage.class.equals(ofm.getClass())) {
+                // advance the position for un-implemented messages
+                data.readerIndex(data.readerIndex()+(ofm.getLengthU() -
+                        OFMessage.MINIMUM_LENGTH));
+            }
+
+            return ofm;
+        } catch (Exception e) {
+            /* Write the offending data along with the error message */
+            data.resetReaderIndex();
+            String msg =
+                    "Message Parse Error for packet:" +  dumpBuffer(data) +
+                    "\nException: " + e.toString();
+            data.resetReaderIndex();
+
+            throw new MessageParseException(msg, e);
+        }
+    }
+
+
+    
+    
+    private void injectFactories(OFMessage ofm) {
+        if (ofm instanceof OFActionFactoryAware) {
+            ((OFActionFactoryAware)ofm).setActionFactory(this);
+        }
+        if (ofm instanceof OFMessageFactoryAware) {
+            ((OFMessageFactoryAware)ofm).setMessageFactory(this);
+        }
+        if (ofm instanceof OFStatisticsFactoryAware) {
+            ((OFStatisticsFactoryAware)ofm).setStatisticsFactory(this);
+        }
+        if (ofm instanceof OFVendorDataFactoryAware) {
+            ((OFVendorDataFactoryAware)ofm).setVendorDataFactory(this);
+        }
+    }
+
+    @Override
+    public OFAction getAction(OFActionType t) {
+        return t.newInstance();
+    }
+
+    @Override
+    public List<OFAction> parseActions(ChannelBuffer data, int length) {
+        return parseActions(data, length, 0);
+    }
+
+    @Override
+    public List<OFAction> parseActions(ChannelBuffer data, int length, int limit) {
+        List<OFAction> results = new ArrayList<OFAction>();
+        OFAction demux = new OFAction();
+        OFAction ofa;
+        int end = data.readerIndex() + length;
+
+        while (limit == 0 || results.size() <= limit) {
+            if ((data.readableBytes() < OFAction.MINIMUM_LENGTH ||
+                (data.readerIndex() + OFAction.MINIMUM_LENGTH) > end))
+                return results;
+
+            data.markReaderIndex();
+            demux.readFrom(data);
+            data.resetReaderIndex();
+
+            if ((demux.getLengthU() > data.readableBytes() ||
+                (data.readerIndex() + demux.getLengthU()) > end))
+                return results;
+
+            ofa = parseActionOne(demux.getType(), data);
+            results.add(ofa);
+        }
+
+        return results;
+    }
+
+    private OFAction parseActionOne(OFActionType type, ChannelBuffer data) {
+        OFAction ofa;
+        data.markReaderIndex();
+        ofa = getAction(type);
+        ofa.readFrom(data);
+
+        if(type == OFActionType.VENDOR) {
+            OFActionVendor vendorAction = (OFActionVendor) ofa;
+
+            OFVendorActionFactory vendorActionFactory = vendorActionRegistry.get(vendorAction.getVendor());
+
+            if(vendorActionFactory != null) {
+                // if we have a specific vendorActionFactory for this vendor id,
+                // delegate to it for vendor-specific reparsing of the message
+                data.resetReaderIndex();
+                OFActionVendor newAction = vendorActionFactory.readFrom(data);
+                if(newAction != null)
+                    ofa = newAction;
+            }
+        }
+
+        if (OFAction.class.equals(ofa.getClass())) {
+            // advance the position for un-implemented messages
+            data.readerIndex(data.readerIndex()+(ofa.getLengthU() -
+                    OFAction.MINIMUM_LENGTH));
+        }
+        return ofa;
+    }
+
+    @Override
+    public OFActionFactory getActionFactory() {
+        return this;
+    }
+
+    @Override
+    public OFStatistics getStatistics(OFType t, OFStatisticsType st) {
+        return st.newInstance(t);
+    }
+
+    @Override
+    public List<OFStatistics> parseStatistics(OFType t, OFStatisticsType st,
+                                              ChannelBuffer data, int length) {
+        return parseStatistics(t, st, data, length, 0);
+    }
+
+    /**
+     * @param t
+     *            OFMessage type: should be one of stats_request or stats_reply
+     * @param st
+     *            statistics type of this message, e.g., DESC, TABLE
+     * @param data
+     *            buffer to read from
+     * @param length
+     *            length of statistics
+     * @param limit
+     *            number of statistics to grab; 0 == all
+     *
+     * @return list of statistics
+     */
+
+    @Override
+    public List<OFStatistics> parseStatistics(OFType t, OFStatisticsType st,
+            ChannelBuffer data, int length, int limit) {
+        List<OFStatistics> results = new ArrayList<OFStatistics>();
+        OFStatistics statistics = getStatistics(t, st);
+
+        int start = data.readerIndex();
+        int count = 0;
+
+        while (limit == 0 || results.size() <= limit) {
+            // TODO Create a separate MUX/DEMUX path for vendor stats
+            if (statistics instanceof OFVendorStatistics)
+                ((OFVendorStatistics)statistics).setLength(length);
+
+            /**
+             * can't use data.remaining() here, b/c there could be other data
+             * buffered past this message
+             */
+            if ((length - count) >= statistics.getLength()) {
+                if (statistics instanceof OFActionFactoryAware)
+                    ((OFActionFactoryAware)statistics).setActionFactory(this);
+                statistics.readFrom(data);
+                results.add(statistics);
+                count += statistics.getLength();
+                statistics = getStatistics(t, st);
+            } else {
+                if (count < length) {
+                    /**
+                     * Nasty case: partial/incomplete statistic found even
+                     * though we have a full message. Found when NOX sent
+                     * agg_stats request with wrong agg statistics length (52
+                     * instead of 56)
+                     *
+                     * just throw the rest away, or we will break framing
+                     */
+                    data.readerIndex(start + length);
+                }
+                return results;
+            }
+        }
+        return results; // empty; no statistics at all
+    }
+
+
+    @Override
+    public OFVendorData getVendorData(OFVendorId vendorId,
+                                      OFVendorDataType vendorDataType) {
+        if (vendorDataType == null)
+            return null;
+
+        return vendorDataType.newInstance();
+    }
+
+    /**
+     * Attempts to parse and return the OFVendorData contained in the given
+     * ChannelBuffer, beginning right after the vendor id.
+     * @param vendor the vendor id that was parsed from the OFVendor message.
+     * @param data the ChannelBuffer from which to parse the vendor data
+     * @param length the length to the end of the enclosing message.
+     * @return an OFVendorData instance
+     */
+    @Override
+    public OFVendorData parseVendorData(int vendor, ChannelBuffer data,
+            int length) {
+        OFVendorDataType vendorDataType = null;
+        OFVendorId vendorId = OFVendorId.lookupVendorId(vendor);
+        if (vendorId != null) {
+            data.markReaderIndex();
+            vendorDataType = vendorId.parseVendorDataType(data, length);
+            data.resetReaderIndex();
+        }
+
+        OFVendorData vendorData = getVendorData(vendorId, vendorDataType);
+        if (vendorData == null)
+            vendorData = new OFByteArrayVendorData();
+
+        vendorData.readFrom(data, length);
+
+        return vendorData;
+    }
+
+    public static String dumpBuffer(ChannelBuffer data) {
+        // NOTE: Reads all the bytes in buffer from current read offset.
+        // Set/Reset ReaderIndex if you want to read from a different location
+        int len = data.readableBytes();
+        StringBuffer sb = new StringBuffer();
+        for (int i=0 ; i<len; i++) {
+            if (i%32 == 0) sb.append("\n");
+            if (i%4 == 0) sb.append(" ");
+            sb.append(String.format("%02x", data.getUnsignedByte(i)));
+        }
+        return sb.toString();
+    }
+
+}