/*
 * This software is distributed under following license based on modified BSD
 * style license.
 * ----------------------------------------------------------------------
 * 
 * Copyright 2009 The Nimbus2 Project. All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 * 
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer. 
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE NIMBUS PROJECT ``AS IS'' AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
 * NO EVENT SHALL THE NIMBUS PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 * 
 * The views and conclusions contained in the software and documentation are
 * those of the authors and should not be interpreted as representing official
 * policies, either expressed or implied, of the Nimbus2 Project.
 */
package jp.ossc.nimbus.service.codemaster;

import java.util.*;
import java.io.*;
import javax.jms.*;

import jp.ossc.nimbus.core.*;
import jp.ossc.nimbus.service.jms.*;
import jp.ossc.nimbus.service.beanflow.*;
import jp.ossc.nimbus.service.publish.MessageReceiver;

/**
 * {@link CodeMasterFinder}サービス。<p>
 *
 * @author H.Nakano
 */
public class DefaultCodeMasterFinderService extends ServiceBase
 implements DefaultCodeMasterFinderServiceMBean, CodeMasterFinder, MessageListener, jp.ossc.nimbus.service.publish.MessageListener{
    
    private static final long serialVersionUID = -4013884085932487905L;
    
    public static final String UPDATE_TIME_KEY = "$updateTime";
    
    private static final String FIND_DATE_KEY = "date";
    private static final String MASTER_DATA_KEY = "data";
    private static final String KEY_SEPARATOR = ",";
    
    private ServiceName beanFlowFactoryServiceName;
    private BeanFlowFactory beanFlowFactory;
    
    private String[] subjects;
    private ServiceName messageReceiverServiceName;
    private MessageReceiver messageReceiver;
    
    private String[] masterNames;
    private String[] startMasterNames;
    private String[] notStartMasterNames;
    private Map<String, Object> startMasterInputMap;
    
    private ServiceName jmsTopicSubscriberFactoryServiceName;
    private JMSMessageConsumerFactory jmsTopicSubscriberFactory;
    private ServiceName[] jmsTopicSubscriberFactoryServiceNames;
    private JMSMessageConsumerFactory[] jmsTopicSubscriberFactories;
    private Set<JMSMessageConsumerFactoryStateListener> stateListeners;
    
    /**
     * マスター格納用Hash
     */
    protected Map<String, TimeManageMaster> master;
    private Set<String> masterNameSet;
    private Properties notifyMasterNameMapping;
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public void setMasterNames(String[] names){
        masterNames = names;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public String[] getMasterNames(){
        return masterNames;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public void setNotifyMasterNameMapping(Properties mapping){
        notifyMasterNameMapping = mapping;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public Properties getNotifyMasterNameMapping(){
        return notifyMasterNameMapping;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public void setBeanFlowFactoryServiceName(ServiceName name){
        beanFlowFactoryServiceName = name;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public ServiceName getBeanFlowFactoryServiceName(){
        return beanFlowFactoryServiceName;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public void setJMSTopicSubscriberFactoryServiceName(ServiceName name){
        jmsTopicSubscriberFactoryServiceName = name;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public ServiceName getJMSTopicSubscriberFactoryServiceName(){
        return jmsTopicSubscriberFactoryServiceName;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public void setJMSTopicSubscriberFactoryServiceNames(ServiceName[] names){
        jmsTopicSubscriberFactoryServiceNames = names;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public ServiceName[] getJMSTopicSubscriberFactoryServiceNames(){
        return jmsTopicSubscriberFactoryServiceNames;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public void setSubjects(String[] subject){
        this.subjects = subject;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public String[] getSubjects(){
        return subjects;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public void setMessageReceiverServiceName(ServiceName name){
        messageReceiverServiceName = name;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public ServiceName getMessageReceiverServiceName(){
        return messageReceiverServiceName;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public void setStartMasterNames(String[] names){
        startMasterNames = names;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public String[] getStartMasterNames(){
        return startMasterNames;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public void setNotStartMasterNames(String[] names){
        notStartMasterNames = names;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public String[] getNotStartMasterNames(){
        return notStartMasterNames;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public void setStartMasterInputMap(Map<String, Object> map){
        startMasterInputMap = map;
    }
    
    // DefaultCodeMasterFinderServiceMBean のJavaDoc
    public Map<String, Object> getStartMasterInputMap(){
        if(startMasterInputMap == null){
            startMasterInputMap = new LinkedHashMap<String, Object>();
        }
        return startMasterInputMap;
    }
    
    public void setBeanFlowFactory(BeanFlowFactory factory){
        beanFlowFactory = factory;
    }
    
    /**
     * JMSMessageConsumerFactoryを設定する。
     */
    public void setJMSMessageConsumerFactory(JMSMessageConsumerFactory factory){
        jmsTopicSubscriberFactory = factory;
    }
    
    /**
     * JMSMessageConsumerFactoryを設定する。
     */
    public void setJMSMessageConsumerFactories(JMSMessageConsumerFactory[] factories){
        jmsTopicSubscriberFactories = factories;
    }
    
    public void createService() throws Exception{
        master = new HashMap<String, TimeManageMaster>();
        masterNameSet = new HashSet<String>();
        stateListeners = new HashSet<JMSMessageConsumerFactoryStateListener>();
    }
    
    public void startService() throws Exception{
        
        if(beanFlowFactoryServiceName != null){
            beanFlowFactory = ServiceManagerFactory
                    .getServiceObject(beanFlowFactoryServiceName);
        }else if(beanFlowFactory == null){
            throw new IllegalArgumentException(
                "It is necessary to set BeanFlowFactoryServiceName or BeanFlowFactory."
            );
        }
        
        if(jmsTopicSubscriberFactoryServiceName != null){
            jmsTopicSubscriberFactory = ServiceManagerFactory
                    .getServiceObject(jmsTopicSubscriberFactoryServiceName);
        }
        
        if(jmsTopicSubscriberFactoryServiceNames != null
            && jmsTopicSubscriberFactoryServiceNames.length != 0){
            jmsTopicSubscriberFactories = new JMSMessageConsumerFactory[jmsTopicSubscriberFactoryServiceNames.length];
            for(int i = 0; i < jmsTopicSubscriberFactoryServiceNames.length; i++){
                jmsTopicSubscriberFactories[i] = ServiceManagerFactory
                        .getServiceObject(jmsTopicSubscriberFactoryServiceNames[i]);
            }
        }
        
        if(messageReceiverServiceName != null){
            messageReceiver = ServiceManagerFactory.getServiceObject(messageReceiverServiceName);
            Set<Object> keySet = new HashSet<Object>();
            if(notifyMasterNameMapping != null){
                keySet.addAll(notifyMasterNameMapping.keySet());
            }
            if(masterNames != null){
                for(int i = 0; i < masterNames.length; i++){
                    keySet.add(masterNames[i]);
                }
            }
            String[] keys = keySet.toArray(new String[keySet.size()]);
            if(subjects == null || subjects.length == 0){
                throw new IllegalArgumentException(
                    "It is necessary to set Subjects."
                );
            }
            for(int i = 0; i < subjects.length; i++){
                messageReceiver.addSubject(this, subjects[i], keys);
            }
        }
        initMasterHash();
        
        entryTopicListener(
            jmsTopicSubscriberFactory,
            jmsTopicSubscriberFactoryServiceName,
            null
        );
        if(jmsTopicSubscriberFactories != null
            && jmsTopicSubscriberFactories.length != 0){
            for(int i = 0; i < jmsTopicSubscriberFactories.length; i++){
                entryTopicListener(
                    jmsTopicSubscriberFactories[i],
                    jmsTopicSubscriberFactoryServiceNames == null
                         || jmsTopicSubscriberFactoryServiceNames.length == 0
                          ? null : jmsTopicSubscriberFactoryServiceNames[i],
                    null
                );
            }
        }
    }
    
    public void stopService() throws Exception{
        if(master != null){
            master.clear();
        }
        if(masterNameSet != null){
            masterNameSet.clear();
        }
        if(stateListeners != null){
            final Iterator<JMSMessageConsumerFactoryStateListener> listeners = stateListeners.iterator();
            while(listeners.hasNext()){
                JMSMessageConsumerFactoryStateListener listener = listeners.next();
                try{
                    listener.close();
                }catch(Exception e){
                }
                listeners.remove();
            }
        }
    }
    
    public void destroyService(){
        master = null;
        masterNameSet = null;
        stateListeners = null;
    }
    
    /**
     *  トピックのリスナー登録
     */
    private void entryTopicListener(
        JMSMessageConsumerFactory subscriberFactory,
        ServiceName factoryName,
        JMSMessageConsumerFactoryStateListener listener
    ) throws Exception{
        if(subscriberFactory == null){
            return;
        }
        ServiceName name = factoryName;
        if(name == null && jmsTopicSubscriberFactory instanceof Service){
            name = new ServiceName(
                ((Service)subscriberFactory).getServiceManagerName(),
                ((Service)subscriberFactory).getServiceName()
            );
        }
        if(name != null){
            if(listener == null){
                listener = new JMSMessageConsumerFactoryStateListener(
                    subscriberFactory,
                    factoryName
                );
            }
            if(!stateListeners.contains(listener)){
                stateListeners.add(listener);
                ServiceManagerFactory.addServiceStateListener(
                    name,
                    listener
                );
            }
            final Service service = ServiceManagerFactory.getService(name);
            if(service.getState() != State.STARTED){
                return;
            }
        }
        final TopicSubscriber subscriber
             = (TopicSubscriber)subscriberFactory.createConsumer();
        if(listener != null){
            listener.setTopicSubscriber(subscriber);
        }
        subscriber.setMessageListener(this);
        Connection con = subscriberFactory.getSessionFactory().getConnection();
        if(con == null){
            con = subscriberFactory.getSessionFactory()
                .getConnectionFactory().getConnection();
        }
        con.start();
    }
    
    /**
     * MasterHashの初期化 
     */
    private void initMasterHash() throws Exception{
        master.clear();
        masterNameSet.clear();
        if(masterNames == null){
            return;
        }
        
        final Map<String, Object> keyInputMap = new LinkedHashMap<String, Object>();
        final Collection<String> startMasterNameSet = Arrays.asList(
            startMasterNames == null ? masterNames : startMasterNames
        );
        final Collection<String> notStartMasterNameSet = Arrays.asList(
            notStartMasterNames == null ? new String[0] : notStartMasterNames
        );
        for(int i = 0; i < masterNames.length; i++){
            masterNameSet.add(masterNames[i]);
            if(startMasterNameSet.contains(masterNames[i])
                && !notStartMasterNameSet.contains(masterNames[i])){
                Object input = null;
                if(startMasterInputMap != null){
                    input = startMasterInputMap.get(masterNames[i]);
                }
                keyInputMap.put(masterNames[i], input);
            }
        }
        final Date updateTime = new Date();
        final Map<String, Object> keyMasterMap = createNewMasters(keyInputMap);
        for(Map.Entry<String, Object> entry : keyMasterMap.entrySet()){
            final String key = entry.getKey();
            final Object newMaster = entry.getValue();
            TimeManageMaster tm = (TimeManageMaster)master.get(key);
            if(tm == null){
                tm = new TimeManageMaster();
                tm.setMasterName(key);
                master.put(key, tm);
            }
            updateNewMaster(key, newMaster, updateTime);
        }
    }
    
    // CodeMasterFinder のJavaDoc
    public Map<String, Object> getCodeMasters(){
        HashMap<String, Object> map = new HashMap<String, Object>();
        Date nowDate = new Date();
        for(String key : masterNameSet){
            TimeManageMaster tmp = master.get(key);
            //現在時刻でマスターを検索
            Object mst = tmp.getMaster(nowDate);
            map.put(key,mst);
        }
        return map;
    }
    
    /**
     * マスター変更タイミング受信
     * Message は MapMessageとし、<br>
     * nameとvalueの組み合わせは、<br>
     * "key" (String)  | [マスター名] (String)<br>
     * "date" (String) | [データ有効日時](long)<br>
     * で設定すること<br>
     * 指定した日付以降の日付が既に設定されていれば、該当するマスタデータを無効にする
     * @see javax.jms.MessageListener#onMessage(javax.jms.Message)
     */
    @SuppressWarnings("unchecked")
    public void onMessage(Message msg){
        //Messageから更新情報を取得
        if(!(msg instanceof MapMessage)){
            getLogger().write("DCMF_00003", msg.getClass());
            return;
        }
        MapMessage mapMsg = (MapMessage)msg;
        Date now = new Date();
        try{
            final Map<String, Object> keyInputMap = new LinkedHashMap<String, Object>();
            final Enumeration<String> en = (Enumeration<String>)mapMsg.getMapNames();
            while(en.hasMoreElements()){
                String originalKey = en.nextElement();
                String[] keys = new String[]{originalKey};
                if(notifyMasterNameMapping != null
                     && notifyMasterNameMapping.containsKey(originalKey)){
                    keys = notifyMasterNameMapping.getProperty(originalKey).split(KEY_SEPARATOR);
                }
                Object input = mapMsg.getObject(originalKey);
                if(input != null && input instanceof byte[]){
                    ByteArrayInputStream bais = new ByteArrayInputStream((byte[])input);
                    ObjectInputStream ois = new ObjectInputStream(bais);
                    try{
                        input = ois.readObject();
                    }catch(Exception e){
                    }finally{
                        if(ois != null){
                            try{
                                ois.close();
                            }catch(IOException e){
                            }
                        }
                    }
                }
                for(int i = 0; i < keys.length; i++){
                    if(!masterNameSet.contains(keys[i])){
                        continue;
                    }
                    keyInputMap.put(keys[i], input);
                }
            }
            if(keyInputMap.size() == 0){
                return;
            }
            final Map<String, Object> keyMasterMap = createNewMasters(keyInputMap);
            for(Map.Entry<String, Object> entry : keyMasterMap.entrySet()){
                final String key = entry.getKey();
                final Object newMaster = entry.getValue();
                Date updateTime = null;
                try{
                    long utc = mapMsg.getLong(key + UPDATE_TIME_KEY);
                    updateTime = new Date(utc);
                }catch(Exception e){
                    updateTime = now;
                }
                updateNewMaster(key, newMaster, updateTime);
            }
        }catch(Exception e){
            getLogger().write("DCMF_00004", e);
            return;
        }
    }
    
    @SuppressWarnings("unchecked")
    public void onMessage(jp.ossc.nimbus.service.publish.Message msg){
        Date now = new Date();
        try{
            Map<String,Object> map = (Map<String,Object>)msg.getObject();
            final Map<String,Object> keyInputMap = new LinkedHashMap<String,Object>();
            for(String originalKey : map.keySet()){
                String[] keys = new String[]{originalKey};
                if(notifyMasterNameMapping != null
                     && notifyMasterNameMapping.containsKey(originalKey)){
                    keys = notifyMasterNameMapping.getProperty(originalKey).split(KEY_SEPARATOR);
                }
                Object input = map.get(originalKey);
                for(int i = 0; i < keys.length; i++){
                    if(!masterNameSet.contains(keys[i])){
                        continue;
                    }
                    keyInputMap.put(keys[i], input);
                }
            }
            if(keyInputMap.size() == 0){
                return;
            }
            final Map<String,Object> keyMasterMap = createNewMasters(keyInputMap);
            for(Map.Entry<String,Object> entry : keyMasterMap.entrySet()){
                final String key = entry.getKey();
                final Object newMaster = entry.getValue();
                Date updateTime = (Date)map.get(key + UPDATE_TIME_KEY);
                if(updateTime == null){
                    updateTime = now;
                }
                updateNewMaster(key, newMaster, updateTime);
            }
        }catch(Exception e){
            getLogger().write("CMS__00004", e);
            return;
        }
    }
    // CodeMasterFinderのJavaDoc
    public void updateCodeMaster(String key) throws Exception{
        updateCodeMaster(key, null);
    }
    
    // CodeMasterFinderのJavaDoc
    public void updateCodeMaster(String key, Date updateTime) throws Exception{
        updateCodeMaster(key, null, updateTime);
    }
    
    // CodeMasterFinderのJavaDoc
    public void updateCodeMaster(String key, Object input, Date updateTime) throws Exception{
        if(!masterNameSet.contains(key)){
            return;
        }
        if(updateTime == null){
            updateTime = new Date();
        }
        updateNewMaster(
            key,
            createNewMaster(key, input),
            updateTime
        );
    }
    
    // CodeMasterFinderのJavaDoc
    public Set<String> getCodeMasterNameSet(){
        return masterNameSet == null
             ? new HashSet<String>() : new HashSet<String>(masterNameSet);
    }
    
    /**
     * 指定された複数の新しいマスタを生成する。<p>
     *
     * @param keyInputMap マスタ名と入力のマップ
     * @return マスタ名とマスタのマップ
     * @exception Exception マスタの取得に失敗した場合
     */
    protected Map<String, Object> createNewMasters(Map<String, Object> keyInputMap) throws Exception{
        if(beanFlowFactory != null){
            return callBeanFlow(keyInputMap);
        }else{
            throw new java.lang.IllegalStateException("FacadeCaller and BeanFlowFactory is null.");
        }
    }
    
    /**
     * 指定された複数のマスタを取得するBeanFlow呼び出しを行う。<p>
     *
     * @param keyInputMap マスタ名と入力のマップ
     * @return マスタ名とマスタのマップ
     * @exception Exception マスタの取得に失敗した場合
     */
    protected Map<String, Object> callBeanFlow(Map<String, Object> keyInputMap) throws Exception{
        final Map<String, Object> result = new LinkedHashMap<String, Object>();
        for(Map.Entry<String, Object> entry : keyInputMap.entrySet()){
            result.put(
                entry.getKey(),
                callBeanFlow(
                    entry.getKey(),
                    entry.getValue()
                )
            );
        }
        return result;
    }
    
    /**
     * 指定された新しいマスタを生成する。<p>
     *
     * @param key マスタ名
     * @param input マスタ取得のための入力
     * @return マスタ
     * @exception Exception マスタの取得に失敗した場合
     */
    protected Object createNewMaster(String key, Object input) throws Exception{
        if(beanFlowFactory != null){
            return callBeanFlow(key, input);
        }else{
            throw new java.lang.IllegalStateException("BeanFlowFactory is null.");
        }
    }
    
    /**
     * 指定されたマスタを取得するBeanFlow呼び出しを行う。<p>
     *
     * @param key マスタ名
     * @param input BeanFlowへの入力
     * @return マスタ
     * @exception Exception マスタの取得に失敗した場合
     */
    protected Object callBeanFlow(String key, Object input) throws Exception{
        final BeanFlow beanflow = beanFlowFactory.createFlow(key);
        return beanflow.execute(input);
    }
    
    /**
     * 指定されたマスタを、指定した時間に更新するように登録する。<p>
     *
     * @param key マスタ名
     * @param mst マスタ
     * @param updateTime 更新時間
     */
    protected void updateNewMaster(
        String key,
        Object mst,
        Date updateTime
    ) throws Exception{
        if(mst == null){
            getLogger().write(
                "DCMF_00001",
                key,
                null
            );
            return;
        }
        TimeManageMaster tm = master.get(key);
        Date effectDt = updateTime;
        if(effectDt == null){
            effectDt = new Date();
        }
        Object newMst = mst;
        if(tm != null && (mst instanceof PartUpdateRecords)){
            PartUpdate pu = (PartUpdate)tm.getMaster(effectDt);
            if(pu == null){
                getLogger().write(
                    "DCMF_00005",
                    key
                );
                return;
            }
            PartUpdate newPu = pu.cloneAndUpdate((PartUpdateRecords)mst);
            newMst = newPu;
        }
        TimeManageMaster newTm = null;
        if(tm == null){
            newTm = new TimeManageMaster();
            newTm.setMasterName(key);
        }else{
            newTm = tm.cloneOwn();
        }
        newTm.addMaster(effectDt, newMst);
        newTm.clear() ;
        master.put(key, newTm);
        getLogger().write(
            "DCMF_00001",
            new Object[]{key, effectDt}
        );
    }
    
    /**
     * マスターBeanの時刻での管理を行うクラス<p>
     * @version $Name:  $
     * @author H.Nakano
     * @since 1.0
     */
    private class TimeManageMaster{
        private String flowKey;
        private List<Map<String, Object>> timeList;
        /**
         * コンストラクター
         */
        public TimeManageMaster(){
            timeList = new ArrayList<Map<String, Object>>();
        }
        /**
         * マスター名設定
         * @param name
         */
        public void setMasterName(String name){
            flowKey = name;
        }
        /**
         * マスター名取得
         * @return
         */
        public String getMasterName(){
            return flowKey;
        }
        
        /**
         * マスターデータ追加
         * @param time
         * @param master
         */
        public void addMaster(Date time ,Object master){
            Map<String, Object> rec = new HashMap<String, Object>();
            rec.put(DefaultCodeMasterFinderService.MASTER_DATA_KEY, master);
            rec.put(FIND_DATE_KEY, time);
            boolean instFlg = false;
            for(int cnt= timeList.size() - 1; cnt > -1 ; cnt--){
                Map<String, Object> map = timeList.get(cnt);
                Date tmpTime = (Date)map.get(FIND_DATE_KEY);
                if(tmpTime.before(time)){
                    if(cnt == timeList.size() - 1){
                        timeList.add(rec);
                    }else{
                        timeList.add(cnt + 1, rec);
                    }
                    instFlg = true;
                    break ;
                }
            }
            if(!instFlg){
                if(timeList.size()==0){
                    timeList.add(rec) ;
                }else{
                    timeList.add(0, rec);
                }
            }
        }
        /**
         * 指定時刻でのマスター取得
         * @param time
         * @return
         */
        public Object getMaster(Date time){
            Object ret = null ;
            for(int cnt = timeList.size() - 1; cnt > -1; cnt--){
                Map<String, Object> map = timeList.get(cnt);
                Date tmpTime = (Date)map.get(FIND_DATE_KEY);
                if(tmpTime.before(time) || tmpTime.equals(time)){
                    ret= map.get(MASTER_DATA_KEY);
                    break;
                }
            }
            return ret;
        }
        
        /**
         * 現在時刻で不必要なマスターを破棄
         */
        public void clear(){
            Date now = new Date() ;
            for(int cnt = timeList.size() - 1; cnt >= 0 ; cnt--){
                Map<String, Object> map = timeList.get(cnt) ;
                Date tmpTime = (Date)map.get(FIND_DATE_KEY) ;
                if(tmpTime.before(now)){
                    if(cnt > 0){
                        for(int rcnt = cnt-1;rcnt>=0;rcnt--){
                            timeList.remove(rcnt) ;
                        }
                        break;
                    }
                }
            }
        }
        /**
         * クローン
         * @return
         */
        public TimeManageMaster cloneOwn(){
            TimeManageMaster ret = new TimeManageMaster();
            ret.setMasterName(this.getMasterName());
            for(int cnt= 0; cnt < timeList.size(); cnt++){
                ret.timeList.add(this.timeList.get(cnt));
            }
            return ret ;
        }
    }
    
    private class JMSMessageConsumerFactoryStateListener
         implements ServiceStateListener{
        
        private JMSMessageConsumerFactory subscriberFactory;
        private TopicSubscriber subscriber;
        private ServiceName factoryName;
        
        public JMSMessageConsumerFactoryStateListener(
            JMSMessageConsumerFactory subscriberFactory,
            ServiceName factoryName
        ){
            this.subscriberFactory = subscriberFactory;
            this.factoryName = factoryName;
        }
        
        public void setTopicSubscriber(TopicSubscriber subscriber){
            this.subscriber = subscriber;
        }
        
        public void stateChanged(ServiceStateChangeEvent e) throws Exception{
            final Service service = e.getService();
            final State state = service.getState();
            final ServiceName name = new ServiceName(
                service.getServiceManagerName(),
                service.getServiceName()
            );
            if(!name.equals(factoryName)){
                return;
            }
            switch(state){
            case STARTED:
                entryTopicListener(subscriberFactory, factoryName, this);
                break;
            case STOPPED:
                if(subscriber != null){
                    try{
                        subscriber.close();
                    }catch(JMSException ex){}
                    subscriber = null;
                }
                break;
            default:
            }
        }
        
        public boolean isEnabledState(State state){
            return state == State.STARTED || state == State.STOPPED;
        }
        
        public void close() throws Exception{
            if(factoryName != null){
                ServiceManagerFactory.removeServiceStateListener(
                    factoryName,
                    this
                );
            }
            if(subscriber != null){
                try{
                    subscriber.close();
                }catch(JMSException e){}
                subscriber = null;
            }
        }
    }
}

