Expanded CMTS emulator to respond properly to gate add requests. 03/20303/1
authorSteven Pisarski <s.pisarski@cablelabs.com>
Wed, 13 May 2015 22:41:12 +0000 (16:41 -0600)
committerSteven Pisarski <s.pisarski@cablelabs.com>
Wed, 13 May 2015 22:41:12 +0000 (16:41 -0600)
Futher expansion needed for gate deletes and the addition of some more validations around the tests contained in PCMMServiceTest. Additionally, the mock CMTS should be moved into its own module as well as adding command line configuration options.

Change-Id: Id72e1c14ad015c76ef3e50824f22555a00f8a267
Signed-off-by: Steven Pisarski <s.pisarski@cablelabs.com>
14 files changed:
packetcable-driver/src/main/java/org/pcmm/gates/IPCMMError.java
packetcable-driver/src/main/java/org/pcmm/rcd/IPCMMClient.java
packetcable-driver/src/main/java/org/pcmm/rcd/IPCMMServer.java
packetcable-driver/src/main/java/org/pcmm/rcd/impl/AbstractPCMMClient.java
packetcable-driver/src/main/java/org/pcmm/rcd/impl/AbstractPCMMServer.java
packetcable-driver/src/main/java/org/pcmm/rcd/impl/CMTS.java
packetcable-driver/src/main/java/org/pcmm/rcd/impl/CmtsDataProcessor.java [new file with mode: 0644]
packetcable-driver/src/main/java/org/pcmm/rcd/impl/CmtsPcmmClientHandler.java [new file with mode: 0644]
packetcable-driver/src/main/java/org/pcmm/rcd/impl/CmtsPepReqStateMan.java [new file with mode: 0644]
packetcable-driver/src/main/java/org/pcmm/rcd/impl/PcmmCmtsConnection.java [new file with mode: 0644]
packetcable-driver/src/main/java/org/umu/cops/prpdp/COPSPdpAgent.java
packetcable-driver/src/main/java/org/umu/cops/prpep/COPSPepReqStateMan.java
packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMService.java
packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PCMMServiceTest.java

index bd0b47a51b08d5eceb7bf3d954f5876c348bf937..394f0bf061d2d8c03dbbb2e70c7a721228764b44 100644 (file)
@@ -1,6 +1,7 @@
-/**
- @header@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
  */
+
 package org.pcmm.gates;
 
 import org.pcmm.base.IPCMMBaseObject;
