/**
* This file is part of Path Computation Element Emulator (PCEE).
*
* PCEE is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* PCEE is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with PCEE. If not, see <http://www.gnu.org/licenses/>.
*/
package com.pcee.architecture.sessionmodule;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Timer;
import java.util.concurrent.LinkedBlockingQueue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.pcee.architecture.ModuleEnum;
import com.pcee.architecture.ModuleManagement;
import com.pcee.architecture.sessionmodule.statemachine.StateMachine;
import com.pcee.architecture.sessionmodule.statemachine.StateMachineClientImpl;
import com.pcee.architecture.sessionmodule.statemachine.StateMachineServerImpl;
import com.pcee.protocol.message.PCEPMessage;
import com.pcee.protocol.message.objectframe.impl.erosubobjects.PCEPAddress;
/**
* Session Layer Implementation with multiple worker threads to process incoming
* messages
*
* @author Marek Drogon
* @author Mohit Chamania
*/
public class SessionModuleImpl extends SessionModule {
private static Logger logger = LoggerFactory.getLogger(SessionModuleImpl.class);
// Management Object used to forward communications between the different
// modules
private ModuleManagement lm;
// Static to define the number of worker threads processing incoming
// messages from different modules
final int sessionThreads;
// Array Of Worker Threads to process incoming messages
private ReadingQueueThreadImpl[] readingQueueThread;
// HashMap to store association between address and the corresponding state
// machine
private HashMap<String, StateMachine> addressToStateMachineHashMap;
// A single Timer to manage timeout events for all state machines
private Timer stateMachineTimer;
/**
* Default Constructor
*
* @param layerManagement
*/
public SessionModuleImpl(ModuleManagement layerManagement) {
logger.debug("Entering: SessionModuleImpl(ModuleManagement layerManagement)");
lm = layerManagement;
sessionThreads = 1;
start();
}
public SessionModuleImpl(ModuleManagement layerManagement,
int sessionThreads) {
logger.debug("Entering: SessionModuleImpl(ModuleManagement layerManagement, int sessionThreads)");
lm = layerManagement;
this.sessionThreads = 1;
start();
}
private class ReadingQueueBuffer {
PCEPMessage message;
ModuleEnum sourceLayer;
public ReadingQueueBuffer(PCEPMessage message, ModuleEnum sourceLayer) {
this.message = message;
this.sourceLayer = sourceLayer;
}
}
/** Worker threads to process incoming messages from different modules */
private class ReadingQueueThreadImpl extends Thread {
// A blocking Queue to store messages for the thread to process
LinkedBlockingQueue<ReadingQueueBuffer> readingQueue = new LinkedBlockingQueue<ReadingQueueBuffer>();
/**
* Function to add a message to the queue for the worker thread to read
*
* @param message
*/
public void addMessage(PCEPMessage message, ModuleEnum sourceLayer) {
logger.debug("Entering: addMessage(PCEPMessage message)");
readingQueue.add(new ReadingQueueBuffer(message, sourceLayer));
}
// Main function of the worker thread to process incoming messages
public void run() {
logger.debug("Entering: run()");
while (true) {
ReadingQueueBuffer temp = null;
try {
logger.info("Waiting for new Messages");
temp = readingQueue.take();
} catch (InterruptedException e) {
break;
}
processMessage(temp.message, temp.sourceLayer);
if (Thread.currentThread().isInterrupted())
break;
}
}
}
public void stop(boolean graceful) {
logger.debug("Entering: stop(" + (graceful?"true":"false") + ")");
if (graceful) {
//include code for graceful stop
} else {
Iterator<StateMachine> iter = addressToStateMachineHashMap.values()
.iterator();
while (iter.hasNext()) {
StateMachine sm = iter.next();
closeConnection(sm.getAddress());
}
for (int i = 0; i < sessionThreads; i++)
readingQueueThread[i].interrupt();
stateMachineTimer.cancel();
}
}
public void start() {
logger.debug("Entering: start()");
// Initialize the timer object
stateMachineTimer = new Timer();
// Create a new map for storing associations between address and state
// machines
addressToStateMachineHashMap = new HashMap<String, StateMachine>();
// Initialize the reading worker threads
readingQueueThread = new ReadingQueueThreadImpl[sessionThreads];
for (int i = 0; i < sessionThreads; i++) {
readingQueueThread[i] = new ReadingQueueThreadImpl();
readingQueueThread[i].setName("SessionLayerThread" + i);
readingQueueThread[i].start();
}
}
public void receiveMessage(PCEPMessage message, ModuleEnum sourceLayer) {
logger.debug("Entering: receiveMessage(PCEPMessage message, ModuleEnum sourceLayer)");
logger.debug("| message: " + message.contentInformation());
logger.debug("| sourceLayer: " + sourceLayer);
int x;
switch (sourceLayer) {
case NETWORK_MODULE:
// Simple hash function to determine to which worker thread a
// message should be assigned
// TODO
x = Integer.parseInt(message.getAddress().getIPv4Address()
.split(":")[1])
% sessionThreads;
readingQueueThread[x].addMessage(message, sourceLayer);
break;
case COMPUTATION_MODULE:
x =
Integer.parseInt(message.getAddress().getIPv4Address().split(":")[1])
% sessionThreads;
readingQueueThread[x].addMessage(message, sourceLayer);
//sendMessage(message, ModuleEnum.NETWORK_MODULE);
break;
case CLIENT_MODULE:
x = Integer.parseInt(message.getAddress().getIPv4Address()
.split(":")[1])
% sessionThreads;
readingQueueThread[x].addMessage(message, sourceLayer);
// TODO
// x =
// Integer.parseInt(message.getAddress().getAddress().split(":")[1])
// % readingThreadCount;
// readingQueueThread[x].addMessage(message);
break;
default:
logger.info("Error in recieveMessage(PCEPMessage message, LayerEnum sourceLayer)");
logger.info("Wrong source Layer");
break;
}
}
public void sendMessage(PCEPMessage message, ModuleEnum targetLayer) {
logger.debug("Entering: sendMessage(PCEPMessage message, ModuleEnum targetLayer)");
logger.debug("| message: " + message.contentInformation());
logger.debug("| targetLayer: " + targetLayer);
switch (targetLayer) {
case NETWORK_MODULE:
lm.getNetworkModule().receiveMessage(message,
ModuleEnum.SESSION_MODULE);
break;
case COMPUTATION_MODULE:
lm.getComputationModule().receiveMessage(message,
ModuleEnum.SESSION_MODULE);
break;
case CLIENT_MODULE:
lm.getClientModule().receiveMessage(message,
ModuleEnum.SESSION_MODULE);
break;
default:
logger.info("Error in sendMessage(PCEPMessage message, LayerEnum targetLayer)");
logger.info("Wrong target Layer 11");
break;
}
}
public void registerConnection(PCEPAddress address, boolean connected,
boolean connectionInitialized, boolean forceClient) {
logger.debug("Entering: registerConnection(Address address, boolean connected, boolean connectionInitialized)");
logger.debug("| address: " + address.getIPv4Address());
logger.debug("| connected: " + connected);
logger.debug("| connectionInitialized: " + connectionInitialized);
// if state machine exists do nothing
if (!addressToStateMachineHashMap.containsKey(address.getIPv4Address())) {
// Function for the client side, where the node is responsible for
// initializing the connection and is not connected initially
if ((connectionInitialized == true) && (connected == false)) {
lm.getNetworkModule().registerConnection(address, connected,
connectionInitialized, forceClient);
}
// If the connection is connected, register the new state machine
// for the connection
if (connected == true) {
createNewStateMachine(address, connectionInitialized, forceClient);
}
}
}
public void closeConnection(PCEPAddress address) {
logger.debug("Entering: closeConnection(PCEPAddress address)");
logger.debug("| address: " + address.getIPv4Address());
if (addressToStateMachineHashMap.containsKey(address.getIPv4Address())) {
// Releasing resources from the state machine
StateMachine stateMachine = getStateMachineFromHashMap(address);
stateMachine.releaseResources();
// removing state machine from hash map
removeStateMachineFromHashMap(address);
// closing connection in the network layer
lm.getNetworkModule().closeConnection(address);
} else {
logger.debug("Could not find a StateMachine for "
+ address.getIPv4Address());
}
}
/**
* Function to create new state machine
*
* @param address
* @param connectionInitialized
*/
private void createNewStateMachine(PCEPAddress address,
boolean connectionInitialized, boolean forceClient) {
logger.debug("Entering: createNewStateMachine(PCEPAddress address, boolean connectionInitialized)");
logger.debug("| address: " + address.getIPv4Address());
logger.debug("| connectionInitialized: " + connectionInitialized);
logger.info("New StateMachine for " + address.getIPv4Address());
// Creating new state machine
// If LM is of type client, create a client state machine or else create a server state machine (by default)
StateMachine stateMachine;
if (lm.isServer()) {
//If in the server a state machine is registered forcefully as a client then we create a client state machine or else we make
// a server state machine
if (forceClient)
stateMachine = new StateMachineClientImpl(lm, address,stateMachineTimer, connectionInitialized);
else
stateMachine = new StateMachineServerImpl(lm, address,stateMachineTimer, connectionInitialized);
} else {
stateMachine = new StateMachineClientImpl(lm, address,stateMachineTimer, connectionInitialized);
}
// adding state machine to hash map
insertStateMachineToHashMap(address, stateMachine);
}
/**
* Function to process and incoming message
*
* @param message
*/
private void processMessage(PCEPMessage message, ModuleEnum sourceLayer) {
logger.debug("Entering: processMessage(PCEPMessage message)");
logger.debug("| message: " + message.contentInformation());
logger.debug("| address: " + message.getAddress().getIPv4Address());
logger.info("Processing Message from "
+ message.getAddress().getIPv4Address());
StateMachine machine = getStateMachineFromHashMap(message.getAddress());
if (machine == null) {
logger.info("State Machine for connection from "
+ message.getAddress().getIPv4Address()
+ " does not exist. Discarding Message");
} else
machine.updateState(message, sourceLayer);
}
/**
* Function to get the State machine from map using the address object
*
* @param address
* @return
*/
private StateMachine getStateMachineFromHashMap(PCEPAddress address) {
logger.debug("Entering: getStateMachineFromHashMap(PCEPAddress address)");
logger.debug("| address: " + address.getIPv4Address());
logger.info("Getting StateMachine for " + address.getIPv4Address());
return addressToStateMachineHashMap.get(address.getIPv4Address());
}
/**
* Function to register the state machine inside the hash map
*
* @param address
* @param stateMachine
*/
private void insertStateMachineToHashMap(PCEPAddress address,
StateMachine stateMachine) {
logger.debug("Entering: insertStateMachineToHashMap(Address address, StateMachineImpl stateMachine)");
logger.debug("| address: " + address.getIPv4Address());
logger.debug("| stateMachine: " + stateMachine.toString());
logger.info("Inserting StateMachine for " + address.getIPv4Address());
addressToStateMachineHashMap
.put(address.getIPv4Address(), stateMachine);
logger.info("| StateMachines active: "
+ addressToStateMachineHashMap.size());
}
/**
* Function to remove the state machine from the hash Map
*
* @param address
*/
private void removeStateMachineFromHashMap(PCEPAddress address) {
logger.debug("Entering: removeStateMachineFromHashMap(PCEPAddress address)");
logger.debug("| address: " + address.getIPv4Address());
logger.info("Removing StateMachine for " + address.getIPv4Address());
addressToStateMachineHashMap.remove(address.getIPv4Address());
logger.info("| StateMachines active: "
+ addressToStateMachineHashMap.size());
}
}