com.athena.peacock.controller.netty.PeacockServerHandler.java Source code

Java tutorial

Introduction

Here is the source code for com.athena.peacock.controller.netty.PeacockServerHandler.java

Source

/* 
 * Athena Peacock Project - Server Provisioning Engine for IDC or Cloud
 * 
 * Copyright (C) 2013 Open Source Consulting, Inc. All rights reserved by Open Source Consulting, Inc.
 *
 * This program 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 2
 * of the License, or (at your option) any later version.
 * 
 * This program 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 this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 *
 * Revision History
 * Author         Date            Description
 * ---------------   ----------------   ------------
 * Sang-cheon Park   2013. 7. 17.      First Draft.
 */
package com.athena.peacock.controller.netty;

import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener;
import io.netty.channel.ChannelHandler.Sharable;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

import javax.inject.Inject;
import javax.inject.Named;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Component;

import com.athena.peacock.common.core.action.ShellAction;
import com.athena.peacock.common.core.command.Command;
import com.athena.peacock.common.netty.PeacockDatagram;
import com.athena.peacock.common.netty.message.AbstractMessage;
import com.athena.peacock.common.netty.message.AgentInitialInfoMessage;
import com.athena.peacock.common.netty.message.AgentSystemStatusMessage;
import com.athena.peacock.common.netty.message.ConfigInfo;
import com.athena.peacock.common.netty.message.MessageType;
import com.athena.peacock.common.netty.message.OSPackageInfoMessage;
import com.athena.peacock.common.netty.message.PackageInfo;
import com.athena.peacock.common.netty.message.ProvisioningCommandMessage;
import com.athena.peacock.common.netty.message.ProvisioningResponseMessage;
import com.athena.peacock.common.netty.message.SoftwareInfo;
import com.athena.peacock.common.netty.message.SoftwareInfoMessage;
import com.athena.peacock.common.provider.AppContext;
import com.athena.peacock.controller.common.component.RHEVMRestTemplate;
import com.athena.peacock.controller.common.component.RHEVMRestTemplateManager;
import com.athena.peacock.controller.common.core.handler.MonFactorHandler;
import com.athena.peacock.controller.web.config.ConfigDto;
import com.athena.peacock.controller.web.hypervisor.HypervisorDto;
import com.athena.peacock.controller.web.hypervisor.HypervisorService;
import com.athena.peacock.controller.web.machine.MachineDto;
import com.athena.peacock.controller.web.machine.MachineService;
import com.athena.peacock.controller.web.monitor.MonDataDto;
import com.athena.peacock.controller.web.monitor.MonFactorDto;
import com.athena.peacock.controller.web.monitor.MonitorService;
import com.athena.peacock.controller.web.ospackage.PackageDto;
import com.athena.peacock.controller.web.ospackage.PackageService;
import com.athena.peacock.controller.web.software.SoftwareDto;
import com.athena.peacock.controller.web.software.SoftwareRepoDto;
import com.athena.peacock.controller.web.software.SoftwareRepoService;
import com.athena.peacock.controller.web.software.SoftwareService;
import com.redhat.rhevm.api.model.Cluster;
import com.redhat.rhevm.api.model.IP;
import com.redhat.rhevm.api.model.VM;
import com.redhat.rhevm.api.model.VMs;

/**
 * <pre>
 *
 * </pre>
 * 
 * @author Sang-cheon Park
 * @version 1.0
 */
@Component
@Qualifier("peacockServerHandler")
@Sharable
public class PeacockServerHandler extends SimpleChannelInboundHandler<Object> {

    private static final Logger logger = LoggerFactory.getLogger(PeacockServerHandler.class);

    @Inject
    @Named("machineService")
    private MachineService machineService;

    @Inject
    @Named("monitorService")
    private MonitorService monitorService;

    @Inject
    @Named("packageService")
    private PackageService packageService;

    @Inject
    @Named("softwareRepoService")
    private SoftwareRepoService softwareRepoService;

    @Inject
    @Named("softwareService")
    private SoftwareService softwareService;

