public ControllerWrapper(Controller controller,
			String groupName, String groupAddress, 
			String pName, 
			double cpuThreshold) throws ClientConstructorException {
		try {
			this.controller = controller;	//bound to controller
			this.groupName = groupName;
			this.groupAddress = groupAddress;
			this.pName = pName;
			this.logger = Logger.getLogger(ControllerWrapper.class);	//create logger
			cl_update = true;	//update mapping in the beginning
			this.mNotifications = new Stack<CPULoadRecord>();
			ControllerWrapper.cpuThreshold = cpuThreshold;
			
			
			channel = new JChannel(false);	//	create own protocol stack
			ProtocolStack stack = new ProtocolStack();
			channel.setProtocolStack(stack);
			
			stack.addProtocol(new UDP().setValue("bind_addr", InetAddress.getByName(groupAddress)))
						.addProtocol(new PING())	//broadcast udp	
						.addProtocol(new MERGE2())	//merge protocol
						.addProtocol(new MERGE3())	//another merging layer for safety
						.addProtocol(new FD_SOCK())
						.addProtocol(new FD_ALL().setValue("timeout",10000)
												 .setValue("interval",3000))	//heartbeat protocol, failure detection
						.addProtocol(new VERIFY_SUSPECT())
						.addProtocol(new BARRIER())
						.addProtocol(new NAKACK())
						.addProtocol(new UNICAST2())
						.addProtocol(new STABLE())
						.addProtocol(new GMS())
						.addProtocol(new UFC())
						.addProtocol(new MFC())
						.addProtocol(new FRAG2())
						.addProtocol(new CENTRAL_LOCK())
						.addProtocol(new COUNTER());
			stack.init();
			
			logger.info(pName + ":protocol stack initialized, bind address:" + groupAddress);
			
			channel.setName(pName);
			syncService = new CounterService(channel);	//master id atomic service
			lock_service = new LockService(channel);
			msg_disp = new MessageDispatcher(channel, 
				new MessageListener() {
				
				@Override
				public void setState(InputStream arg0) throws Exception {
					//empty method - initial state when connect
					//will be used in future versions
				}
				
				@Override
				public void receive(Message msg) {
					switch (RecvMessageHandler.getMessageType(msg)) {
					case RecvMessageHandler.CPU_NOTIFICATION : {
							mNotifications.push(RecvMessageHandler.getCPULoad(msg));
							ControllerWrapper.this.controller.printMessage(msg.copy());
							break;
					}
					case RecvMessageHandler.STOP : {
							int message_id = ((ClientStopMessage)msg.getObject()).id;
							if (message_id == ControllerWrapper.this.id) {
								stopClient();	//stop client
								sendStopGUI();
							}
							break;
					}
					case RecvMessageHandler.UNKNOWN : {
							logger.info(ControllerWrapper.this.pName + ": Unknown message type, " +
									"message:" + msg.toString());
							break;
						}
					}
				}
				
				@Override
				public void getState(OutputStream arg0) throws Exception {
					//empty method	- must read doc
					//will be used in future versions
				}
			}, new MembershipListener() {
					@Override
					public void viewAccepted(View newView) {
						clView = newView;
						//TODO
						//buggy place here
						if (firstConnect) {
							id = newView.size();
							master_id = syncService.getOrCreateCounter(master_counter_name, id);
							firstConnect = false;
						}
						cl_update = true;
					}
				
					@Override
					public void unblock() {
						//empty method
						//maybe useful in future
					}
				
					@Override
						public void suspect(Address addr) {
						logger.info(ControllerWrapper.this.pName + "Node:" + addr + " may have crushed.");
						//do nothing here
					}
				
					@Override
						public void block() {
						//empty method
						//maybe useful in future
					}
			}, new RequestHandler() {
					/**
					 * request type - RequestCPULoadMessage -> new CPULoadMessage
					 * request type - NodeInforRequest -> new NodeInfoResponse
					 * request type - unknown -> null
					 */
					@Override	//called after cpu-load request
					public Object handle(Message msg) throws Exception {
						if (msg.getObject() instanceof RequestCPULoadMessage)
							return new CPULoadMessage();
						if (msg.getObject() instanceof NodeInfoRequest) {
							//TODO
							//buggy place here, master id may be undefined or id undefined
							//nullpointer exception somewhere here
							return new NodeInfoResponse(ControllerWrapper.this.id, 
														ControllerWrapper.this.pName, 
														ControllerWrapper.this.groupAddress, 
														master_id.get() == ControllerWrapper.this.id ? true : false);
						}
						return null;	//should never get here
					}
			});
			logger.info(pName + ":JGroups message dispatcher and counter initialized");
		} catch (Exception e) {
			e.printStackTrace();
			logger.error(pName + ExceptionUtils.getStackTrace(e));
			throw new ClientConstructorException("Exception:client constructor failed, " +
					"message: " + e.getMessage());
		}
	}