@@ -9,10 +10,10 @@ import org.pcmm.base.IPCMMBaseObject;
  *
  */
 public interface IPCMMError extends IPCMMBaseObject {
-       static final short LENGTH = 8;
-       static final byte SNUM = 14;
-       static final byte STYPE = 1;
-       final String[] errors = { "Insufficient Resources", "Unknown GateID",
+       short LENGTH = 8;
+       byte SNUM = 14;
+       byte STYPE = 1;
+       String[] errors = { "Insufficient Resources", "Unknown GateID",
                        "Missing Required Object", "Invalid Object",
                        "Volume Based Usage Limit Exceeded",
                        "Time Based Usage Limit Exceeded", "Session Class Limit Exceeded",
@@ -32,7 +33,7 @@ public interface IPCMMError extends IPCMMBaseObject {
                        "Multicast Downstream Resequencing mismatch",
                        "Other, Unspecified Error" };
 
-       static enum Description {
+       enum Description {
                ERROR_01((short) 1, errors[0]), ERROR_02((short) 2, errors[1]), ERROR_06(
                                (short) 6, errors[2]), ERROR_07((short) 7, errors[3]), ERROR_08(
                                (short) 8, errors[4]), ERROR_09((short) 9, errors[5]), ERROR_10(
@@ -54,7 +55,7 @@ public interface IPCMMError extends IPCMMBaseObject {
                private final short code;
                private final String description;
 
-               private Description(short code, String description) {
+               Description(short code, String description) {
                        this.code = code;
                        this.description = description;
                }
@@ -71,6 +72,7 @@ public interface IPCMMError extends IPCMMBaseObject {
                        switch (errCode) {
                        case 1:
                        case 2:
+                       case 4:
                                return errors[errCode - 1];
                        case 127:
                                return errors[32];
index 4317021d76a2cd5a86a7e58d549b30c0469bfc8d..81cdbd67e7fb5f3101657029b05d0761cd182c57 100644 (file)
@@ -1,12 +1,13 @@
-/**
- @header@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
  */
 
 package org.pcmm.rcd;
 
+import org.umu.cops.stack.COPSHandle;
 import org.umu.cops.stack.COPSMsg;
 
-import java.net.InetAddress;
+import java.io.IOException;
 
 /**
  * <p>
@@ -26,7 +27,7 @@ public interface IPCMMClient {
        /**
         * PCMM client-type
         */
-       static final short CLIENT_TYPE = (short) 0x800A;
+       short CLIENT_TYPE = (short) 0x800A;
 
        /**
         * sends a message to the server.
@@ -45,25 +46,9 @@ public interface IPCMMClient {
 
        /**
         * tries to connect to the server.
-        * 
-        * @param address
-        *            server address
-        * @param port
-        *            server port
-        * @return connection state
+        * @throws IOException
         */
-       boolean tryConnect(String address, int port);
-
-       /**
-        * tries to connect to the server.
-        * 
-        * @param address
-        *            server address
-        * @param port
-        *            server port
-        * @return connection state
-        */
-       boolean tryConnect(InetAddress address, int port);
+       void connect() throws IOException;
 
        /**
         * disconnects from server.
@@ -83,15 +68,8 @@ public interface IPCMMClient {
         * 
         * @return client handle
         */
-       String getClientHandle();
+       COPSHandle getClientHandle();
 
-       /**
-        * 
-        * sets the client handle
-        * 
-        * @param handle
-        *            cleint hanlde
-        */
-       void setClientHandle(String handle);
+       void setClientHandle(COPSHandle handle);
 
 }
index 794af06dfaee2a8004af07cab5f70be340caabdc..e27e1ef93e1762c2450a7853505ef39b1c1e84a9 100644 (file)
@@ -1,13 +1,14 @@
-/**
- @header@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
  */
 
-
 package org.pcmm.rcd;
 
 import org.pcmm.concurrent.IWorker;
 import org.pcmm.state.IStateful;
 
+import java.io.IOException;
+
 /**
  * <p>
  * As discussed in RFC 2753 [11], the policy management framework underlying
@@ -64,28 +65,27 @@ import org.pcmm.state.IStateful;
  * with scalability and fault-tolerance.
  * </p>
  * </p>
- *
- *
  */
 public interface IPCMMServer extends IStateful {
 
     /**
-     *
+     * Starts all connections and threads
      */
-    void startServer();
+    void startServer() throws IOException;
 
     /**
-     *
+     * Stops all connections and threads
      */
     void stopServer();
 
     /**
      * When a client connects to the server, a handler is needed to manage the
      * exchange of the messages between this client and the server.
-     *
-     *
      */
-    public static interface IPCMMClientHandler extends IWorker, IPCMMClient {
-
+    interface IPCMMClientHandler extends IWorker, IPCMMClient {
+        /**
+         * Responsible for closing resources such as server connections or threads
+         */
+        void stop();
     }
 }
index 6a29743c823b9c92ee08649c46e3701473067389..639265e8894826231844652ea56678553a9c163a 100644 (file)
-/**
- @header@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
  */
-package org.pcmm.rcd.impl;
 
-import java.io.IOException;
-import java.net.InetAddress;
-import java.net.Socket;
-import java.net.UnknownHostException;
+package org.pcmm.rcd.impl;
 
 import org.pcmm.nio.PCMMChannel;
-// import org.junit.Assert;
 import org.pcmm.objects.MMVersionInfo;
 import org.pcmm.rcd.IPCMMClient;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
+import org.umu.cops.stack.COPSHandle;
 import org.umu.cops.stack.COPSMsg;
 
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.Socket;
+
+// import org.junit.Assert;
+
 /**
- * 
+ *
  * default implementation for {@link IPCMMClient}
- * 
- * 
+ *
+ *
  */
 public class AbstractPCMMClient implements IPCMMClient {
 
-       protected Logger logger = LoggerFactory.getLogger(AbstractPCMMClient.class);
-       /**
-        * socket used to communicated with server.
-        */
-       private Socket socket;
-
-       private String clientHanlde;
-
-       private MMVersionInfo versionInfo;
-
-       private PCMMChannel channel;
-
-       public AbstractPCMMClient() {
-       }
-
-       /*
-        * (non-Javadoc)
-        * 
-        * @see pcmm.rcd.IPCMMClient#sendRequest(pcmm.messages.IMessage)
-        */
-       public void sendRequest(COPSMsg requestMessage) {
-               try {
-                       channel.sendMsg(requestMessage);
-               } catch (Exception e) {
-                       logger.error(e.getMessage(), getSocket());
-               }
-       }
-
-       /*
-        * (non-Javadoc)
-        * 
-        * @see org.pcmm.rcd.IPCMMClient#readMessage()
-        */
-       public COPSMsg readMessage() {
-               try {
-                       COPSMsg recvdMsg = channel.receiveMessage();
-                       // logger.debug("received message : " + recvdMsg.getHeader());
-                       return recvdMsg;
-               } catch (Exception e) {
-                       logger.error(e.getMessage(), getSocket());
-               }
-               return null;
-       }
-
-       /*
-        * (non-Javadoc)
-        * 
-        * @see pcmm.rcd.IPCMMClient#tryConnect(java.lang.String, int)
-        */
-       public boolean tryConnect(String address, int port) {
-               try {
-                       InetAddress addr = InetAddress.getByName(address);
-                       tryConnect(addr, port);
-               } catch (UnknownHostException e) {
-                       logger.error(e.getMessage());
-                       return false;
-               }
-               return true;
-       }
-
-       /*
-        * (non-Javadoc)
-        * 
-        * @see pcmm.rcd.IPCMMClient#tryConnect(java.net.InetAddress, int)
-        */
-       public boolean tryConnect(InetAddress address, int port) {
-               try {
-                       setSocket(new Socket(address, port));
-               } catch (IOException e) {
-                       logger.error(e.getMessage());
-                       return false;
-               }
-               return true;
-       }
-
-       /*
-        * (non-Javadoc)
-        * 
-        * @see pcmm.rcd.IPCMMClient#disconnect()
-        */
-       public boolean disconnect() {
-               if (isConnected()) {
-                       try {
-                               socket.close();
-                               channel = null;
-                       } catch (IOException e) {
-                               logger.error(e.getMessage());
-                       }
-               }
-               return true;
-       }
-
-       /**
-        * @return the socket
-        */
-       public Socket getSocket() {
-               return socket;
-       }
-
-       public PCMMChannel getChannel() {
-               return channel;
-       }
-
-       /**
-        * @param socket
-        *            the socket to set
-        */
-       public void setSocket(Socket socket) {
-               this.socket = socket;
-               if (this.socket != null
-                               && (this.channel == null || !this.channel.getSocket().equals(
-                                               this.socket)))
-                       channel = new PCMMChannel(this.socket);
-       }
-
-       /*
-        * (non-Javadoc)
-        * 
-        * @see org.pcmm.rcd.IPCMMClient#isConnected()
-        */
-       public boolean isConnected() {
-               return socket != null && socket.isConnected();
-       }
-
-       /**
-        * @return the versionInfo
-        */
-       public MMVersionInfo getVersionInfo() {
-               return versionInfo;
-       }
-
-       /**
-        * @param versionInfo
-        *            the versionInfo to set
-        */
-       public void setVersionInfo(MMVersionInfo versionInfo) {
-               this.versionInfo = versionInfo;
-       }
-
-       @Override
-       public String getClientHandle() {
-               return clientHanlde;
-       }
-
-       @Override
-       public void setClientHandle(String handle) {
-               this.clientHanlde = handle;
-       }
+    private final static Logger logger = LoggerFactory.getLogger(AbstractPCMMClient.class);
+
+    private final String host;
+
+    private final int port;
+
+    // TODO - consider removing this attribute as it is never being used
+    private MMVersionInfo versionInfo;
+
+    // Following two attributes are set when connect() is called
+    /**
+     * socket used to communicated with server.
+     */
+    private transient Socket socket;
+
+    private transient PCMMChannel channel;
+
+    // TODO - determine why this class holds a handle as it is not being used.
+    private transient COPSHandle clientHandle;
+
+    /**
+     * When true, this means the socket object will be generated via the host name and port number vs. being injected.
+     * In this case, the socket will be closed on disconnect(), else, the client creating the socked object will be
+     * responsible.
+     */
+    private final boolean ownSocket;
+
+    public AbstractPCMMClient(final String host, final int port) {
+        this.host = host;
+        this.port = port;
+        this.ownSocket = true;
+    }
+
+    public AbstractPCMMClient(final Socket socket) {
+        this.host = socket.getInetAddress().getHostName();
+        this.port = socket.getPort();
+        this.socket = socket;
+        this.ownSocket = false;
+    }
+
+    @Override
+    public void connect() throws IOException {
+        if (socket == null) {
+            socket = new Socket(InetAddress.getByName(host), port);
+        }
+        channel = new PCMMChannel(this.socket);
+    }
+
+    @Override
+    public boolean disconnect() {
+        if (isConnected()) {
+            try {
+                if (ownSocket) {
+                    socket.close();
+                }
+                channel = null;
+            } catch (IOException e) {
+                logger.error(e.getMessage());
+            }
+        }
+        return true;
+    }
+
+    @Override
+    public void sendRequest(COPSMsg requestMessage) {
+        try {
+            channel.sendMsg(requestMessage);
+        } catch (Exception e) {
+            logger.error(e.getMessage(), getSocket());
+        }
+    }
+
+    @Override
+    public COPSMsg readMessage() {
+        try {
+            COPSMsg recvdMsg = channel.receiveMessage();
+            logger.debug("received message : " + recvdMsg.getHeader());
+            return recvdMsg;
+        } catch (Exception e) {
+            logger.error(e.getMessage(), getSocket());
+        }
+        return null;
+    }
+
+    /**
+     * @return the socket
+     */
+    public Socket getSocket() {
+        return socket;
+    }
+
+    @Override
+    public boolean isConnected() {
+        return socket != null && socket.isConnected();
+    }
+
+    /**
+     * @return the versionInfo
+     */
+    public MMVersionInfo getVersionInfo() {
+        return versionInfo;
+    }
+
+    /**
+     * @param versionInfo
+     *            the versionInfo to set
+     */
+    public void setVersionInfo(MMVersionInfo versionInfo) {
+        this.versionInfo = versionInfo;
+    }
+
+    @Override
+    public COPSHandle getClientHandle() {
+        return clientHandle;
+    }
+
+    @Override
+    public void setClientHandle(final COPSHandle handle) {
+        this.clientHandle = handle;
+    }
 
 }
index 5bd01d491f2e196ae484d7c1b884086f571abaae..eeb3b0eb096cf96ed7e1f1330315f128bcbe467b 100644 (file)
@@ -1,23 +1,22 @@
-/**
- @header@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
  */
+
 package org.pcmm.rcd.impl;
 
 import org.pcmm.PCMMConstants;
 import org.pcmm.PCMMProperties;
 import org.pcmm.concurrent.IWorkerPool;
 import org.pcmm.concurrent.impl.WorkerPool;
-import org.pcmm.messages.impl.MessageFactory;
 import org.pcmm.rcd.IPCMMServer;
 import org.pcmm.state.IState;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
-import org.umu.cops.stack.COPSHeader.OPCode;
-import org.umu.cops.stack.COPSMsg;
 
 import java.io.IOException;
 import java.net.ServerSocket;
 import java.net.Socket;
+import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
 
 // import org.junit.Assert;
@@ -28,22 +27,28 @@ import java.util.concurrent.Executors;
  * @see pcmm.rcd.IPCMMServer
  */
 public abstract class AbstractPCMMServer implements IPCMMServer {
-       protected Logger logger;
+
+       private final static Logger logger = LoggerFactory.getLogger(AbstractPCMMServer.class);
+
        /*
         * A ServerSocket to accept messages ( OPN requests)
         */
-       private ServerSocket serverSocket;
-
-       private Socket stopSocket;
+       private transient ServerSocket serverSocket;
 
        private volatile boolean keepAlive;
-       /*
-     *
-     */
-       private int port;
+
+       /**
+        * The port number on which to start the server.
+        */
+       private final int port;
 
        IWorkerPool pool;
 
+       /**
+        * The thread pool executor
+        */
+       private final ExecutorService executorService;
+
        /**
         * Constructor to use the port number contained within the PCMMProperties static object
         */
@@ -60,9 +65,9 @@ public abstract class AbstractPCMMServer implements IPCMMServer {
                // XXX - Assert.assertTrue(port >= 0 && port <= 65535);
                this.port = port;
                keepAlive = true;
-               logger = LoggerFactory.getLogger(getClass().getName());
                int poolSize = PCMMProperties.get(PCMMConstants.PS_POOL_SIZE, Integer.class);
                pool = new WorkerPool(poolSize);
+               executorService = Executors.newSingleThreadExecutor();
        }
 
        /*
@@ -70,18 +75,15 @@ public abstract class AbstractPCMMServer implements IPCMMServer {
         * 
         * @see pcmm.rcd.IPCMMServer#startServer()
         */
-       public void startServer() {
+       public void startServer() throws IOException {
                if (serverSocket != null)
                        return;
-               try {
-                       serverSocket = new ServerSocket(port);
-                       port = serverSocket.getLocalPort();
-                       logger.info("Server started and listening on port :" + port);
-               } catch (IOException e) {
-                       logger.error(e.getMessage());
-               }
+
+               serverSocket = new ServerSocket(port);
+               logger.info("Server started and listening on port :" + port);
+
                // execute this in a single thread executor
-               Executors.newSingleThreadExecutor().execute(new Runnable() {
+               executorService.execute(new Runnable() {
                        public void run() {
                                while (keepAlive) {
                                        try {
@@ -98,19 +100,7 @@ public abstract class AbstractPCMMServer implements IPCMMServer {
                                                logger.error(e.getMessage());
                                        }
                                }
-                               try {
-                                       if (stopSocket != null && stopSocket.isConnected()) {
-                                               logger.info("Cleaning up");
-                                               stopSocket.close();
-                                       }
-                                       if (serverSocket != null && serverSocket.isBound()) {
-                                               logger.info("Server about to stop");
-                                               serverSocket.close();
-                                               logger.info("Server stopped");
-                                       }
-                               } catch (IOException e) {
-                                       logger.error(e.getMessage());
-                               }
+                               stopServer();
                        }
                });
        }
@@ -122,40 +112,29 @@ public abstract class AbstractPCMMServer implements IPCMMServer {
         * @param socket - the connection to the PCMM server
         * @return client handler
         */
-       protected abstract IPCMMClientHandler getPCMMClientHandler(Socket socket);
+       protected abstract IPCMMClientHandler getPCMMClientHandler(Socket socket) throws IOException;
 
-       /*
-        * (non-Javadoc)
-        * 
-        * @see pcmm.rcd.IPCMMServer#stopServer()
-        */
+       @Override
        public void stopServer() {
                // set to stop
                keepAlive = false;
+               executorService.shutdownNow();
                try {
                        if (serverSocket != null) {
-                               stopSocket = new Socket(serverSocket.getInetAddress(), serverSocket.getLocalPort());
-                               logger.info("STOP socket created and attached");
+                               serverSocket.close();
                        }
                } catch (Exception e) {
                        logger.error(e.getMessage());
                }
+               pool.killAll();
        }
 
-       /*
-        * (non-Javadoc)
-        * 
-        * @see pcmm.state.IStateful#recordState()
-        */
+       @Override
        public void recordState() {
-
+               // TODO - implement me
        }
 
-       /*
-        * (non-Javadoc)
-        * 
-        * @see pcmm.state.IStateful#getRecoredState()
-        */
+       @Override
        public IState getRecoredState() {
                return null;
        }
@@ -164,46 +143,8 @@ public abstract class AbstractPCMMServer implements IPCMMServer {
         * @return the port
         */
        public int getPort() {
-               return port;
-       }
-
-       /**
-        * @param port
-        *            the port to set
-        */
-       public void setPort(int port) {
-               this.port = port;
-       }
-
-       /*
-        * (non-Javadoc)
-        * 
-        * @see pcmm.rcd.IPCMMServer.IPCMMClientHandler
-        */
-       public abstract class AbstractPCMMClientHandler extends AbstractPCMMClient
-                       implements IPCMMClientHandler {
-
-               protected boolean sendCCMessage = false;
-
-               public AbstractPCMMClientHandler(Socket socket) {
-                       super();
-                       setSocket(socket);
-               }
-
-               @Override
-               public boolean disconnect() {
-                       // XXX send CC message
-                       sendCCMessage = true;
-                       /*
-                        * is this really needed ?
-                        */
-                       // if (getSocket() != null)
-                       // handlersPool.remove(getSocket());
-                       COPSMsg message = MessageFactory.getInstance().create(OPCode.CC);
-                       sendRequest(message);
-                       return super.disconnect();
-               }
-
+               if (serverSocket != null && serverSocket.isBound()) return serverSocket.getLocalPort();
+               else return this.port;
        }
 
 }
index 4a89bad0e0733d9a740dc43778746299db1a80a8..1c9f29cd3eca1dc1128e9deef4e0a0a6a5b7dadf 100644 (file)
-/**
- @header@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
  */
+
 package org.pcmm.rcd.impl;
 
-import org.pcmm.gates.IPCMMGate;
-import org.pcmm.gates.ITransactionID;
-import org.pcmm.gates.impl.PCMMGateReq;
-import org.pcmm.messages.impl.MessageFactory;
+import org.pcmm.PCMMConstants;
+import org.pcmm.PCMMProperties;
+import org.pcmm.gates.IGateSpec.Direction;
 import org.pcmm.rcd.ICMTS;
-import org.umu.cops.COPSStateMan;
-import org.umu.cops.prpep.COPSPepConnection;
-import org.umu.cops.prpep.COPSPepDataProcess;
-import org.umu.cops.prpep.COPSPepException;
-import org.umu.cops.prpep.COPSPepReqStateMan;
-import org.umu.cops.stack.*;
-import org.umu.cops.stack.COPSHeader.OPCode;
 
+import java.io.IOException;
 import java.net.Socket;
 import java.util.*;
-import java.util.concurrent.Callable;
+import java.util.concurrent.ConcurrentHashMap;
 
 /**
  * This class starts a mock CMTS that can be used for testing.
  */
 public class CMTS extends AbstractPCMMServer implements ICMTS {
 
+       /**
+        * Receives messages from the COPS client
+        */
+       private final Map<String, IPCMMClientHandler> handlerMap;
+
+       /**
+        * The configured gates
+        */
+       private final Map<Direction, Set<String>> gateConfig;
+
+       /**
+        * The connected CMTSs and whether or not they are up
+        */
+       private final Map<String, Boolean> cmStatus;
+
        /**
         * Constructor for having the server port automatically assigned
         * Call getPort() after startServer() is called to determine the port number of the server
         */
-       public CMTS() {
-               this(0);
+       public CMTS(final Map<Direction, Set<String>> gateConfig, final Map<String, Boolean> cmStatus) {
+               this(0, gateConfig, cmStatus);
        }
 
        /**
         * Constructor for starting the server to a pre-defined port number
         * @param port - the port number on which to start the server.
         */
-       public CMTS(final int port) {
+       public CMTS(final int port, final Map<Direction, Set<String>> gateConfig, final Map<String, Boolean> cmStatus) {
                super(port);
+               if (gateConfig == null || cmStatus == null) throw new IllegalArgumentException("Config must not be null");
+               this.gateConfig = Collections.unmodifiableMap(gateConfig);
+               this.cmStatus = Collections.unmodifiableMap(cmStatus);
+               handlerMap = new ConcurrentHashMap<>();
        }
 
        @Override
-       protected IPCMMClientHandler getPCMMClientHandler(final Socket socket) {
-
-               return new AbstractPCMMClientHandler(socket) {
-
-                       private COPSHandle handle;
-
-                       public void run() {
-                               try {
-                                       // send OPN message
-                                       // set the major version info and minor version info to
-                                       // default (5,0)
-                                       logger.info("Send OPN message to the PS");
-                                       sendRequest(MessageFactory.getInstance().create(OPCode.OPN, new Properties()));
-                                       // wait for CAT
-                                       COPSMsg recvMsg = readMessage();
-
-                                       if (recvMsg.getHeader().getOpCode().equals(OPCode.CC)) {
-                                               COPSClientCloseMsg cMsg = (COPSClientCloseMsg) recvMsg;
-                                               logger.info("PS requested Client-Close" + cMsg.getError().getDescription());
-                                               // send a CC message and close the socket
-                                               disconnect();
-                                               return;
-                                       }
-                                       if (recvMsg.getHeader().getOpCode().equals(OPCode.CAT)) {
-                                               logger.info("received Client-Accept from PS");
-                                               COPSClientAcceptMsg cMsg = (COPSClientAcceptMsg) recvMsg;
-                                               // Support
-                                               if (cMsg.getIntegrity() != null) {
-                                                       throw new COPSPepException("Unsupported object (Integrity)");
-                                               }
-
-                                               // Mandatory KATimer
-                                               COPSKATimer kt = cMsg.getKATimer();
-                                               if (kt == null)
-                                                       throw new COPSPepException("Mandatory COPS object missing (KA Timer)");
-                                               short kaTimeVal = kt.getTimerVal();
-
-                                               // ACTimer
-                                               COPSAcctTimer at = cMsg.getAcctTimer();
-                                               short acctTimer = 0;
-                                               if (at != null)
-                                                       acctTimer = at.getTimerVal();
-
-                                               logger.info("Send a REQ message to the PS");
-                                               {
-                                                       Properties prop = new Properties();
-                                                       COPSMsg reqMsg = MessageFactory.getInstance().create(OPCode.REQ, prop);
-                                                       handle = ((COPSReqMsg) reqMsg).getClientHandle();
-                                                       sendRequest(reqMsg);
-                                               }
-                                               // Create the connection manager
-                                               final PCMMCmtsConnection conn = new PCMMCmtsConnection(CLIENT_TYPE, socket);
-                                               // pcmm specific handler
-                                               // conn.addReqStateMgr(handle, new
-                                               // PCMMPSReqStateMan(CLIENT_TYPE, handle));
-                                               conn.addRequestState(handle, new CmtsDataProcessor());
-                                               conn.setKaTimer(kaTimeVal);
-                                               conn.setAcctTimer(acctTimer);
-                                               logger.info(getClass().getName() + " Thread(conn).start");
-                                               new Thread(conn).start();
-                                       } else {
-                                               // messages of other types are not expected
-                                               throw new COPSPepException("Message not expected. Closing connection for " + socket.toString());
-                                       }
-                               } catch (Exception e) {
-                                       logger.error(e.getMessage());
-                               }
-                       }
-
-                       @Override
-                       public void task(Callable<?> c) {
-                               // TODO Auto-generated method stub
-
-                       }
-
-                       @Override
-                       public void shouldWait(int t) {
-                               // TODO Auto-generated method stub
-
-                       }
-
-                       @Override
-                       public void done() {
-                               // TODO Auto-generated method stub
-
-                       }
-
-               };
-       }
-
-       class PCMMCmtsConnection extends COPSPepConnection {
-
-               public PCMMCmtsConnection(final short clientType, final Socket sock) {
-                       super(clientType, sock);
-               }
-
-               public COPSPepReqStateMan addRequestState(final COPSHandle clientHandle, final COPSPepDataProcess process)
-                               throws COPSException {
-                       return super.addRequestState(clientHandle, process);
+       public void stopServer() {
+               for (final IPCMMClientHandler handler : handlerMap.values()) {
+                       handler.stop();
                }
+               super.stopServer();
        }
 
-       class CmtsDataProcessor implements COPSPepDataProcess {
-
-               private Map<String, String> removeDecs;
-               private Map<String, String> installDecs;
-               private Map<String, String> errorDecs;
-               private COPSPepReqStateMan stateManager;
-
-               public CmtsDataProcessor() {
-                       setRemoveDecs(new HashMap<String, String>());
-                       setInstallDecs(new HashMap<String, String>());
-                       setErrorDecs(new HashMap<String, String>());
-               }
-
-               @Override
-               public void setDecisions(final COPSPepReqStateMan man, final Map<String, String> removeDecs,
-                                 final Map<String, String> installDecs, final Map<String, String> errorDecs) {
-                       setRemoveDecs(removeDecs);
-                       setInstallDecs(installDecs);
-                       setErrorDecs(errorDecs);
-                       setStateManager(man);
-               }
-
-               @Override
-               public boolean isFailReport(final COPSPepReqStateMan man) {
-                       return (errorDecs != null && errorDecs.size() > 0);
-               }
-
-               @Override
-               public Map<String, String> getReportData(final COPSPepReqStateMan man) {
-                       if (isFailReport(man)) {
-                               return errorDecs;
-                       } else {
-                               final Map<String, String> siDataHashTable = new HashMap<>();
-                               if (installDecs.size() > 0) {
-                                       String data = "";
-                                       for (String k : installDecs.keySet()) {
-                                               data = installDecs.get(k);
-                                               break;
-                                       }
-                                       final ITransactionID transactionID = new PCMMGateReq(new COPSData(data).getData()).getTransactionID();
-                                       final IPCMMGate responseGate = new PCMMGateReq();
-                                       responseGate.setTransactionID(transactionID);
-
-                    // TODO FIXME - Why is the key always null??? What value should be used here???
-                    final String key = null;
-                                       siDataHashTable.put(key, new String(responseGate.getData()));
-                               }
-                               return siDataHashTable;
-                       }
-               }
-
-               @Override
-               public Map<String, String> getClientData(COPSPepReqStateMan man) {
-                       // TODO Auto-generated method stub
-                       return new HashMap<>();
-               }
-
-               @Override
-               public Map<String, String> getAcctData(COPSPepReqStateMan man) {
-                       // TODO Auto-generated method stub
-                       return new HashMap<>();
-               }
-
-               @Override
-               public void notifyClosedConnection(final COPSStateMan man, final COPSError error) {
-                       // TODO Auto-generated method stub
-               }
-
-               @Override
-               public void notifyNoKAliveReceived(final COPSStateMan man) {
-                       // TODO Auto-generated method stub
-               }
-
-               @Override
-               public void closeRequestState(final COPSStateMan man) {
-                       // TODO Auto-generated method stub
-               }
-
-               @Override
-               public void newRequestState(final COPSPepReqStateMan man) {
-                       // TODO Auto-generated method stub
-               }
-
-               public Map<String, String> getRemoveDecs() {
-                       return new HashMap<>(removeDecs);
-               }
-
-               public void setRemoveDecs(final Map<String, String> removeDecs) {
-                       this.removeDecs = new HashMap<>(removeDecs);
-               }
-
-               public Map<String, String> getInstallDecs() {
-                       return new HashMap<>(installDecs);
-               }
-
-               public void setInstallDecs(final Map<String, String> installDecs) {
-                       this.installDecs = new HashMap<>(installDecs);
-               }
-
-               public Map<String, String> getErrorDecs() {
-                       return errorDecs;
-               }
-
-               public void setErrorDecs(final Map<String, String> errorDecs) {
-                       this.errorDecs = new HashMap<>(errorDecs);
-               }
-
-               public COPSPepReqStateMan getStateManager() {
-                       return stateManager;
-               }
-
-               public void setStateManager(COPSPepReqStateMan stateManager) {
-                       this.stateManager = stateManager;
-               }
+       @Override
+       protected IPCMMClientHandler getPCMMClientHandler(final Socket socket) throws IOException {
+               final String key = socket.getLocalAddress().getHostName() + ':' + socket.getPort();
+               if (handlerMap.get(key) == null) {
+                       final IPCMMClientHandler handler = new CmtsPcmmClientHandler(socket, gateConfig, cmStatus);
+                       handler.connect();
+                       handlerMap.put(key, handler);
+                       return handler;
+               } else return handlerMap.get(key);
+       }
 
+       /**
+        * To start a CMTS
+        * @param args - the arguments which will contain configuration information
+        * @throws IOException - should the server fail to start for reasons such as port contention.
+        */
+       public static void main(final String[] args) throws IOException {
+               final CMTS cmts = new CMTS(PCMMProperties.get(PCMMConstants.PCMM_PORT, Integer.class),
+                               new HashMap<Direction, Set<String>>(), new HashMap<String, Boolean>());
+               cmts.startServer();
        }
+
 }
diff --git a/packetcable-driver/src/main/java/org/pcmm/rcd/impl/CmtsDataProcessor.java b/packetcable-driver/src/main/java/org/pcmm/rcd/impl/CmtsDataProcessor.java
new file mode 100644 (file)
index 0000000..c63f402
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
+ */
+
+package org.pcmm.rcd.impl;
+
+import org.pcmm.gates.IPCMMGate;
+import org.pcmm.gates.ITransactionID;
+import org.pcmm.gates.impl.PCMMGateReq;
+import org.umu.cops.COPSStateMan;
+import org.umu.cops.prpep.COPSPepDataProcess;
+import org.umu.cops.prpep.COPSPepReqStateMan;
+import org.umu.cops.stack.COPSData;
+import org.umu.cops.stack.COPSError;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * Data processor for the CMTS emulator.
+ */
+class CmtsDataProcessor implements COPSPepDataProcess {
+
+    private transient Map<String, String> installDecs = new ConcurrentHashMap<>();
+    private transient Map<String, String> errorDecs = new ConcurrentHashMap<>();
+
+    @Override
+    public void setDecisions(final COPSPepReqStateMan man, final Map<String, String> removeDecs,
+                             final Map<String, String> installDecs, final Map<String, String> errorDecs) {
+
+        // TODO - parameters man & removeDecs not used. They were members when encapsulated in CMTS but were never used.
+        this.installDecs = new ConcurrentHashMap<>(installDecs);
+        this.errorDecs = new ConcurrentHashMap<>(errorDecs);
+    }
+
+    @Override
+    public boolean isFailReport(final COPSPepReqStateMan man) {
+        return (errorDecs != null && errorDecs.size() > 0);
+    }
+
+    @Override
+    public Map<String, String> getReportData(final COPSPepReqStateMan man) {
+        if (isFailReport(man)) {
+            return errorDecs;
+        } else {
+            final Map<String, String> siDataHashTable = new HashMap<>();
+            if (installDecs.size() > 0) {
+                String data = "";
+                for (String k : installDecs.keySet()) {
+                    data = installDecs.get(k);
+                    break;
+                }
+                final ITransactionID transactionID = new PCMMGateReq(new COPSData(data).getData()).getTransactionID();
+                final IPCMMGate responseGate = new PCMMGateReq();
+                responseGate.setTransactionID(transactionID);
+
+                // TODO FIXME - Why is the key always null??? What value should be used here???
+                final String key = null;
+                siDataHashTable.put(key, new String(responseGate.getData()));
+            }
+            return siDataHashTable;
+        }
+    }
+
+    @Override
+    public Map<String, String> getClientData(COPSPepReqStateMan man) {
+        // TODO Auto-generated method stub
+        return new HashMap<>();
+    }
+
+    @Override
+    public Map<String, String> getAcctData(COPSPepReqStateMan man) {
+        // TODO Auto-generated method stub
+        return new HashMap<>();
+    }
+
+    @Override
+    public void notifyClosedConnection(final COPSStateMan man, final COPSError error) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void notifyNoKAliveReceived(final COPSStateMan man) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void closeRequestState(final COPSStateMan man) {
+        // TODO Auto-generated method stub
+    }
+
+    @Override
+    public void newRequestState(final COPSPepReqStateMan man) {
+        // TODO Auto-generated method stub
+    }
+
+}
diff --git a/packetcable-driver/src/main/java/org/pcmm/rcd/impl/CmtsPcmmClientHandler.java b/packetcable-driver/src/main/java/org/pcmm/rcd/impl/CmtsPcmmClientHandler.java
new file mode 100644 (file)
index 0000000..e54af54
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
+ */
+
+package org.pcmm.rcd.impl;
+
+import org.pcmm.gates.IGateSpec.Direction;
+import org.pcmm.messages.impl.MessageFactory;
+import org.pcmm.rcd.IPCMMServer.IPCMMClientHandler;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.umu.cops.prpep.COPSPepException;
+import org.umu.cops.stack.*;
+import org.umu.cops.stack.COPSHeader.OPCode;
+
+import java.net.Socket;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Properties;
+import java.util.Set;
+import java.util.concurrent.Callable;
+
+/**
+ * This class was created by moving an anonymous inner class from CMTS.java and is responsible for creating a persistent
+ * connection with a PEP.
+ */
+public class CmtsPcmmClientHandler extends AbstractPCMMClient implements IPCMMClientHandler {
+
+    private final static Logger logger = LoggerFactory.getLogger(CmtsPcmmClientHandler.class);
+
+    /**
+     * The thread accepting PEP COPS messages
+     */
+    private transient Thread thread;
+
+    /**
+     * The configured gates
+     */
+    private final Map<Direction, Set<String>> gateConfig;
+
+    /**
+     * The connected cable modems and whether or not they are up
+     */
+    private final Map<String, Boolean> cmStatus;
+
+    /**
+     * Constructor when a socket connection has not been established
+     * @param host - the host to connect
+     * @param port - the port to connect
+     * @param gateConfig - the configured gates
+     * @param cmStatus - the configured cable modem and their state
+     */
+    public CmtsPcmmClientHandler(final String host, final int port, final Map<Direction, Set<String>> gateConfig,
+                                 final Map<String, Boolean> cmStatus) {
+        super(host, port);
+        this.gateConfig = Collections.unmodifiableMap(gateConfig);
+        this.cmStatus = Collections.unmodifiableMap(cmStatus);
+    }
+
+    /**
+     * Constructor with a connected socket.
+     * @param socket - the socket connection
+     * @param gateConfig - the configured gates
+     * @param cmStatus - the configured cable modem and their state
+     */
+    public CmtsPcmmClientHandler(final Socket socket, final Map<Direction, Set<String>> gateConfig,
+                                 final Map<String, Boolean> cmStatus) {
+        super(socket);
+        this.gateConfig = Collections.unmodifiableMap(gateConfig);
+        this.cmStatus = Collections.unmodifiableMap(cmStatus);
+    }
+
+    public void stop() {
+        if (thread != null && thread.isAlive())
+            thread.interrupt();
+    }
+
+    @Override
+    public void run() {
+        try {
+            logger.info("Send OPN message to the PS");
+            sendRequest(MessageFactory.getInstance().create(OPCode.OPN, new Properties()));
+
+            // wait for CAT
+            final COPSMsg recvMsg = readMessage();
+
+            switch (recvMsg.getHeader().getOpCode()) {
+                case CC:
+                    final COPSClientCloseMsg closeMsg = (COPSClientCloseMsg) recvMsg;
+                    logger.info("PS requested Client-Close" + closeMsg.getError().getDescription());
+                    // send a CC message and close the socket
+                    disconnect();
+                    break;
+                case CAT:
+                    logger.info("received Client-Accept from PS");
+                    final COPSClientAcceptMsg acceptMsg = (COPSClientAcceptMsg) recvMsg;
+                    // Support
+                    if (acceptMsg.getIntegrity() != null) {
+                        throw new COPSPepException("Unsupported object (Integrity)");
+                    }
+
+                    // Mandatory KATimer
+                    final COPSKATimer kt = acceptMsg.getKATimer();
+                    if (kt == null)
+                        throw new COPSPepException("Mandatory COPS object missing (KA Timer)");
+                    short kaTimeVal = kt.getTimerVal();
+
+                    // ACTimer
+                    final COPSAcctTimer at = acceptMsg.getAcctTimer();
+                    short acctTimer = 0;
+                    if (at != null)
+                        acctTimer = at.getTimerVal();
+
+                    logger.info("Send a REQ message to the PS");
+                    final Properties prop = new Properties();
+                    final COPSMsg reqMsg = MessageFactory.getInstance().create(OPCode.REQ, prop);
+                    final COPSHandle handle = ((COPSReqMsg) reqMsg).getClientHandle();
+                    sendRequest(reqMsg);
+
+                    // Create the connection manager
+                    final PcmmCmtsConnection conn = new PcmmCmtsConnection(CLIENT_TYPE, getSocket(), gateConfig,
+                            cmStatus);
+                    conn.addRequestState(handle, new CmtsDataProcessor());
+                    conn.setKaTimer(kaTimeVal);
+                    conn.setAcctTimer(acctTimer);
+
+                    logger.info(getClass().getName() + " Thread(conn).start");
+                    thread = new Thread(conn);
+                    thread.start();
+                    break;
+                default:
+                    throw new COPSPepException("Message not expected. Closing connection for " + getSocket().toString());
+            }
+        } catch (Exception e) {
+            logger.error(e.getMessage());
+        }
+    }
+
+    @Override
+    public void task(Callable<?> c) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void shouldWait(int t) {
+        // TODO Auto-generated method stub
+
+    }
+
+    @Override
+    public void done() {
+        // TODO Auto-generated method stub
+
+    }
+
+}
diff --git a/packetcable-driver/src/main/java/org/pcmm/rcd/impl/CmtsPepReqStateMan.java b/packetcable-driver/src/main/java/org/pcmm/rcd/impl/CmtsPepReqStateMan.java
new file mode 100644 (file)
index 0000000..58dcfcc
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
+ */
+
+package org.pcmm.rcd.impl;
+
+import org.pcmm.gates.IGateSpec.Direction;
+import org.pcmm.gates.IPCMMError;
+import org.pcmm.gates.impl.GateID;
+import org.pcmm.gates.impl.PCMMError;
+import org.pcmm.gates.impl.PCMMGateReq;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.umu.cops.prpep.COPSPepException;
+import org.umu.cops.prpep.COPSPepMsgSender;
+import org.umu.cops.prpep.COPSPepReqStateMan;
+import org.umu.cops.stack.*;
+import org.umu.cops.stack.COPSDecision.DecisionFlag;
+import org.umu.cops.stack.COPSObjHeader.CNum;
+import org.umu.cops.stack.COPSObjHeader.CType;
+import org.umu.cops.stack.COPSReportType.ReportType;
+
+import java.io.IOException;
+import java.net.Socket;
+import java.util.*;
+
+/**
+ * PEP State manager implementation for use in a CMTS.
+ */
+public class CmtsPepReqStateMan extends COPSPepReqStateMan {
+
+    private final static Logger logger = LoggerFactory.getLogger(CmtsPepReqStateMan.class);
+
+    /**
+     * The configured gates
+     */
+    private final Map<Direction, Set<String>> gateConfig;
+
+    /**
+     * The connected CMTSs and whether or not they are up
+     */
+    private final Map<String, Boolean> cmStatus;
+
+    /**
+     * Contains the gates that have been set where the key is the gate name and the value is a Set of subIds
+     * that are using this gate
+     */
+    private final Map<String, Set<String>> gatesSetMap;
+
+    /**
+     * Create a State Request Manager
+     *
+     * @param clientType - the client type for this connection
+     * @param clientHandle - the client-handle for this connection
+     * @param process - the data processor
+     * @param socket - the socket connection
+     * @param gateConfig - the configured service class names (gates)
+     */
+    public CmtsPepReqStateMan(final short clientType, final COPSHandle clientHandle, final CmtsDataProcessor process,
+                              final Socket socket, final Map<Direction, Set<String>> gateConfig,
+                              final Map<String, Boolean> cmStatus) {
+        super(clientType, clientHandle, process, socket, new COPSPepMsgSender(clientType, clientHandle, socket));
+        this.gateConfig = Collections.unmodifiableMap(gateConfig);
+        this.cmStatus = Collections.unmodifiableMap(cmStatus);
+
+        this.gatesSetMap = new HashMap<>();
+        for (final Set<String> gateIdSet: gateConfig.values()) {
+            for (final String gateId : gateIdSet) {
+                gatesSetMap.put(gateId, new HashSet<String>());
+            }
+        }
+    }
+
+    @Override
+    protected void processDecision(final COPSDecisionMsg dMsg) throws COPSException {
+        logger.info("Processing decision message - " + dMsg);
+        final Map<COPSContext, Set<COPSDecision>> decisions = dMsg.getDecisions();
+
+        final Map<String, String> removeDecs = new HashMap<>();
+        final Map<String, String> installDecs = new HashMap<>();
+
+        for (final Set<COPSDecision> copsDecisions: decisions.values()) {
+            final COPSDecision cmddecision = copsDecisions.iterator().next();
+            switch (cmddecision.getCommand()) {
+                case INSTALL:
+                    for (final COPSDecision decision : copsDecisions) {
+                        if (decision.getFlag().equals(DecisionFlag.REQERROR)) {
+                            logger.info("processing decision");
+                            // This is assuming a gate set right or wrong
+                            if (dMsg.getDecisions().size() == 1 && dMsg.getDecSI() != null) {
+                                final PCMMGateReq gateReq = new PCMMGateReq(dMsg.getDecSI().getData().getData());
+                                if (gateReq.getGateSpec() != null) {
+                                    processGateReq(gateReq, _socket);
+                                }
+                            }
+                        }
+                    }
+                    break;
+                case REMOVE:
+                    for (final COPSDecision decision : copsDecisions) {
+                        // TODO - implement gate delete
+                    }
+                    break;
+            }
+
+        }
+
+        //** Apply decisions to the configuration
+        // TODO - why is this collection never getting populated???
+        final Map<String, String> errorDecs = new HashMap<>();
+        _process.setDecisions(this, removeDecs, installDecs, errorDecs);
+        _status = Status.ST_DECS;
+
+
+        if (_process.isFailReport(this)) {
+            // COPSDebug.out(getClass().getName(),"Sending FAIL Report\n");
+            _sender.sendFailReport(_process.getReportData(this));
+        } else {
+            // COPSDebug.out(getClass().getName(),"Sending SUCCESS Report\n");
+            _sender.sendSuccessReport(_process.getReportData(this));
+        }
+        _status = Status.ST_REPORT;
+
+        if (!_syncState) {
+            _sender.sendSyncComplete();
+            _syncState = true;
+            _status = Status.ST_SYNCALL;
+        }
+    }
+
+    private void processGateReq(final PCMMGateReq gateReq, final Socket socket) throws COPSException {
+        // TODO - Check and/or Set state here
+        // Gate ADD gateReq.getTrafficProfile() != null
+        // Gate REMOVE gateReq.getTrafficProfile() == null
+        final String subId = gateReq.getSubscriberID().getSourceIPAddress().getHostAddress();
+
+        // Get direction here
+        final Direction gateDir = gateReq.getGateSpec().getDirection();
+        final Set<String> gateNames = gateConfig.get(gateDir);
+        final String gateName = gateReq.getTrafficProfile().getData().str();
+
+        IPCMMError error = new PCMMError();
+        if (subId == null || gateDir == null || gateNames == null || gateName == null) {
+            // Missing required object
+            error.setErrorCode((short)3);
+        } else if (!cmStatus.keySet().contains(subId)
+                || (cmStatus.keySet().contains(subId) && !cmStatus.get(subId))) {
+            // Invalid Object
+            error.setErrorCode((short)13);
+        } else if (!gateNames.contains(gateName.trim())) {
+            error.setErrorCode((short)11);
+        } else {
+            error = null;
+            gatesSetMap.get(gateName.trim()).add(subId);
+        }
+        gateReq.setError(error);
+
+        logger.info("Processing gate request [" + gateName + "] with direction [" + gateDir + ']');
+
+        // Get gate name
+
+        // Set response
+        final List<Byte> data = new ArrayList<>();
+        for (final byte val : gateReq.getTransactionID().getAsBinaryArray())
+            data.add(val);
+        for (final byte val : gateReq.getAMID().getAsBinaryArray())
+            data.add(val);
+        for (final byte val : gateReq.getSubscriberID().getAsBinaryArray())
+            data.add(val);
+        if (error != null) for (final byte val : gateReq.getError().getAsBinaryArray())
+            data.add(val);
+
+        // Assign a gate ID
+        final GateID gateID = new GateID();
+        gateID.setGateID(UUID.randomUUID().hashCode());
+        for (final byte val : gateID.getAsBinaryArray())
+            data.add(val);
+
+
+        final byte[] csiArr = new byte[data.size()];
+        for (int i = 0; i < data.size(); i++) {
+            csiArr[i] = data.get(i);
+        }
+        final COPSClientSI si = new COPSClientSI(CNum.CSI, CType.DEF, new COPSData(csiArr, 0, csiArr.length));
+
+        final ReportType reportType;
+        if (gateReq.getError() == null) reportType = ReportType.SUCCESS; else reportType = ReportType.FAILURE;
+
+        logger.info("Returning " + reportType + " for gate request [" + gateName + "] direction [" + gateDir
+                + "] for host - " + subId);
+        final COPSReportMsg reportMsg = new COPSReportMsg(_clientType, getClientHandle(),
+                    new COPSReportType(reportType), si, null);
+        try {
+            reportMsg.writeData(socket);
+        } catch (IOException e) {
+            throw new COPSPepException("Error writing gate set SUCCESS Report", e);
+        }
+    }
+
+}
diff --git a/packetcable-driver/src/main/java/org/pcmm/rcd/impl/PcmmCmtsConnection.java b/packetcable-driver/src/main/java/org/pcmm/rcd/impl/PcmmCmtsConnection.java
new file mode 100644 (file)
index 0000000..8e3b365
--- /dev/null
@@ -0,0 +1,67 @@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
+ */
+
+package org.pcmm.rcd.impl;
+
+import org.pcmm.gates.IGateSpec.Direction;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.umu.cops.prpep.COPSPepConnection;
+import org.umu.cops.prpep.COPSPepDataProcess;
+import org.umu.cops.prpep.COPSPepException;
+import org.umu.cops.prpep.COPSPepReqStateMan;
+import org.umu.cops.stack.COPSException;
+import org.umu.cops.stack.COPSHandle;
+
+import java.net.Socket;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The connection object for the PCMM CMTS
+ */
+class PcmmCmtsConnection extends COPSPepConnection {
+
+    private final static Logger logger = LoggerFactory.getLogger(COPSPepConnection.class);
+
+    /**
+     * The configured gates
+     */
+    private final Map<Direction, Set<String>> gateConfig;
+
+    /**
+     * The connected CMTSs and whether or not they are up
+     */
+    private final Map<String, Boolean> cmStatus;
+
+    /**
+     * Constructor
+     * @param clientType - the client-type
+     * @param sock - the socket connection
+     * @param gateConfig - the configured gates
+     * @param cmStatus - the configured CMs and whether or each is connected
+     */
+    public PcmmCmtsConnection(final short clientType, final Socket sock, final Map<Direction, Set<String>> gateConfig,
+                              final Map<String, Boolean> cmStatus) {
+        super(clientType, sock);
+        this.gateConfig = Collections.unmodifiableMap(gateConfig);
+        this.cmStatus = Collections.unmodifiableMap(cmStatus);
+    }
+
+    @Override
+    public COPSPepReqStateMan addRequestState(final COPSHandle clientHandle, final COPSPepDataProcess process)
+            throws COPSException {
+        final COPSPepReqStateMan manager = new CmtsPepReqStateMan(_clientType, clientHandle, (CmtsDataProcessor)process,
+                _sock, gateConfig, cmStatus);
+        if (_managerMap.get(clientHandle) != null)
+            throw new COPSPepException("Duplicate Handle, rejecting " + clientHandle);
+
+        _managerMap.put(clientHandle, manager);
+        logger.info("Added state manager with key - " + clientHandle);
+        manager.initRequestState();
+        return manager;
+    }
+}
+
index c7c0ec7eec32a1849ef706bafe80a7f8849afd8b..071a11c00692319592ec86045f740ea7251fea22 100644 (file)
@@ -15,6 +15,7 @@ import org.umu.cops.stack.COPSHeader.OPCode;
 
 import java.io.IOException;
 import java.net.InetAddress;
+import java.net.InetSocketAddress;
 import java.net.Socket;
 
 /**
@@ -131,8 +132,8 @@ public class COPSPdpAgent {
      */
     public void connect() throws IOException, COPSException {
         // Create Socket and send OPN
-        final InetAddress addr = InetAddress.getByName(_host);
-        _socket = new Socket(addr, _serverPort);
+        _socket = new Socket();
+        _socket.connect(new InetSocketAddress(InetAddress.getByName(_host), _serverPort));
         logger.info("PDP Socket Opened. Waiting to receive client-open message");
         final COPSMsg msg = COPSTransceiver.receiveMsg(_socket);
         logger.debug("Message received of type - " + msg.getHeader().getOpCode());
@@ -161,7 +162,7 @@ public class COPSPdpAgent {
         if (_thread != null) _thread.interrupt();
         else logger.warn("Unable to locate PDP connection thread. Cannot stop it.");
 
-        if (_socket.isConnected())
+        if (_socket != null && _socket.isConnected())
             try {
                 _socket.close();
             } catch (IOException e) {
index 532b573de3c81bf891adccd90ba84917de795113..3a4f6587ca8a27baf4ac30834a27fab3c4970e88 100644 (file)
@@ -50,7 +50,7 @@ public class COPSPepReqStateMan extends COPSStateMan {
     /**
         The Msg Sender is used to send COPS messages
      */
-    private final COPSPepMsgSender _sender;
+    protected final COPSPepMsgSender _sender;
 
     /**
      * Sync State
index 3ea86486519927846a2b9f867c76037e477fe3c3..f2a4fe044331924117f90032dbb355485ef705fd 100644 (file)
@@ -1,3 +1,7 @@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
+ */
+
 package org.opendaylight.controller.packetcable.provider;
 
 import com.google.common.collect.Maps;
@@ -42,7 +46,8 @@ public class PCMMService {
                ipAddr = ccap.getConnection().getIpAddress();
                portNum = ccap.getConnection().getPort();
                ccapClient = new CcapClient(ipAddr, portNum);
-               logger.info("Attempting to add CCAP with ID {} @ {}:{}", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(), portNum.getValue());
+               logger.info("Attempting to add CCAP with ID {} @ {}:{}", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(),
+                               portNum.getValue());
        }
 
        public void disconect() {
@@ -64,7 +69,6 @@ public class PCMMService {
        }
 
        // TODO - Consider creating an object to return that contains a success flag, message, and gate ID or gate object
-       // TODO FIXME - the gate appears to be getting set as per restconf but I am not seeing the proper logging occurring
        public String sendGateSet(final String gatePathStr, final InetAddress subId, final Gates qosGate,
                                                          final ServiceFlowDirection scnDir) {
                logger.info("Sending gate to CCAP with ID - " + ccap.getCcapId());
@@ -93,43 +97,49 @@ public class PCMMService {
                // assemble the final gate request
                final PCMMGateReq gateReq = gateBuilder.getGateReq();
 
-               // and remember it
-               gateRequests.put(gatePathStr, gateReq);
-               // and send it to the CCAP
-               ccapClient.sendGateSet(gateReq);
-               // and wait for the COPS response to complete processing gate request
-               try {
-                       // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
-                       // TODO - handle this synchronization.
-                       // TODO - if not changing this, may want to make this timeout configurable
-                       synchronized(gateReq) {
-                               logger.info("Waiting 1000ms for gate request to be updated");
-                               gateReq.wait(1000);
-                               logger.debug("Gate request error - " + gateReq.getError());
-                               logger.debug("Gate request ID - " + gateReq.getGateID());
+               if (gateRequests.get(gatePathStr) == null) {
+                       // and remember it
+                       gateRequests.put(gatePathStr, gateReq);
+                       // and send it to the CCAP
+                       ccapClient.sendGateSet(gateReq);
+                       // and wait for the COPS response to complete processing gate request
+                       try {
+                               // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
+                               // TODO - handle this synchronization.
+                               // TODO - if not changing this, may want to make this timeout configurable
+                               synchronized(gateReq) {
+                                       logger.info("Waiting 5000ms for gate request to be updated");
+                                       gateReq.wait(5000);
+                                       logger.debug("Gate request error - " + gateReq.getError());
+                                       logger.debug("Gate request ID - " + gateReq.getGateID());
+                               }
+                       } catch (Exception e) {
+                               logger.error("PCMMService: sendGateSet(): gate response timeout exceeded for "
+                                               + gatePathStr + '/' + gateReq, e);
+                               return String.format("408 Request Timeout - gate response timeout exceeded for %s/%s",
+                                               ccap.getCcapId(), gatePathStr);
                        }
-               } catch (Exception e) {
-                       logger.error("PCMMService: sendGateSet(): gate response timeout exceeded for "
-                                       + gatePathStr + '/' + gateReq, e);
-                       return String.format("408 Request Timeout - gate response timeout exceeded for %s/%s",
-                                       ccap.getCcapId(), gatePathStr);
-               }
-               if (gateReq.getError() != null) {
-                       logger.error("PCMMService: sendGateSet(): returned error: {}",
-                                       gateReq.getError().toString());
-                       return String.format("404 Not Found - sendGateSet for %s/%s returned error - %s",
-                                       ccap.getCcapId(), gatePathStr, gateReq.getError().toString());
-               } else {
-                       if (gateReq.getGateID() != null) {
-                               logger.info(String.format("PCMMService: sendGateSet(): returned GateId %08x: ",
-                                               gateReq.getGateID().getGateID()));
-                               return String.format("200 OK - sendGateSet for %s/%s returned GateId %08x",
-                                               ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID());
+                       if (gateReq.getError() != null) {
+                               logger.error("PCMMService: sendGateSet(): returned error: {}",
+                                               gateReq.getError().toString());
+                               return String.format("404 Not Found - sendGateSet for %s/%s returned error - %s",
+                                               ccap.getCcapId(), gatePathStr, gateReq.getError().toString());
                        } else {
-                               logger.info("PCMMService: sendGateSet(): no gateId returned:");
-                               return String.format("404 Not Found - sendGateSet for %s/%s no gateId returned",
-                                               ccap.getCcapId(), gatePathStr);
+                               if (gateReq.getGateID() != null) {
+                                       logger.info(String.format("PCMMService: sendGateSet(): returned GateId %08x: ",
+                                                       gateReq.getGateID().getGateID()));
+                                       return String.format("200 OK - sendGateSet for %s/%s returned GateId %08x",
+                                                       ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID());
+                               } else {
+                                       logger.info("PCMMService: sendGateSet(): no gateId returned:");
+                                       return String.format("404 Not Found - sendGateSet for %s/%s no gateId returned",
+                                                       ccap.getCcapId(), gatePathStr);
+                               }
                        }
+               } else {
+                       logger.info("PCMMService: sendGateSet(): no gateId returned:");
+                       return String.format("404 Not Found - sendGateSet for %s/%s already exists",
+                                       ccap.getCcapId(), gatePathStr);
                }
        }
 
@@ -162,6 +172,7 @@ public class PCMMService {
                                return true;
                        }
                } else {
+                       logger.warn("Attempt to delete non-existent gate with path - " + gatePathStr);
                        return false;
                }
        }
index 936fdeb4b809aa7d09f6c426ad9468b3db9c192f..3a1a24f78dac945d30af257e352b3b72713057df 100644 (file)
@@ -1,3 +1,7 @@
+/*
+ * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
+ */
+
 package org.opendaylight.controller.packetcable.provider;
 
 import org.junit.After;
@@ -19,6 +23,8 @@ import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.pcmm.qos.classifie
 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.pcmm.qos.gate.spec.GateSpec;
 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.pcmm.qos.gates.apps.subs.Gates;
 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.pcmm.qos.traffic.profile.TrafficProfile;
+import org.pcmm.PCMMPdpAgent;
+import org.pcmm.gates.IGateSpec.Direction;
 import org.pcmm.gates.IPCMMGate;
 import org.pcmm.rcd.IPCMMClient;
 import org.pcmm.rcd.impl.CMTS;
@@ -29,10 +35,7 @@ import org.umu.cops.stack.COPSDecision.DecisionFlag;
 import org.umu.cops.stack.COPSObjHeader.CNum;
 import org.umu.cops.stack.COPSObjHeader.CType;
 
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
-import java.io.OutputStream;
+import java.io.*;
 import java.net.InetAddress;
 import java.net.Socket;
 import java.util.HashMap;
@@ -47,86 +50,216 @@ import java.util.Set;
  */
 public class PCMMServiceTest {
 
+    private final static String ccapId = "ccap-1";
+    private final static String gatePath = "testGatePath";
+
+    /**
+     * Denotes whether or not a real CMTS is being tested against.
+     * Ensure the checked-in value is always false else tests will most likely fail.
+     */
+    private final static boolean realCmts = false;
+
+
+    // The test objects/values to use that will be instantiated in @Before
+
     /**
      * The mock CMTS running on localhost with a dynamic port assigned.
      */
     private CMTS icmts;
 
     /**
-     * Represents the IP address of the CM the gate should be set against.
+     * The IP address object for the CMTS to test against
+     */
+    private Ipv4Address cmtsAddr;
+
+    /**
+     * The gate classifier's srcIp value, any valid IP should work.
      */
-    private byte[] cmAddr = new byte[4];
+    private Ipv4Address srcAddr;
+
+    /**
+     * The gate classifier's dstIp value, any valid IP should work.
+     */
+    private Ipv4Address dstAddr;
+
+    /**
+     * Defines the CMTS to add to the PCMMService
+     */
+    private Ccaps ccap;
+
+    /**
+     * The class under test
+     */
+    private PCMMService service;
+
+    /**
+     * The cable modem IP address to which a gate should be set
+     */
+    private InetAddress cmAddrInet;
+    private InetAddress invalidCmAddrInet;
 
     @Before
-    public void setup() {
-        icmts = new CMTS();
-        icmts.startServer();
-
-        cmAddr[0] = 10;
-        cmAddr[1] = 32;
-        cmAddr[2] = 110;
-        cmAddr[3] = (byte)180;
+    public void setup() throws IOException {
+        final Set<String> upGates = new HashSet<>();
+        upGates.add("extrm_up");
+        final Set<String> dnGates = new HashSet<>();
+        dnGates.add("extrm_dn");
+        final Map<Direction, Set<String>> gates = new HashMap<>();
+        gates.put(Direction.UPSTREAM, upGates);
+        gates.put(Direction.DOWNSTREAM, dnGates);
+
+        srcAddr = new Ipv4Address("10.10.10.0");
+        dstAddr = new Ipv4Address("10.32.99.99");
+
+        if (realCmts) {
+            cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
+            invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
+
+            // Use me when testing against a CMTS or emulator not running in the same JVM
+            cmtsAddr = new Ipv4Address("10.32.10.3");
+            ccap = makeCcapsObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr, ccapId);
+        } else {
+            cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
+            invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
+
+            // Use me for automated testing and the CMTS emulator running in the same JVM
+            cmtsAddr = new Ipv4Address("127.0.0.1");
+
+            final Map<String, Boolean> cmStatus = new HashMap<>();
+            cmStatus.put(cmAddrInet.getHostAddress(), true);
+            cmStatus.put(invalidCmAddrInet.getHostAddress(), false);
+            icmts = new CMTS(gates, cmStatus);
+            icmts.startServer();
+
+            ccap = makeCcapsObj(icmts.getPort(), cmtsAddr, ccapId);
+        }
+
+        service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
     }
 
     @After
     public void tearDown() {
         if (icmts != null) icmts.stopServer();
+        service.disconect();
     }
 
     @Test
     public void testAddCcap() {
+        connectToCmts(service);
+    }
 
-        // TODO - comment out for testing
-        //        final Ipv4Address cmtsAddr = new Ipv4Address("10.32.10.3");
-//        final Ccaps ccap = makeCcapsObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr.getValue(), "ccap-1");
-
-        // TODO - Use this instead for automated testing
-        final Ipv4Address cmtsAddr = new Ipv4Address("127.0.0.1");
-        final Ccaps ccap = makeCcapsObj(icmts.getPort(), cmtsAddr.getValue(), "ccap-1");
+    @Test
+    public void testAddInvalidCcapBadHost() {
+        cmtsAddr = new Ipv4Address("0.0.0.0");
+        ccap = makeCcapsObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr, ccapId);
+        service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
+        final String message = service.addCcap();
+        Assert.assertNotNull(message);
+        final String expectedMsg = "404 Not Found - CCAP " + ccapId + " failed to connect @ " + cmtsAddr.getValue()
+                + ':' + PCMMPdpAgent.WELL_KNOWN_PDP_PORT + " - Connection refused";
+        Assert.assertEquals(expectedMsg, message);
+    }
 
-        final PCMMService service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
+    @Test
+    public void testAddInvalidCcapBadPort() {
+        ccap = makeCcapsObj(1234, cmtsAddr, ccapId);
+        service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
         final String message = service.addCcap();
         Assert.assertNotNull(message);
-        Assert.assertTrue(message, message.startsWith("200"));
-        Assert.assertNotNull(service.ccapClient.pcmmPdp.getClientHandle());
-        service.disconect();
+        final String expectedMsg = "404 Not Found - CCAP " + ccapId + " failed to connect @ " + cmtsAddr.getValue()
+                + ":1234 - Connection refused";
+        Assert.assertEquals(expectedMsg, message);
     }
 
-//    @Test
-    public void testAddAndRemoveUpGate() throws Exception {
+    @Test
+    public void testAddValidUpGateTwice() throws Exception {
+        connectToCmts(service);
+        final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
+        addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
+                expectedMsg1);
 
-        // TODO - Use this block to test against a real CMTS
-//        final Ipv4Address cmtsAddr = new Ipv4Address("10.32.10.3");
-//        final Ccaps ccap = makeCcapsObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr.getValue(), "ccap-1");
+        final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
+        addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
+                expectedMsg2);
 
-        // TODO - Use this block for automated testing
-        final Ipv4Address cmtsAddr = new Ipv4Address("127.0.0.1");
-        final Ccaps ccap = makeCcapsObj(icmts.getPort(), cmtsAddr.getValue(), "ccap-1");
+        Assert.assertTrue(deleteGate(service, gatePath));
+    }
 
-        final PCMMService service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
-        service.addCcap();
+    @Test
+    public void testAddTwoValidUpGates() throws Exception {
+        connectToCmts(service);
 
-        final Gates gate = makeGateObj("extrm_up", cmtsAddr, ServiceFlowDirection.Us);
-        final String gatePath = "testGatePath";
+        final String gatePath1 = "gatePath1";
+        final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath1 + " returned GateId";
+        addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath1,
+                expectedMsg1);
 
-        // Add gate
-        final String msg = service.sendGateSet(gatePath, InetAddress.getByAddress(cmAddr), gate,
-                ServiceFlowDirection.Us);
-        Assert.assertTrue(msg, msg.startsWith("200"));
+        final String gatePath2 = "gatePath2";
+        final String expectedMsg2 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath2 + " returned GateId";
+        addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath2,
+                expectedMsg2);
 
-        // TODO - add validation to the PCMMGateReq contained within the map
-        Assert.assertEquals(1, service.gateRequests.size());
+        Assert.assertTrue(deleteGate(service, gatePath1));
+        Assert.assertTrue(deleteGate(service, gatePath2));
+    }
 
-        // Remove gate
-        service.sendGateDelete(gatePath);
+    @Test
+    public void testAddValidDownGateTwice() throws Exception {
+        connectToCmts(service);
+        final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
+        addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
+                expectedMsg1);
 
-        // Wait up to 1 sec for response to be processed
-        final long start = System.currentTimeMillis();
-        while (1000 < System.currentTimeMillis() - start) {
-            if (service.gateRequests.size() == 0) break;
-        }
-        Assert.assertEquals(0, service.gateRequests.size());
-        service.disconect();
+        final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
+        addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
+                expectedMsg2);
+
+        Assert.assertTrue(deleteGate(service, gatePath));
+    }
+
+    @Test
+    public void testDeleteNonExistentGate() throws Exception {
+        connectToCmts(service);
+        Assert.assertFalse(deleteGate(service, gatePath));
+    }
+
+    @Test
+    public void testAddAndRemoveValidUpGate() throws Exception {
+        final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
+        addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
+                expectedMsgStart);
+    }
+
+    @Test
+    public void testAddAndRemoveValidDownGate() throws Exception {
+        final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
+        addRemoveValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
+                expectedMsgStart);
+    }
+
+    @Test
+    public void testAddAndRemoveInvalidCmAddrUpGate() throws Exception {
+        // TODO - fix cmts emulator
+        final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
+                + " returned error - Error Code: 13 Error Subcode : 0  Invalid SubscriberID";
+        addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, invalidCmAddrInet,
+                gatePath, expectedMsgStart);
+    }
+
+    @Test
+    public void testAddInvalidScnUpGate() throws Exception {
+        final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
+                + " returned error - Error Code: 11 Error Subcode : 0  Undefined Service Class Name";
+        addRemoveValidateGate(service, "extrm_up_invalid", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet,
+                gatePath, expectedMsgStart);
+    }
+
+    @Test
+    public void testAddInvalidScnDownGate() throws Exception {
+        final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
+                + " returned error - Error Code: 11 Error Subcode : 0  Undefined Service Class Name";
+        addRemoveValidateGate(service, "extrm_dn_invalid", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet,
+                gatePath, expectedMsgStart);
     }
 
     /**
@@ -138,11 +271,10 @@ public class PCMMServiceTest {
     @Test
     public void testGateRequestDecisionMsg() throws Exception {
         final Socket socket = new MockSocket();
-        final Ccaps ccap = makeCcapsObj(icmts.getPort(), "127.0.0.1", "ccap-1");
-        final Ipv4Address cmtsAddr = new Ipv4Address("127.0.0.1");
-        final Gates gate = makeGateObj("extrm_up", cmtsAddr, ServiceFlowDirection.Us);
-        final IPCMMGate gateReq = makeGateRequest(ccap, gate, InetAddress.getByName("localhost"),
-                ServiceFlowDirection.Us);
+
+        final ServiceFlowDirection direction = ServiceFlowDirection.Us;
+        final Gates gate = makeGateObj("extrm_up", cmtsAddr, direction, new Ipv4Address("127.0.0.1"));
+        final IPCMMGate gateReq = makeGateRequest(ccap, gate, InetAddress.getByName("localhost"), direction);
         final byte[] data = gateReq.getData();
 
         final Set<COPSDecision> decisionSet = new HashSet<>();
@@ -160,6 +292,78 @@ public class PCMMServiceTest {
         Assert.assertEquals(decisionMsg, msg);
     }
 
+    /**
+     * Attempts to create a gate against a CMTS, validates the results then attempts to delete it.
+     * @param service - the service used to connect to a CMTS for issuing requests
+     * @param scnName - the service class name (aka. gate name)
+     * @param srcAddr - the address to the CMTS subnet?
+     * @param dstAddr - the destination address
+     * @param direction - the gate direction
+     * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
+     * @param gatePath - the path to the gate
+     * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
+     */
+    private void addRemoveValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
+                                       final Ipv4Address dstAddr, final ServiceFlowDirection direction,
+                                       final InetAddress cmAddrInet, final String gatePath,
+                                       final String expGateSetMsgStart) {
+        connectToCmts(service);
+        addAndValidateGate(service, scnName, srcAddr, dstAddr, direction, cmAddrInet, gatePath, expGateSetMsgStart);
+        deleteGate(service, gatePath);
+    }
+
+    private void connectToCmts(final PCMMService service) {
+        final String message = service.addCcap();
+        Assert.assertNotNull(message);
+        final String expectedMsg = "200 OK - CCAP " + ccapId + " connected @ "
+                + ccap.getConnection().getIpAddress().getIpv4Address().getValue()
+                + ":" + ccap.getConnection().getPort().getValue();
+        Assert.assertEquals(expectedMsg, message);
+        Assert.assertNotNull(service.ccapClient.pcmmPdp.getClientHandle());
+    }
+
+    /**
+     * Attempts to create a gate against a CMTS and validates the results.
+     * @param service - the service used to connect to a CMTS for issuing requests
+     * @param scnName - the service class name (aka. gate name)
+     * @param srcAddr - the address to the CMTS subnet?
+     * @param dstAddr - the destination address
+     * @param direction - the gate direction
+     * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
+     * @param gatePath - the path to the gate
+     * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
+     */
+    private void addAndValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
+                                    final Ipv4Address dstAddr, final ServiceFlowDirection direction,
+                                    final InetAddress cmAddrInet, final String gatePath,
+                                    final String expGateSetMsgStart) {
+        final Gates gate = makeGateObj(scnName, srcAddr, direction, dstAddr);
+
+        final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
+        Assert.assertNotNull(gateSetMsg);
+        Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
+
+        // TODO - add validation to the PCMMGateReq contained within the map
+        Assert.assertNotNull(service.gateRequests.get(gatePath));
+    }
+
+    /**
+     * Attempts to delete a gate
+     * @param service - the service used to connect to a CMTS for issuing requests
+     * @param gatePath - the path to the gate
+     */
+    private boolean deleteGate(final PCMMService service, final String gatePath) {
+        final boolean out = service.sendGateDelete(gatePath);
+
+        // Wait up to 1 sec for response to be processed
+        final long start = System.currentTimeMillis();
+        while (1000 < System.currentTimeMillis() - start) {
+            if (service.gateRequests.size() == 0) break;
+        }
+        Assert.assertNull(service.gateRequests.get(gatePath));
+        return out;
+    }
+
     /**
      * Creates a mock Ccaps object that can be used for connecting to a CMTS
      * @param inPort - the CMTS port number
@@ -167,7 +371,7 @@ public class PCMMServiceTest {
      * @param ccapId - the ID of the CCAP
      * @return - the mock Ccaps object
      */
-    private Ccaps makeCcapsObj(final int inPort, final String ipAddr, final String ccapId) {
+    private Ccaps makeCcapsObj(final int inPort, final Ipv4Address ipAddr, final String ccapId) {
         final Ccaps ccap = Mockito.mock(Ccaps.class);
         final Connection conn = Mockito.mock(Connection.class);
         Mockito.when(ccap.getConnection()).thenReturn(conn);
@@ -177,8 +381,7 @@ public class PCMMServiceTest {
 
         final IpAddress addr = Mockito.mock(IpAddress.class);
         Mockito.when(conn.getIpAddress()).thenReturn(addr);
-        final Ipv4Address ipv4 = new Ipv4Address(ipAddr);
-        Mockito.when(addr.getIpv4Address()).thenReturn(ipv4);
+        Mockito.when(addr.getIpv4Address()).thenReturn(ipAddr);
 
         Mockito.when(ccap.getCcapId()).thenReturn(ccapId);
         final AmId amid = Mockito.mock(AmId.class);
@@ -195,7 +398,8 @@ public class PCMMServiceTest {
      * @param dstAddr - the CM address this gate should be set against
      * @return - the gate request
      */
-    private Gates makeGateObj(final String scnValue, final Ipv4Address dstAddr, final ServiceFlowDirection direction) {
+    private Gates makeGateObj(final String scnValue, final Ipv4Address srcAddr, final ServiceFlowDirection direction,
+                              final Ipv4Address dstAddr) {
         final Gates gate = Mockito.mock(Gates.class);
         final GateSpec gateSpec = Mockito.mock(GateSpec.class);
         Mockito.when(gate.getGateSpec()).thenReturn(gateSpec);
@@ -218,7 +422,6 @@ public class PCMMServiceTest {
         Mockito.when(classifier.getDstPort()).thenReturn(dstPort);
         final TpProtocol protocol = new TpProtocol(0);
         Mockito.when(classifier.getProtocol()).thenReturn(protocol);
-        final Ipv4Address srcAddr = new Ipv4Address("127.0.0.1");
         Mockito.when(classifier.getSrcIp()).thenReturn(srcAddr);
         final PortNumber srcPort = new PortNumber(1234);
         Mockito.when(classifier.getSrcPort()).thenReturn(srcPort);