    @SuppressWarnings("unchecked")
    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception {
        logger.debug("channelRead0() has invoked.");
        logger.debug("[Server] IP Address => " + ctx.channel().remoteAddress().toString());
        logger.debug("[Server] Object => " + msg.getClass().getName());
        //logger.debug("[Server] Contents => " + msg.toString());

        if ("bye".equals(msg.toString())) {
            // Response and exit.
            ChannelFuture future = ctx.write("This channel will be closed.");
            future.addListener(ChannelFutureListener.CLOSE);
        } else {
            if (msg instanceof PeacockDatagram) {
                MessageType messageType = ((PeacockDatagram<?>) msg).getMessageType();

                switch (messageType) {
                case COMMAND:
                    break;
                case RESPONSE:
                    ProvisioningResponseMessage responseMsg = ((PeacockDatagram<ProvisioningResponseMessage>) msg)
                            .getMessage();

                    if (responseMsg.isBlocking()) {
                        CallbackManagement.poll().handle(responseMsg);
                    }
                    break;
                case SYSTEM_STATUS:
                    AgentSystemStatusMessage statusMsg = ((PeacockDatagram<AgentSystemStatusMessage>) msg)
                            .getMessage();

                    //ThreadLocal cannot use.
                    //List<MonFactorDto> monFactorList = (List<MonFactorDto>) ThreadLocalUtil.get(PeacockConstant.MON_FACTOR_LIST);
                    List<MonFactorDto> monFactorList = AppContext.getBean(MonFactorHandler.class)
                            .getMonFactorList();

                    List<MonDataDto> monDataList = new ArrayList<MonDataDto>();
                    MonDataDto monData = null;

                    for (MonFactorDto monFactor : monFactorList) {
                        monData = new MonDataDto();

                        monData.setMachineId(statusMsg.getAgentId());
                        monData.setMonFactorId(monFactor.getMonFactorId());
                        monData.setMonDataValue(getMonDataValue(monFactor, statusMsg));
                        monData.setRegUserId(1);
                        monData.setUpdUserId(1);

                        monDataList.add(monData);
                    }

                    if (this.monitorService == null) {
                        monitorService = AppContext.getBean(MonitorService.class);
                    }

                    monitorService.insertMonDataList(monDataList);

                    break;
                case INITIAL_INFO:
                    AgentInitialInfoMessage infoMsg = ((PeacockDatagram<AgentInitialInfoMessage>) msg).getMessage();

                    if (infoMsg.getMachineId() != null) {
                        // register a new channel
                        ChannelManagement.registerChannel(infoMsg.getMachineId(), ctx.channel());
                        break;
                    }

                    String ipAddr = ctx.channel().remoteAddress().toString();
                    ipAddr = ipAddr.substring(1, ipAddr.indexOf(":"));

                    // ipAddr?  rhev_id RHEV Manager .
                    String machineId = infoMsg.getAgentId();
                    Integer hypervisorId = null;
                    String displayName = null;
                    String clusterName = null;
                    String isPrd = "N";
                    String isVm = "N";
                    String description = null;
                    boolean isMatch = false;
                    try {
                        List<RHEVMRestTemplate> templates = RHEVMRestTemplateManager.getAllTemplates();

                        if (templates == null || templates.size() == 0) {
                            List<HypervisorDto> hypervisorList = AppContext.getBean(HypervisorService.class)
                                    .getHypervisorList();
                            RHEVMRestTemplateManager.resetRHEVMRestTemplate(hypervisorList);
                            templates = RHEVMRestTemplateManager.getAllTemplates();
                        }

                        logger.debug("[PeacockServerHandler] templates.size() : {}", templates.size());

                        for (RHEVMRestTemplate restTemplate : templates) {
                            VMs vms = restTemplate.submit("/api/vms?search=" + ipAddr, HttpMethod.GET, VMs.class);

                            if (vms.getVMs().size() > 0) {
                                isVm = "Y";

                                List<VM> vmList = vms.getVMs();
                                List<IP> ipList = null;
                                for (VM vm : vmList) {
                                    // ip ? ? getIps() null? .
                                    ipList = vm.getGuestInfo().getIps().getIPs();

                                    for (IP ip : ipList) {
                                        if (ip.getAddress().equals(ipAddr)) {
                                            isMatch = true;
                                            machineId = vm.getId();
                                            hypervisorId = restTemplate.getHypervisorId();
                                            displayName = vm.getName();
                                            description = vm.getDescription();

                                            Cluster cluster = restTemplate.submit(vm.getCluster().getHref(),
                                                    HttpMethod.GET, Cluster.class);
                                            clusterName = cluster.getName();
                                            break;
                                        }
                                    }

                                    if (isMatch) {
                                        break;
                                    }
                                }
                            }

                            if (isMatch) {
                                break;
                            }
                        }
                    } catch (Exception e) {
                        // ignore
                        logger.error("Unhandled Exception has occurred.", e);
                    }

                    // register a new channel
                    ChannelManagement.registerChannel(machineId, ctx.channel());

                    // Agent? RHEV Manager? ?? ID ??? AgentID 
                    //  Agent? Software  ?? ?  Software  ? ? 
                    AgentInitialInfoMessage returnMsg = new AgentInitialInfoMessage();
                    returnMsg.setAgentId(machineId);
                    PeacockDatagram<AbstractMessage> datagram = new PeacockDatagram<AbstractMessage>(returnMsg);
                    ctx.channel().writeAndFlush(datagram);

                    if (this.machineService == null) {
                        machineService = AppContext.getBean(MachineService.class);
                    }

                    // Instance  DB? .
                    MachineDto machine = new MachineDto();
                    machine.setMachineId(machineId);
                    machine.setHypervisorId(hypervisorId);
                    machine.setDisplayName(displayName);
                    machine.setDescription(description);

                    if (StringUtils.isNotEmpty(displayName)) {
                        if (displayName.toLowerCase().startsWith("hhilws")
                                && !displayName.toLowerCase().startsWith("hhilwsd")) {
                            isPrd = "Y";
                        }
                    }
                    machine.setIsPrd(isPrd);

                    machine.setMachineMacAddr(infoMsg.getMacAddrMap().get(ipAddr));
                    machine.setIsVm(isVm);
                    machine.setCluster(clusterName);
                    machine.setOsName(infoMsg.getOsName());
                    machine.setOsVer(infoMsg.getOsVersion());
                    machine.setOsArch(infoMsg.getOsArch());
                    machine.setCpuClock(Integer.toString(infoMsg.getCpuClock()));
                    machine.setCpuNum(Integer.toString(infoMsg.getCpuNum()));
                    machine.setMemSize(Long.toString(infoMsg.getMemSize()));
                    machine.setIpAddr(ipAddr);
                    machine.setHostName(infoMsg.getHostName());
                    machine.setRegUserId(1);
                    machine.setUpdUserId(1);

                    machineService.insertMachine(machine);

                    // machine_additional_info_tbl? hostname ?  IP    ?  .( ? IP applyYn  ?)
                    machine = machineService.getAdditionalInfo(machineId);

                    boolean hostnameChanged = false;
                    //   hostname   chhost.sh  .
                    if (machine != null && StringUtils.isNotEmpty(machine.getHostName())
                            && !machine.getHostName().equals(infoMsg.getHostName())) {
                        try {
                            // /etc/hosts ?? ? ipAddress  Machine? IP?  ? IP? ?.
                            String ipAddress = null;

                            if (StringUtils.isNotEmpty(machine.getIpAddress())) {
                                ipAddress = machine.getIpAddress();
                            } else {
                                ipAddress = ipAddr;
                            }

                            ProvisioningCommandMessage cmdMsg = new ProvisioningCommandMessage();
                            cmdMsg.setAgentId(machine.getMachineId());
                            //cmdMsg.setBlocking(true);

                            int sequence = 0;
                            Command command = new Command("SET_HOSTNAME");

                            ShellAction action = new ShellAction(sequence++);
                            action.setCommand("sh");
                            action.addArguments("chhost.sh");
                            action.addArguments(ipAddress);
                            action.addArguments(machine.getHostName());
                            command.addAction(action);

                            cmdMsg.addCommand(command);

                            datagram = new PeacockDatagram<AbstractMessage>(cmdMsg);
                            ctx.channel().writeAndFlush(datagram);

                            hostnameChanged = true;
                        } catch (Exception e) {
                            // HostName ? ??  IP ?   ??  .
                            logger.error("Unhandled exception has occurred while change hostname.", e);
                        }
                    }

                    boolean resetIp = false;
                    if (machine != null && StringUtils.isNotEmpty(machine.getIpAddress())) {
                        if (machine.getApplyYn().equals("N") && !machine.getIpAddress().equals(ipAddr)) {
                            //  IP ?   Agent ? ? ? ? ???   .
                            machine.setIpAddr(ipAddr);
                            machineService.applyStaticIp(machine);
                            resetIp = true;
                        }
                    }

                    if (!resetIp) {
                        if (hostnameChanged) {
                            // IP  ? hostname ?  peacock-agent restart .
                            machineService.agentRestart(machineId);
                        } else {
                            // Package   ? Software      ? ?? .
                            if (this.softwareService == null) {
                                softwareService = AppContext.getBean(SoftwareService.class);
                            }
                            List<SoftwareDto> softwareList = softwareService.getSoftwareInstallListAll(machineId);

                            PackageService packageService = AppContext.getBean(PackageService.class);
                            PackageDto ospackage = new PackageDto();
                            ospackage.setMachineId(machineId);
                            int packageCnt = packageService.getPackageListCnt(ospackage);

                            returnMsg = new AgentInitialInfoMessage();
                            returnMsg.setAgentId(machineId);
                            if (softwareList != null && softwareList.size() > 0) {
                                returnMsg.setSoftwareInstalled("Y");
                            } else {
                                returnMsg.setSoftwareInstalled("N");
                            }
                            if (packageCnt > 0) {
                                returnMsg.setPackageCollected("Y");
                            } else {
                                returnMsg.setPackageCollected("N");
                            }
                            datagram = new PeacockDatagram<AbstractMessage>(returnMsg);
                            ctx.channel().writeAndFlush(datagram);
                        }
                    }

                    break;
                case PACKAGE_INFO:
                    OSPackageInfoMessage packageMsg = ((PeacockDatagram<OSPackageInfoMessage>) msg).getMessage();
                    List<PackageInfo> packageInfoList = packageMsg.getPackageInfoList();
                    PackageInfo packageInfo = null;
                    List<PackageDto> packageList = new ArrayList<PackageDto>();
                    PackageDto ospackage = null;
                    for (int i = 0; i < packageInfoList.size(); i++) {
                        packageInfo = packageInfoList.get(i);

                        ospackage = new PackageDto();
                        ospackage.setPkgId(i + 1);
                        ospackage.setMachineId(packageMsg.getAgentId());
                        ospackage.setName(packageInfo.getName());
                        ospackage.setArch(packageInfo.getArch());
                        ospackage.setSize(packageInfo.getSize());
                        ospackage.setVersion(packageInfo.getVersion());
                        ospackage.setReleaseInfo(packageInfo.getRelease());
                        ospackage.setInstallDate(packageInfo.getInstallDate());
                        ospackage.setSummary(packageInfo.getSummary());
                        ospackage.setDescription(packageInfo.getDescription());

                        packageList.add(ospackage);
                    }

                    if (packageList.size() > 0) {
                        if (packageService == null) {
                            packageService = AppContext.getBean(PackageService.class);
                        }

                        packageService.insertPackageList(packageList);
                    }

                    break;
                case SOFTWARE_INFO:
                    SoftwareInfoMessage softwareMsg = ((PeacockDatagram<SoftwareInfoMessage>) msg).getMessage();
                    List<SoftwareInfo> softwareInfoList = softwareMsg.getSoftwareInfoList();
                    SoftwareInfo softwareInfo = null;

                    if (softwareRepoService == null) {
                        softwareRepoService = AppContext.getBean(SoftwareRepoService.class);
                    }

                    SoftwareRepoDto softwareRepo = new SoftwareRepoDto();
                    softwareRepo.setStart(0);
                    softwareRepo.setLimit(100);
                    List<SoftwareRepoDto> softwareRepoList = softwareRepoService.getSoftwareRepoList(softwareRepo);

                    List<ConfigDto> configList = null;
                    List<ConfigInfo> configInfoList = null;
                    SoftwareDto software = null;
                    ConfigDto config = null;
                    int softwareId = 0;

                    StringBuilder stopCmd = null;
                    StringBuilder startCmd = null;

                    for (int i = 0; i < softwareInfoList.size(); i++) {
                        softwareInfo = softwareInfoList.get(i);

                        for (SoftwareRepoDto repo : softwareRepoList) {
                            softwareId = 0;

                            if (repo.getSoftwareName().toLowerCase()
                                    .indexOf(softwareInfo.getName().toLowerCase()) > 0) {
                                // ?  ? ??    ? softwareId .
                                softwareId = repo.getSoftwareId();
                                if (repo.getSoftwareVersion().startsWith(softwareInfo.getVersion())) {
                                    softwareId = repo.getSoftwareId();
                                    break;
                                }
                            }
                        }

                        //software.setInstallLocation(apacheHome + "," + serverHome + "/bin," + serverHome + "/conf," + serverHome + "/log," + serverHome + "/run," + serverHome + "/www");
                        //software.setInstallLocation(serverHome + "/apps," + serverHome + "/bin," + serverHome + "/Servers," + serverHome + "/svrlogs," + serverHome + "/wily," + serverHome + "/jboss-ews-2.1");
                        //software.setInstallLocation(jbossHome + "," + serverBase + ", " + serverHome + "/apps," + serverHome + "/svrlogs," + serverHome + "/wily");

                        stopCmd = new StringBuilder();
                        startCmd = new StringBuilder();

                        stopCmd.append("WORKING_DIR:").append(softwareInfo.getStopWorkingDir()).append(",")
                                .append("CMD:").append(softwareInfo.getStopCommand()).append(",").append("ARGS:")
                                .append(softwareInfo.getStopArgs());

                        startCmd.append("WORKING_DIR:").append(softwareInfo.getStartWorkingDir()).append(",")
                                .append("CMD:").append(softwareInfo.getStartCommand()).append(",").append("ARGS:")
                                .append(softwareInfo.getStartArgs());

                        software = new SoftwareDto();
                        software.setMachineId(softwareMsg.getAgentId());
                        software.setSoftwareId(softwareId);
                        software.setInstallLocation(StringUtils.join(softwareInfo.getInstallLocations(), ","));
                        software.setInstallStat("COMPLETED");
                        software.setInstallLog("Software installed manually.");
                        software.setServiceStopCmd(stopCmd.toString());
                        software.setServiceStartCmd(startCmd.toString());
                        software.setDescription("");
                        software.setDeleteYn("N");

                        configInfoList = softwareInfo.getConfigInfoList();
                        configList = new ArrayList<ConfigDto>();

                        for (ConfigInfo configInfo : configInfoList) {
                            config = new ConfigDto();
                            config.setMachineId(softwareMsg.getAgentId());
                            config.setSoftwareId(softwareId);
                            config.setConfigFileLocation(configInfo.getConfigFileLocation());
                            config.setConfigFileName(configInfo.getConfigFileName());
                            config.setConfigFileContents(configInfo.getConfigFileContents());
                            config.setDeleteYn("N");

                            configList.add(config);
                        }

                        if (softwareService == null) {
                            softwareService = AppContext.getBean(SoftwareService.class);
                        }

                        softwareService.insertSoftware(software, configList);
                    }

                    break;
                }
            }
        }
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        logger.debug("channelReadComplete() has invoked.");
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        logger.debug("channelActive() has invoked.");
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.debug("channelInactive() has invoked.");

        // deregister a closed channel
        ChannelManagement.deregisterChannel(ctx.channel());
    }

    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        logger.debug("handlerAdded() has invoked.");
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        logger.error("Unexpected exception from downstream.", cause);

        // ctx will not be closed.
        //if (!(cause instanceof NestedRuntimeException)) {
        //   ctx.close();
        //}
    }

    private String getMonDataValue(MonFactorDto monFactor, AgentSystemStatusMessage statusMsg) {
        String value = null;

        if (monFactor.getMonFactorName().toLowerCase().indexOf("cpu") > -1) {
            if (monFactor.getMonFactorName().toLowerCase().indexOf("idle") > -1) {
                value = statusMsg.getIdleCpu();
            } else if (monFactor.getMonFactorName().toLowerCase().indexOf("combined") > -1) {
                value = statusMsg.getCombinedCpu();
            }
        } else if (monFactor.getMonFactorName().toLowerCase().indexOf("memory") > -1) {
            if (monFactor.getMonFactorName().toLowerCase().indexOf("total") > -1) {
                value = statusMsg.getTotalMem();
            } else if (monFactor.getMonFactorName().toLowerCase().indexOf("free") > -1) {
                value = statusMsg.getFreeMem();
            } else if (monFactor.getMonFactorName().toLowerCase().indexOf("used") > -1) {
                value = statusMsg.getUsedMem();
            }
        } else if (monFactor.getMonFactorName().toLowerCase().indexOf("disk") > -1) {
            if (monFactor.getMonFactorName().toLowerCase().indexOf("usage") > -1) {
                value = statusMsg.getDiskUsage();
            } else if (monFactor.getMonFactorName().toLowerCase().indexOf("total") > -1) {
                value = statusMsg.getTotalDisk();
            } else if (monFactor.getMonFactorName().toLowerCase().indexOf("used") > -1) {
                value = statusMsg.getUsedDisk();
            }
        }

        return value;
    }

    /**
     * <pre>
     *  Agent Provisioning  ?    ?? .
     * </pre>
     * @param datagram
     * @return
     * @throws Exception 
     */
    public ProvisioningResponseMessage sendMessage(PeacockDatagram<AbstractMessage> datagram) throws Exception {
        Channel channel = ChannelManagement.getChannel(datagram.getMessage().getAgentId());
        boolean isBlocking = datagram.getMessage().isBlocking();

        if (isBlocking) {
            Callback callback = new Callback();
            CallbackManagement.lock();

            try {
                CallbackManagement.add(callback);

                if (channel != null) {
                    channel.writeAndFlush(datagram);
                } else {
                    throw new Exception("Channel is null.");
                }
            } finally {
                CallbackManagement.unlock();
            }

            return callback.get();
        } else {
            if (channel != null) {
                channel.writeAndFlush(datagram);
            } else {
                throw new Exception("Channel is null.");
            }

            return null;
        }
    }//end of sendMessage()

    /**
     * <pre>
     * channelMap ? agentId?  Channel? ??  true,  false
     * </pre>
     * @param agentId
     * @return
     */
    public boolean isActive(String agentId) {
        return ChannelManagement.getChannel(agentId) != null ? true : false;
    }

    /**
     * <pre>
     * channelMap ? agentId?  Channel? close .
     * </pre>
     * @param agentId
     */
    public void channelClose(String agentId) {
        Channel channel = ChannelManagement.getChannel(agentId);

        if (channel != null) {
            channel.close();
        }
    }

    /**
     * <pre>
     * Channel   ?
     * </pre>
     * @author Sang-cheon Park
     * @version 1.0
     */
    static class ChannelManagement {

        static Map<String, Channel> channelMap = new ConcurrentHashMap<String, Channel>();

        /**
         * <pre>
         *  ?? ?.
         * </pre>
         * @param agentId
         * @param channel
         */
        synchronized static void registerChannel(String agentId, Channel channel) {
            logger.debug("agentId({}) and channel({}) will be added to channelMap.", agentId, channel);

            // ? ?? ?? ?  close.
            Channel c = channelMap.get(agentId);
            if (c != null) {
                c.close();
            }

            channelMap.put(agentId, channel);
        }//end of registerChannel()

        /**
         * <pre>
         * agentId?  ?? map? .
         * </pre>
         * @param agentId
         */
        synchronized static void deregisterChannel(String agentId) {
            logger.debug("agentId({}) will be removed from channelMap.", agentId);
            channelMap.remove(agentId);
        }//end of deregisterChannel()

        /**
         * <pre>
         *  ? ?? map? .
         * </pre>
         * @param channel
         */
        synchronized static void deregisterChannel(Channel channel) {
            Iterator<Entry<String, Channel>> iter = channelMap.entrySet().iterator();

            Entry<String, Channel> entry = null;
            while (iter.hasNext()) {
                entry = iter.next();

                if (entry.getValue() != null && entry.getValue() == channel) {
                    deregisterChannel(entry.getKey());
                    break;
                }
            }
        }//end of deregisterChannel()

        /**
         * <pre>
         * agentId?  ?  .
         * </pre>
         * @param agentId
         * @return
         */
        static Channel getChannel(String agentId) {
            return channelMap.get(agentId);
        }//end of getChannel()
    }
    //end of ChannelManagement.java

    /**
      * <pre>
      * ?      ?
      * </pre>
      * @author Sang-cheon Park
      * @version 1.0
     */
    static class Callback {

        private final CountDownLatch latch = new CountDownLatch(1);

        private ProvisioningResponseMessage response;

        ProvisioningResponseMessage get() {
            try {
                latch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            return response;
        }

        void handle(ProvisioningResponseMessage response) {
            this.response = response;
            latch.countDown();
        }
    }
    //end of Callback.java

    /**
     * <pre>
     * Multi-thread ? Callback ?   ? 
     * </pre>
     * @author Sang-cheon Park
     * @version 1.0
     */
    static class CallbackManagement {
        private static final Lock lock = new ReentrantLock();
        private static final Queue<Callback> callbacks = new ConcurrentLinkedQueue<Callback>();

        static void lock() {
            lock.lock();
        }

        static void unlock() {
            lock.unlock();
        }

        static void add(Callback callback) {
            callbacks.add(callback);
        }

        static Callback poll() {
            return callbacks.poll();
        }

        static int getSize() {
            return callbacks.size();
        }
    }
    //end of CallbackManagement.java

}
//end of PeacockServerHandler.java