/*
 * 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.scheduler;

import java.util.*;
import java.util.Date;
import java.io.*;
import java.sql.*;
import java.text.*;

import jp.ossc.nimbus.beans.*;
import jp.ossc.nimbus.core.*;
import jp.ossc.nimbus.daemon.*;
import jp.ossc.nimbus.service.sql.*;
import jp.ossc.nimbus.service.distribute.ClusterService;
import jp.ossc.nimbus.service.sequence.Sequence;

/**
 * データベーススケジュール管理。<p>
 * スケジュールをデータベースで作成・管理し、実行すべきスケジュールを提供する。<br>
 * このサービスを使用するには、予めデータベースに以下のテーブルが必要である。<br>
 * <ul>
 *   <li>スケジュールマスタテーブル({@link DatabaseScheduleManagerService.ScheduleMasterTableSchema ScheduleMasterTableSchema})</li>
 *   <li>スケジュール依存関係マスタテーブル({@link DatabaseScheduleManagerService.ScheduleDependsMasterTableSchema ScheduleDependsMasterTableSchema})</li>
 *   <li>スケジュールテーブル({@link DatabaseScheduleManagerService.ScheduleTableSchema ScheduleTableSchema})</li>
 *   <li>スケジュール依存関係テーブル({@link DatabaseScheduleManagerService.ScheduleDependsTableSchema ScheduleDependsTableSchema})</li>
 * </ul>
 *
 * @author M.Takata
 */
public class DatabaseScheduleManagerService extends ServiceBase
 implements ScheduleManager, DatabaseScheduleManagerServiceMBean{
    
    private static final long serialVersionUID = -768179222440496616L;
    protected Properties scheduleMakerTypeMapping;
    protected Map<String, ScheduleMaker> addedScheduleMakerMap;
    protected Map<String, ScheduleMaker> scheduleMakerMap;
    protected ServiceName defaultScheduleMakerServiceName;
    protected ScheduleMaker defaultScheduleMaker;
    
    protected ServiceName connectionFactoryServiceName;
    protected ConnectionFactory connectionFactory;
    
    protected ScheduleMasterTableSchema scheduleMasterTableSchema = new ScheduleMasterTableSchema();
    protected ScheduleDependsMasterTableSchema scheduleDependsMasterTableSchema = new ScheduleDependsMasterTableSchema();
    protected ScheduleTableSchema scheduleTableSchema = new ScheduleTableSchema();
    protected ScheduleDependsTableSchema scheduleDependsTableSchema = new ScheduleDependsTableSchema();
    
    protected String nextScheduleIdSelectQuery;
    
    protected String dateFormat = DEFAULT_DATE_FORMAT;
    protected String timeFormat = DEFAULT_TIME_FORMAT;
    
    protected String updateUserId;
    
    protected Set<ScheduleControlListener> scheduleControlListeners;
    
    protected boolean isMakeScheduleOnStart = true;
    
    protected long controlStateCheckInterval = 1000l;
    protected Daemon controlStateChecker;
    
    protected long timeoverCheckInterval = 1000l;
    protected Daemon timeoverChecker;
    
    protected boolean isLockForFindExecutable;
    
    protected ServiceName clusterServiceName;
    protected ClusterService cluster;
    protected ClusterListener clusterListener;
    
    protected ServiceName sequenceServiceName;
    protected Sequence sequence;
    
    protected boolean isUseConcatFunction;
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setDefaultScheduleMakerServiceName(ServiceName name){
        defaultScheduleMakerServiceName = name;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public ServiceName getDefaultScheduleMakerServiceName(){
        return defaultScheduleMakerServiceName;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setScheduleMakerTypeMapping(Properties mapping){
        scheduleMakerTypeMapping = mapping;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public Properties getScheduleMakerTypeMapping(){
        return scheduleMakerTypeMapping;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setConnectionFactoryServiceName(ServiceName name){
        connectionFactoryServiceName = name;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public ServiceName getConnectionFactoryServiceName(){
        return connectionFactoryServiceName;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setDateFormat(String format){
        dateFormat = format;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public String getDateFormat(){
        return dateFormat;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setTimeFormat(String format){
        timeFormat = format;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public String getTimeFormat(){
        return timeFormat;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setUpdateUserId(String id){
        updateUserId = id;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public String getUpdateUserId(){
        return updateUserId;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public ScheduleMasterTableSchema getScheduleMasterTableSchema(){
        return scheduleMasterTableSchema;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setScheduleMasterTableSchema(ScheduleMasterTableSchema schema){
        scheduleMasterTableSchema = schema;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public ScheduleDependsMasterTableSchema getScheduleDependsMasterTableSchema(){
        return scheduleDependsMasterTableSchema;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setScheduleDependsMasterTableSchema(ScheduleDependsMasterTableSchema schema){
        scheduleDependsMasterTableSchema = schema;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public ScheduleTableSchema getScheduleTableSchema(){
        return scheduleTableSchema;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setScheduleTableSchema(ScheduleTableSchema schema){
        scheduleTableSchema = schema;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public ScheduleDependsTableSchema getScheduleDependsTableSchema(){
        return scheduleDependsTableSchema;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setScheduleDependsTableSchema(ScheduleDependsTableSchema schema){
        scheduleDependsTableSchema = schema;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setNextScheduleIdSelectQuery(String query){
        nextScheduleIdSelectQuery = query;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public String getNextScheduleIdSelectQuery(){
        return nextScheduleIdSelectQuery;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setMakeScheduleOnStart(boolean isMake){
        isMakeScheduleOnStart = isMake;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public boolean isMakeScheduleOnStart(){
        return isMakeScheduleOnStart;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setControlStateCheckInterval(long interval){
        controlStateCheckInterval = interval;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public long getControlStateCheckInterval(){
        return controlStateCheckInterval;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setTimeoverCheckInterval(long interval){
        timeoverCheckInterval = interval;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public long getTimeoverCheckInterval(){
        return timeoverCheckInterval;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setLockForFindExecutable(boolean isLock){
        isLockForFindExecutable = isLock;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public boolean isLockForFindExecutable(){
        return isLockForFindExecutable;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setClusterServiceName(ServiceName name){
        clusterServiceName = name;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public ServiceName getClusterServiceName(){
        return clusterServiceName;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setSequenceServiceName(ServiceName name){
        sequenceServiceName = name;
    }
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public ServiceName getSequenceServiceName(){
        return sequenceServiceName;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void setUseConcatFunction(boolean isUse){
        isUseConcatFunction = isUse;
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public boolean isUseConcatFunction(){
        return isUseConcatFunction;
    }
    
    /**
     * サービスの生成処理を行う。<p>
     *
     * @exception Exception サービスの生成処理に失敗した場合
     */
    public void createService() throws Exception{
        scheduleMakerMap = new HashMap<String, ScheduleMaker>();
        addedScheduleMakerMap = null;
        scheduleControlListeners = Collections.synchronizedSet(new LinkedHashSet<ScheduleControlListener>());
    }
    
    /**
     * サービスの開始処理を行う。<p>
     *
     * @exception Exception サービスの開始処理に失敗した場合
     */
    public void startService() throws Exception{
        
        if(scheduleMakerTypeMapping != null
             && scheduleMakerTypeMapping.size() != 0){
            final ServiceNameEditor editor = new ServiceNameEditor();
            editor.setServiceManagerName(getServiceManagerName());
            for(Map.Entry<Object, Object> entry : scheduleMakerTypeMapping.entrySet()){
                editor.setAsText((String)entry.getValue());
                final ServiceName scheduleMakerServiceName
                    = (ServiceName)editor.getValue();
                final ScheduleMaker scheduleMaker
                    = ServiceManagerFactory.getServiceObject(scheduleMakerServiceName);
                if(scheduleMakerMap.containsKey(entry.getKey())){
                    throw new IllegalArgumentException(
                        "Dupulicate scheduleMakerTypeMapping : "
                            + entry.getKey()
                    );
                }
                scheduleMakerMap.put((String)entry.getKey(), scheduleMaker);
            }
        }
        
        if(defaultScheduleMakerServiceName != null){
            defaultScheduleMaker = (ScheduleMaker)ServiceManagerFactory
                .getServiceObject(defaultScheduleMakerServiceName);
        }
        if(defaultScheduleMaker == null){
            final DefaultScheduleMakerService defaultScheduleMakerService
                = new DefaultScheduleMakerService();
            defaultScheduleMakerService.create();
            defaultScheduleMakerService.start();
            defaultScheduleMaker = defaultScheduleMakerService;
        }
        
        if(connectionFactoryServiceName != null){
            connectionFactory = (ConnectionFactory)ServiceManagerFactory
                .getServiceObject(connectionFactoryServiceName);
        }
        if(connectionFactory == null){
            throw new IllegalArgumentException("ConnectionFactory is null.");
        }
        
        if(sequenceServiceName != null){
            sequence = ServiceManagerFactory.getServiceObject(sequenceServiceName);
        }
        
        if(updateUserId == null){
            updateUserId = java.net.InetAddress.getLocalHost().getHostName();
        }
        
        final SimpleDateFormat format = new SimpleDateFormat(dateFormat);
        format.applyPattern(timeFormat);
        
        if(isMakeScheduleOnStart){
            final Date now = new Date();
            final List<Schedule> oldScheduleList = findSchedules(now);
            if(oldScheduleList == null || oldScheduleList.size() == 0){
                makeSchedule(now);
            }
        }
        
        if(controlStateCheckInterval > 0
            && scheduleControlListeners != null
            && scheduleControlListeners.size() != 0
        ){
            controlStateChecker = new Daemon(new ControlStateChecker());
            controlStateChecker.setName("Nimbus SchedulerManagerControlStateChecker " + getServiceNameObject());
            controlStateChecker.suspend();
            controlStateChecker.start();
        }
        
        if(timeoverCheckInterval > 0){
            timeoverChecker = new Daemon(new TimeoverChecker());
            timeoverChecker.setName("Nimbus SchedulerManagerTimeoverChecker " + getServiceNameObject());
            timeoverChecker.suspend();
            timeoverChecker.start();
        }
        if(clusterServiceName != null && (controlStateChecker != null || timeoverChecker != null)){
            cluster = ServiceManagerFactory.getServiceObject(clusterServiceName);
            clusterListener = new ClusterListener();
            cluster.addClusterListener(clusterListener);
        }else{
            if(controlStateChecker != null){
                controlStateChecker.resume();
            }
            if(timeoverChecker != null){
                timeoverChecker.resume();
            }
        }
    }
    
    /**
     * サービスの停止処理を行う。<p>
     *
     * @exception Exception サービスの停止処理に失敗した場合
     */
    public void stopService() throws Exception{
        
        if(controlStateChecker != null){
            controlStateChecker.stop();
        }
        
        if(timeoverChecker != null){
            timeoverChecker.stop();
        }
        
        if(cluster != null){
            cluster.removeClusterListener(clusterListener);
            clusterListener = null;
            cluster = null;
        }
        
        if(scheduleMakerMap != null){
            scheduleMakerMap.clear();
        }
    }
    
    /**
     * サービスの破棄処理を行う。<p>
     *
     * @exception Exception サービスの破棄処理に失敗した場合
     */
    public void destroyService() throws Exception{
        scheduleMakerMap = null;
        addedScheduleMakerMap = null;
        scheduleControlListeners = null;
    }
    
    // ScheduleManagerのJavaDoc
    public void makeSchedule(Date date) throws ScheduleMakeException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleMakeException(e);
        }
        Statement st1 = null;
        Statement st2 = null;
        PreparedStatement ps1 = null;
        PreparedStatement ps2 = null;
        ResultSet rs = null;
        try{
            final Map<String, Set<String>> dependsMasterMap = new HashMap<String, Set<String>>();
            st1 = con.createStatement();
            rs = st1.executeQuery(
                "select * from " + scheduleDependsMasterTableSchema.table
            );
            while(rs.next()){
                final String id = rs.getString(
                    scheduleDependsMasterTableSchema.id
                );
                Set<String> dependsIdSet = dependsMasterMap.get(id);
                if(dependsIdSet == null){
                    dependsIdSet = new HashSet<String>();
                    dependsMasterMap.put(id, dependsIdSet);
                }
                dependsIdSet.add(
                    rs.getString(scheduleDependsMasterTableSchema.dependsId)
                );
            }
            rs.close();
            rs = null;
            
            rs = st1.executeQuery(
                "select * from " + scheduleMasterTableSchema.table
            );
            st2 = con.createStatement();
            ps1 = con.prepareStatement(
                "insert into " + scheduleTableSchema.table
                    + " ("
                    + scheduleTableSchema.id + ','
                    + scheduleTableSchema.masterId + ','
                    + scheduleTableSchema.date + ','
                    + scheduleTableSchema.time + ','
                    + scheduleTableSchema.taskName + ','
                    + scheduleTableSchema.input + ','
                    + scheduleTableSchema.output + ','
                    + scheduleTableSchema.initialDate + ','
                    + scheduleTableSchema.initialTime + ','
                    + scheduleTableSchema.retryInterval + ','
                    + scheduleTableSchema.retryEndTime + ','
                    + scheduleTableSchema.maxDelayTime + ','
                    + scheduleTableSchema.state + ','
                    + scheduleTableSchema.controlState + ','
                    + scheduleTableSchema.checkState + ','
                    + scheduleTableSchema.executorKey + ','
                    + scheduleTableSchema.executorType + ','
                    + scheduleTableSchema.executeStartTime + ','
                    + scheduleTableSchema.executeEndTime + ','
                    + scheduleTableSchema.rowVersion + ','
                    + scheduleTableSchema.updateUserId + ','
                    + scheduleTableSchema.updateTime
                    + ") values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'0','" + updateUserId + "',?)"
            );
            ps2 = con.prepareStatement(
                "insert into " + scheduleDependsTableSchema.table
                    + " ("
                    + scheduleDependsTableSchema.id + ','
                    + scheduleDependsTableSchema.dependsId + ','
                    + scheduleDependsTableSchema.rowVersion + ','
                    + scheduleDependsTableSchema.updateUserId + ','
                    + scheduleDependsTableSchema.updateTime
                    + ") values(?,?,'0','" + updateUserId + "',?)"
            );
            while(rs.next()){
                DefaultScheduleMaster scheduleMaster = createScheduleMaster(rs);
                final Set<String> dependsIdSet
                    = dependsMasterMap.get(scheduleMaster.getId());
                if(dependsIdSet != null){
                    scheduleMaster.setDepends(
                        dependsIdSet.toArray(
                            new String[dependsIdSet.size()]
                        )
                    );
                }
                
                ScheduleMaker maker = scheduleMakerMap.get(
                    scheduleMaster.getScheduleType()
                );
                if(maker == null){
                    maker = defaultScheduleMaker;
                }
                if(!scheduleMaster.isEnabled()){
                    continue;
                }
                final Schedule[] schedules = maker.makeSchedule(
                    date,
                    scheduleMaster
                );
                if(schedules == null || schedules.length == 0){
                    continue;
                }
                for(int i = 0; i < schedules.length; i++){
                    addSchedule(
                        st2,
                        ps1,
                        ps2,
                        schedules[i]
                    );
                }
            }
        }catch(ScheduleManageException e){
            throw new ScheduleMakeException(e);
        }catch(ParseException e){
            throw new ScheduleMakeException(e);
        }catch(ClassNotFoundException e){
            throw new ScheduleMakeException(e);
        }catch(IOException e){
            throw new ScheduleMakeException(e);
        }catch(SQLException e){
            throw new ScheduleMakeException(e);
        }finally{
            if(st1 != null){
                try{
                    st1.close();
                }catch(SQLException e){
                }
            }
            if(st2 != null){
                try{
                    st2.close();
                }catch(SQLException e){
                }
            }
            if(ps1 != null){
                try{
                    ps1.close();
                }catch(SQLException e){
                }
            }
            if(ps2 != null){
                try{
                    ps2.close();
                }catch(SQLException e){
                }
            }
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    /**
     * スケジュールを追加する。<p>
     *
     * @param schedule スケジュール
     * @param isCreateId スケジュールIDを再発番するかどうか
     * @exception ScheduleManageException スケジュールの追加に失敗した場合
     */
    protected void addSchedule(
        Schedule schedule,
        boolean isCreateId
    ) throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        Statement st = null;
        PreparedStatement ps1 = null;
        PreparedStatement ps2 = null;
        try{
            if(isCreateId){
                st = con.createStatement();
            }
            ps1 = con.prepareStatement(
                "insert into " + scheduleTableSchema.table
                    + " ("
                    + scheduleTableSchema.id + ','
                    + scheduleTableSchema.masterId + ','
                    + scheduleTableSchema.date + ','
                    + scheduleTableSchema.time + ','
                    + scheduleTableSchema.taskName + ','
                    + scheduleTableSchema.input + ','
                    + scheduleTableSchema.output + ','
                    + scheduleTableSchema.initialDate + ','
                    + scheduleTableSchema.initialTime + ','
                    + scheduleTableSchema.retryInterval + ','
                    + scheduleTableSchema.retryEndTime + ','
                    + scheduleTableSchema.maxDelayTime + ','
                    + scheduleTableSchema.state + ','
                    + scheduleTableSchema.controlState + ','
                    + scheduleTableSchema.checkState + ','
                    + scheduleTableSchema.executorKey + ','
                    + scheduleTableSchema.executorType + ','
                    + scheduleTableSchema.executeStartTime + ','
                    + scheduleTableSchema.executeEndTime + ','
                    + scheduleTableSchema.rowVersion + ','
                    + scheduleTableSchema.updateUserId + ','
                    + scheduleTableSchema.updateTime
                    + ") values(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,'0','" + updateUserId + "',?)"
            );
            ps2 = con.prepareStatement(
                "insert into " + scheduleDependsTableSchema.table
                    + " ("
                    + scheduleDependsTableSchema.id + ','
                    + scheduleDependsTableSchema.dependsId + ','
                    + scheduleDependsTableSchema.rowVersion + ','
                    + scheduleDependsTableSchema.updateUserId + ','
                    + scheduleDependsTableSchema.updateTime
                    + ") values(?,?,'0','" + updateUserId + "',?)"
            );
            addSchedule(
                st,
                ps1,
                ps2,
                schedule
            );
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }finally{
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(ps1 != null){
                try{
                    ps1.close();
                }catch(SQLException e){
                }
            }
            if(ps2 != null){
                try{
                    ps2.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    /**
     * スケジュールを追加する。<p>
     *
     * @param nextScheduleIdStatement スケジュールIDを採番するStatement
     * @param scheduleInsertStatement スケジュールをINSERTするStatement
     * @param scheduleDependsInsertStatement スケジュールの依存関係をINSERTするStatement
     * @param schedule スケジュール
     * @exception ScheduleManageException スケジュールの追加に失敗した場合
     */
    protected void addSchedule(
        Statement nextScheduleIdStatement,
        PreparedStatement scheduleInsertStatement,
        PreparedStatement scheduleDependsInsertStatement,
        Schedule schedule
    ) throws ScheduleManageException{
        ResultSet rs = null;
        try{
            if(nextScheduleIdSelectQuery != null){
                rs = nextScheduleIdStatement.executeQuery(
                    nextScheduleIdSelectQuery
                );
                rs.next();
                schedule.setId(rs.getObject(1).toString());
                rs.close();
                rs = null;
            }else if(sequence != null){
                schedule.setId(sequence.increment());
            }
            scheduleInsertStatement.setString(
                1,
                schedule.getId()
            );
            scheduleInsertStatement.setString(
                2,
                schedule.getMasterId()
            );
            final SimpleDateFormat format
                = new SimpleDateFormat(dateFormat);
            if(schedule.getTime() == null){
                scheduleInsertStatement.setNull(
                    3,
                    Types.CHAR
                );
            }else{
                scheduleInsertStatement.setString(
                    3,
                    format.format(schedule.getTime())
                );
            }
            format.applyPattern(timeFormat);
            if(schedule.getTime() == null){
                scheduleInsertStatement.setNull(
                    4,
                    Types.CHAR
                );
            }else{
                scheduleInsertStatement.setString(
                    4,
                    format.format(schedule.getTime())
                );
            }
            scheduleInsertStatement.setString(
                5,
                schedule.getTaskName()
            );
            if(schedule.getInput() == null){
                scheduleInsertStatement.setNull(
                    6,
                    Types.VARCHAR
                );
            }else{
                scheduleTableSchema.setInputObject(6, scheduleInsertStatement, schedule.getInput());
            }
            if(schedule.getOutput() == null){
                scheduleInsertStatement.setNull(
                    7,
                    Types.VARCHAR
                );
            }else{
                scheduleTableSchema.setOutputObject(7, scheduleInsertStatement, schedule.getOutput());
            }
            format.applyPattern(dateFormat);
            final Date initialTime = schedule.getInitialTime() == null
                ? schedule.getTime() : schedule.getInitialTime();
            if(initialTime == null){
                scheduleInsertStatement.setNull(
                    8,
                    Types.CHAR
                );
            }else{
                scheduleInsertStatement.setString(
                    8,
                    format.format(initialTime)
                );
            }
            format.applyPattern(timeFormat);
            if(initialTime == null){
                scheduleInsertStatement.setNull(
                    9,
                    Types.CHAR
                );
            }else{
                scheduleInsertStatement.setString(
                    9,
                    format.format(initialTime)
                );
            }
            if(schedule.getRetryInterval() > 0){
                scheduleInsertStatement.setLong(
                    10,
                    schedule.getRetryInterval()
                );
            }else{
                scheduleInsertStatement.setNull(
                    10,
                    Types.DECIMAL
                );
            }
            format.applyPattern(dateFormat + timeFormat);
            if(schedule.getRetryEndTime() == null){
                scheduleInsertStatement.setNull(
                    11,
                    Types.CHAR
                );
            }else{
                scheduleInsertStatement.setString(
                    11,
                    format.format(schedule.getRetryEndTime())
                );
            }
            if(schedule.getMaxDelayTime() > 0){
                scheduleInsertStatement.setLong(
                    12,
                    schedule.getMaxDelayTime()
                );
            }else{
                scheduleInsertStatement.setNull(
                    12,
                    Types.DECIMAL
                );
            }
            scheduleInsertStatement.setString(
                13,
                scheduleTableSchema.getStateString(schedule.getState())
            );
            scheduleInsertStatement.setString(
                14,
                scheduleTableSchema.getControlStateString(
                    schedule.getControlState()
                )
            );
            scheduleInsertStatement.setString(
                15,
                scheduleTableSchema.getCheckStateString(
                    schedule.getCheckState()
                )
            );
            if(schedule.getExecutorKey() == null){
                scheduleInsertStatement.setNull(
                    16,
                    Types.VARCHAR
                );
            }else{
                scheduleInsertStatement.setString(
                    16,
                    schedule.getExecutorKey()
                );
            }
            if(schedule.getExecutorType() == null){
                scheduleInsertStatement.setNull(
                    17,
                    Types.VARCHAR
                );
            }else{
                scheduleInsertStatement.setString(
                    17,
                    schedule.getExecutorType()
                );
            }
            if(schedule.getExecuteStartTime() == null){
                scheduleInsertStatement.setNull(
                    18,
                    Types.CHAR
                );
            }else{
                scheduleInsertStatement.setString(
                    18,
                    format.format(schedule.getExecuteStartTime())
                );
            }
            if(schedule.getExecuteEndTime() == null){
                scheduleInsertStatement.setNull(
                    19,
                    Types.CHAR
                );
            }else{
                scheduleInsertStatement.setString(
                    19,
                    format.format(schedule.getExecuteEndTime())
                );
            }
            Timestamp now = new Timestamp(System.currentTimeMillis());
            scheduleInsertStatement.setTimestamp(
                20,
                now
            );
            scheduleInsertStatement.executeUpdate();
            
            final String[] depends = schedule.getDepends();
            if(depends != null && depends.length != 0){
                if(scheduleDependsInsertStatement == null){
                    throw new ScheduleManageException("ScheduleDependsInsertQuery is null.");
                }
                for(int i = 0; i < depends.length; i++){
                    scheduleDependsInsertStatement.setString(
                        1,
                        schedule.getId()
                    );
                    scheduleDependsInsertStatement.setString(
                        2,
                        depends[i]
                    );
                    scheduleDependsInsertStatement.setTimestamp(
                        3,
                        now
                    );
                    scheduleDependsInsertStatement.executeUpdate();
                }
            }
        }catch(IOException e){
            throw new ScheduleManageException(e);
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public void setScheduleMaker(String scheduleType, ScheduleMaker maker)
     throws IllegalArgumentException{
        if(addedScheduleMakerMap == null){
            addedScheduleMakerMap = new HashMap<String, ScheduleMaker>();
        }
        if(addedScheduleMakerMap.containsKey(scheduleType)){
            throw new IllegalArgumentException(
                "Dupulicate scheduleType : " + scheduleType
            );
        }
        addedScheduleMakerMap.put(scheduleType, maker);
    }
    
    // ScheduleManagerのJavaDoc
    public ScheduleMaker getScheduleMaker(String scheduleType){
        return scheduleMakerMap.get(scheduleType);
    }
    
    // ScheduleManagerのJavaDoc
    public void setDefaultScheduleMaker(ScheduleMaker maker){
        defaultScheduleMaker = maker;
    }
    
    // ScheduleManagerのJavaDoc
    public ScheduleMaker getDefaultScheduleMaker(){
        return defaultScheduleMaker;
    }
    
    // ScheduleManagerのJavaDoc
    public List<ScheduleMaster> findAllScheduleMasters() throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        Statement st = null;
        PreparedStatement ps1 = null;
        PreparedStatement ps2 = null;
        ResultSet rs = null;
        List<ScheduleMaster> result = new ArrayList<ScheduleMaster>();
        try{
            final Map<String, Set<String>> dependsMasterMap = new HashMap<String, Set<String>>();
            st = con.createStatement();
            rs = st.executeQuery(
                "select * from " + scheduleDependsMasterTableSchema.table
            );
            while(rs.next()){
                final String id = rs.getString(
                    scheduleDependsMasterTableSchema.id
                );
                Set<String> dependsIdSet = dependsMasterMap.get(id);
                if(dependsIdSet == null){
                    dependsIdSet = new HashSet<String>();
                    dependsMasterMap.put(id, dependsIdSet);
                }
                dependsIdSet.add(
                    rs.getString(scheduleDependsMasterTableSchema.dependsId)
                );
            }
            rs.close();
            rs = null;
            
            rs = st.executeQuery(
                "select * from " + scheduleMasterTableSchema.table
            );
            while(rs.next()){
                DefaultScheduleMaster scheduleMaster = createScheduleMaster(rs);
                final Set<String> dependsIdSet
                    = dependsMasterMap.get(scheduleMaster.getId());
                if(dependsIdSet != null){
                    scheduleMaster.setDepends(
                        dependsIdSet.toArray(
                            new String[dependsIdSet.size()]
                        )
                    );
                }
                result.add(scheduleMaster);
            }
        }catch(ScheduleManageException e){
            throw new ScheduleMakeException(e);
        }catch(ParseException e){
            throw new ScheduleMakeException(e);
        }catch(ClassNotFoundException e){
            throw new ScheduleMakeException(e);
        }catch(IOException e){
            throw new ScheduleMakeException(e);
        }catch(SQLException e){
            throw new ScheduleMakeException(e);
        }finally{
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(ps1 != null){
                try{
                    ps1.close();
                }catch(SQLException e){
                }
            }
            if(ps2 != null){
                try{
                    ps2.close();
                }catch(SQLException e){
                }
            }
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
        return result;
    }
    
    // ScheduleManagerのJavaDoc
    public ScheduleMaster findScheduleMaster(String id) throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select * from " + scheduleMasterTableSchema.table
                    + " where " + scheduleMasterTableSchema.id + "=?"
            );
            st.setString(1, id);
            rs = st.executeQuery();
            if(!rs.next()){
                return null;
            }
            return setDependsOnScheduleMaster(con, createScheduleMaster(rs));
        }catch(ParseException e){
            throw new ScheduleManageException(e);
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }catch(IOException e){
            throw new ScheduleManageException(e);
        }catch(ClassNotFoundException e){
            throw new ScheduleManageException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    /**
     * スケジュールマスタテーブルの検索結果からスケジュールマスタを生成する。<p>
     *
     * @param rs スケジュールマスタテーブルの検索結果
     * @return スケジュールマスタ
     * @exception SQLException SQL例外
     * @exception ParseException 日付と時刻のパースに失敗した場合
     * @exception IOException 入力オブジェクトの読み込みに失敗した場合
     * @exception ClassNotFoundException 入力オブジェクトの読み込みに失敗した場合
     */
    protected DefaultScheduleMaster createScheduleMaster(ResultSet rs)
     throws SQLException, ParseException, IOException, ClassNotFoundException{
        final SimpleDateFormat timeFormatter
            = new SimpleDateFormat(timeFormat);
        final DefaultScheduleMaster scheduleMaster
            = new DefaultScheduleMaster();
        scheduleMaster.setId(
            rs.getString(scheduleMasterTableSchema.id)
        );
        scheduleMaster.setTaskName(
            rs.getString(scheduleMasterTableSchema.taskName)
        );
        scheduleMaster.setScheduleType(
            rs.getString(scheduleMasterTableSchema.scheduleType)
        );
        scheduleMaster.setInput(
            scheduleMasterTableSchema.getInputObject(rs)
        );
        String str = rs.getString(scheduleMasterTableSchema.startTime);
        if(str != null){
            scheduleMaster.setStartTime(timeFormatter.parse(str));
        }
        str = rs.getString(scheduleMasterTableSchema.endTime);
        if(str != null){
            scheduleMaster.setEndTime(timeFormatter.parse(str));
        }
        long longVal = rs.getLong(
            scheduleMasterTableSchema.repeatInterval
        );
        if(!rs.wasNull()){
            scheduleMaster.setRepeatInterval(longVal);
        }
        longVal = rs.getLong(
            scheduleMasterTableSchema.retryInterval
        );
        if(!rs.wasNull()){
            scheduleMaster.setRetryInterval(longVal);
        }
        str = rs.getString(scheduleMasterTableSchema.retryEndTime);
        if(str != null){
            scheduleMaster.setRetryEndTime(timeFormatter.parse(str));
        }
        longVal = rs.getLong(
            scheduleMasterTableSchema.maxDelayTime
        );
        if(!rs.wasNull()){
            scheduleMaster.setMaxDelayTime(longVal);
        }
        scheduleMaster.setExecutorKey(
            rs.getString(scheduleMasterTableSchema.executorKey)
        );
        scheduleMaster.setExecutorType(
            rs.getString(scheduleMasterTableSchema.executorType)
        );
        Object obj = rs.getObject(scheduleMasterTableSchema.enable);
        boolean isEnabled = true;
        if(obj instanceof Boolean){
            isEnabled = ((Boolean)obj).booleanValue();
        }else if(obj instanceof Number){
            isEnabled = ((Number)obj).intValue() != 0;
        }else{
            if(obj == null){
                isEnabled = false;
            }else{
                isEnabled = Boolean.valueOf(obj.toString())
                    .booleanValue();
            }
        }
        scheduleMaster.setEnabled(isEnabled);
        return scheduleMaster;
    }
    
    protected ScheduleMaster setDependsOnScheduleMaster(Connection con, ScheduleMaster schedule) throws SQLException{
        final List<ScheduleMaster> tmp = new ArrayList<ScheduleMaster>();
        tmp.add(schedule);
        setDependsOnScheduleMasters(con, tmp);
        return (ScheduleMaster)tmp.get(0);
    }
    
    protected List<ScheduleMaster> setDependsOnScheduleMasters(Connection con, List<ScheduleMaster> schedules) throws SQLException{
        if(schedules.size() == 0){
            return schedules;
        }
        final StringBuilder buf = new StringBuilder();
        Statement st = con.createStatement();
        ResultSet rs = null;
        Map<String, ScheduleMaster> scheduleMap = new HashMap<String, ScheduleMaster>();
        Map<String, List<String>> scheduleDependsMap = new HashMap<String, List<String>>();
        int to = schedules.size() % 1000 == 0 ? schedules.size() / 1000 : schedules.size() / 1000 + 1; 
        for(int i = 0; i < to; i++){
            scheduleMap.clear();
            scheduleDependsMap.clear();
            buf.setLength(0);
            buf.append("select * from ");
            buf.append(scheduleDependsMasterTableSchema.table);
            buf.append(" where ");
            buf.append(scheduleDependsMasterTableSchema.id);
            buf.append(" in (");
            int startIndex = i * 1000;
            for(int j = startIndex, jmax = Math.min(startIndex + 1000, schedules.size()); j < jmax; j++){
                ScheduleMaster schedule = schedules.get(j);
                buf.append('\'').append(schedule.getId()).append('\'');
                if(j != jmax - 1){
                    buf.append(',');
                }
                scheduleMap.put(schedule.getId(), schedule);
            }
            buf.append(')');
            rs = st.executeQuery(buf.toString());
            while(rs.next()){
                final String id = rs.getString(scheduleDependsMasterTableSchema.id);
                List<String> depends = scheduleDependsMap.get(id);
                if(depends == null){
                    depends = new ArrayList<String>();
                    scheduleDependsMap.put(id, depends);
                }
                depends.add(rs.getString(scheduleDependsMasterTableSchema.dependsId));
            }
            final Iterator<Map.Entry<String, List<String>>> itr = scheduleDependsMap.entrySet().iterator();
            while(itr.hasNext()){
                final Map.Entry<String, List<String>> entry = itr.next();
                final DefaultScheduleMaster schedule = (DefaultScheduleMaster)scheduleMap.get(entry.getKey());
                final List<String> depends = entry.getValue();
                if(depends.size() != 0){
                    schedule.setDepends(depends.toArray(new String[depends.size()]));
                }
            }
            rs.close();
        }
        return schedules;
    }
    
    // ScheduleManagerのJavaDoc
    public List<Schedule> findAllSchedules() throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        Statement st = null;
        ResultSet rs = null;
        try{
            st = con.createStatement();
            rs = st.executeQuery("select * from " + scheduleTableSchema.table);
            final List<Schedule> result = new ArrayList<Schedule>();
            while(rs.next()){
                result.add(createSchedule(rs));
            }
            setDependsOnSchedules(con, result);
            return result;
        }catch(ParseException e){
            throw new ScheduleManageException(e);
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }catch(IOException e){
            throw new ScheduleManageException(e);
        }catch(ClassNotFoundException e){
            throw new ScheduleManageException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    /**
     * スケジュールテーブルの検索結果からスケジュールを生成する。<p>
     *
     * @param rs スケジュールテーブルの検索結果
     * @return スケジュール
     * @exception SQLException SQL例外
     * @exception ParseException 日付と時刻のパースに失敗した場合
     * @exception IOException 入力オブジェクトの読み込みに失敗した場合
     * @exception ClassNotFoundException 入力オブジェクトの読み込みに失敗した場合
     */
    protected Schedule createSchedule(ResultSet rs)
     throws SQLException, ParseException, IOException, ClassNotFoundException{
        final DefaultSchedule schedule = new DefaultSchedule();
        schedule.setId(rs.getString(scheduleTableSchema.id));
        schedule.setMasterId(rs.getString(scheduleTableSchema.masterId));
        final SimpleDateFormat format = new SimpleDateFormat(
            dateFormat + timeFormat);
        String str = rs.getString(scheduleTableSchema.date)
            + rs.getString(scheduleTableSchema.time);
        schedule.setTime(format.parse(str));
        schedule.setTaskName(rs.getString(scheduleTableSchema.taskName));
        schedule.setInput(scheduleTableSchema.getInputObject(rs));
        schedule.setOutput(scheduleTableSchema.getOutputObject(rs));
        str = rs.getString(scheduleTableSchema.initialDate)
            + rs.getString(scheduleTableSchema.initialTime);
        schedule.setInitialTime(format.parse(str));
        long longVal = rs.getLong(scheduleTableSchema.retryInterval);
        if(!rs.wasNull()){
            schedule.setRetryInterval(longVal);
        }
        final String retryEndTimeStr = rs.getString(scheduleTableSchema.retryEndTime);
        if(retryEndTimeStr != null){
            schedule.setRetryEndTime(format.parse(retryEndTimeStr));
        }
        longVal = rs.getLong(scheduleTableSchema.maxDelayTime);
        if(!rs.wasNull()){
            schedule.setMaxDelayTime(longVal);
        }
        schedule.setState(
            scheduleTableSchema.getState(
                rs.getString(scheduleTableSchema.state)
            )
        );
        schedule.setControlState(
            scheduleTableSchema.getControlState(
                rs.getString(scheduleTableSchema.controlState)
            )
        );
        schedule.setCheckState(
            scheduleTableSchema.getCheckState(
                rs.getString(scheduleTableSchema.checkState)
            )
        );
        schedule.setExecutorKey(rs.getString(scheduleTableSchema.executorKey));
        schedule.setExecutorType(rs.getString(scheduleTableSchema.executorType));
        final String executeStartTimeStr = rs.getString(scheduleTableSchema.executeStartTime);
        if(executeStartTimeStr != null){
            schedule.setExecuteStartTime(format.parse(executeStartTimeStr));
        }
        final String executeEndTimeStr = rs.getString(scheduleTableSchema.executeEndTime);
        if(executeEndTimeStr != null){
            schedule.setExecuteEndTime(format.parse(executeEndTimeStr));
        }
        return schedule;
    }
    
    protected Schedule setDependsOnSchedule(Connection con, Schedule schedule) throws SQLException{
        final List<Schedule> tmp = new ArrayList<Schedule>();
        tmp.add(schedule);
        setDependsOnSchedules(con, tmp);
        return (Schedule)tmp.get(0);
    }
    
    protected List<Schedule> setDependsOnSchedules(Connection con, List<Schedule> schedules) throws SQLException{
        if(schedules.size() == 0){
            return schedules;
        }
        final StringBuilder buf = new StringBuilder();
        Statement st = con.createStatement();
        ResultSet rs = null;
        Map<String, Schedule> scheduleMap = new HashMap<String, Schedule>();
        Map<String, List<String>> scheduleDependsMap = new HashMap<String, List<String>>();
        int to = schedules.size() % 1000 == 0 ? schedules.size() / 1000 : schedules.size() / 1000 + 1; 
        for(int i = 0; i < to; i++){
            scheduleMap.clear();
            scheduleDependsMap.clear();
            buf.setLength(0);
            buf.append("select * from ");
            buf.append(scheduleDependsTableSchema.table);
            buf.append(" where ");
            buf.append(scheduleDependsTableSchema.id);
            buf.append(" in (");
            int startIndex = i * 1000;
            for(int j = startIndex, jmax = Math.min(startIndex + 1000, schedules.size()); j < jmax; j++){
                Schedule schedule = (Schedule)schedules.get(j);
                buf.append('\'').append(schedule.getId()).append('\'');
                if(j != jmax - 1){
                    buf.append(',');
                }
                scheduleMap.put(schedule.getId(), schedule);
            }
            buf.append(')');
            rs = st.executeQuery(buf.toString());
            while(rs.next()){
                final String id = rs.getString(scheduleDependsTableSchema.id);
                List<String> depends = scheduleDependsMap.get(id);
                if(depends == null){
                    depends = new ArrayList<String>();
                    scheduleDependsMap.put(id, depends);
                }
                depends.add(rs.getString(scheduleDependsTableSchema.dependsId));
            }
            final Iterator<Map.Entry<String, List<String>>> itr = scheduleDependsMap.entrySet().iterator();
            while(itr.hasNext()){
                final Map.Entry<String, List<String>> entry = itr.next();
                final DefaultSchedule schedule = (DefaultSchedule)scheduleMap.get(entry.getKey());
                final List<String> depends = entry.getValue();
                if(depends.size() != 0){
                    schedule.setDepends(depends.toArray(new String[depends.size()]));
                }
            }
            rs.close();
        }
        return schedules;
    }
    
    // ScheduleManagerのJavaDoc
    public Schedule findSchedule(String id) throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select * from " + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, id);
            rs = st.executeQuery();
            if(!rs.next()){
                return null;
            }
            return setDependsOnSchedule(con, createSchedule(rs));
        }catch(ParseException e){
            throw new ScheduleManageException(e);
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }catch(IOException e){
            throw new ScheduleManageException(e);
        }catch(ClassNotFoundException e){
            throw new ScheduleManageException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public List<Schedule> findSchedules(String masterId) throws ScheduleManageException{
        List<Schedule> result = new ArrayList<Schedule>();
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select * from " + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.masterId + "=?"
            );
            st.setString(1, masterId);
            rs = st.executeQuery();
            while(rs.next()){
                result.add(createSchedule(rs));
            }
            setDependsOnSchedules(con, result);
        }catch(ParseException e){
            throw new ScheduleManageException(e);
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }catch(IOException e){
            throw new ScheduleManageException(e);
        }catch(ClassNotFoundException e){
            throw new ScheduleManageException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
        Collections.sort(result);
        return result;
    }
    
    // ScheduleManagerのJavaDoc
    public List<Schedule> findSchedules(Date date) throws ScheduleManageException{
        final Calendar cal = Calendar.getInstance();
        cal.setTime(date);
        cal.set(Calendar.HOUR_OF_DAY, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);
        final Date from = cal.getTime();
        cal.set(Calendar.HOUR_OF_DAY, 23);
        cal.set(Calendar.MINUTE, 59);
        cal.set(Calendar.SECOND, 59);
        cal.set(Calendar.MILLISECOND, 999);
        final Date to = cal.getTime();
        return findSchedules(from, to);
    }
    
    // ScheduleManagerのJavaDoc
    public List<Schedule> findSchedules(Date from, Date to)
     throws ScheduleManageException{
        return findSchedules(from, to, null);
    }
    
    // ScheduleManagerのJavaDoc
    public List<Schedule> findSchedules(int[] states) throws ScheduleManageException{
        return findSchedules(null, null, states);
    }
    
    // ScheduleManagerのJavaDoc
    public List<Schedule> findSchedules(Date from, Date to, int[] states)
     throws ScheduleManageException{
        return findSchedules(from, to, states, null, null, null, false);
    }
    
    // ScheduleManagerのJavaDoc
    public List<Schedule> findSchedules(Date from, Date to, int[] states, String masterId) throws ScheduleManageException{
        return findSchedules(from, to, states, masterId, null, null, false);
    }
    
    protected StringBuilder concatQuery(StringBuilder buf, String s1, String s2){
        if(isUseConcatFunction){
            buf.append("concat(").append(s1).append(',').append(s2).append(')');
        }else{
            buf.append(s1).append("||").append(s2);
        }
        return buf;
    }
    
    protected List<Schedule> findSchedules(
        Date from,
        Date to,
        int[] states,
        String masterId,
        String[] executorTypes,
        String executorKey,
        boolean isLock
    ) throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            StringBuilder buf = new StringBuilder();
            buf.append("select * from ");
            buf.append(scheduleTableSchema.table);
            boolean isAppendWhere = false;
            if(from != null){
                if(!isAppendWhere){
                    buf.append(" where ");
                    isAppendWhere = true;
                }
                concatQuery(buf, scheduleTableSchema.date, scheduleTableSchema.time);
                buf.append(">=?");
            }
            if(to != null){
                if(!isAppendWhere){
                    buf.append(" where ");
                    isAppendWhere = true;
                }else{
                    buf.append(" and ");
                }
                concatQuery(buf, scheduleTableSchema.date, scheduleTableSchema.time);
                buf.append("<=?");
            }
            if(states != null && states.length != 0){
                if(!isAppendWhere){
                    buf.append(" where (");
                    isAppendWhere = true;
                }else{
                    buf.append(" and (");
                }
                for(int i = 0; i < states.length; i++){
                    buf.append(scheduleTableSchema.state);
                    buf.append("=?");
                    if(i != states.length - 1){
                        buf.append(" or ");
                    }
                }
                buf.append(')');
            }
            if(masterId != null){
                if(!isAppendWhere){
                    buf.append(" where ");
                    isAppendWhere = true;
                }else{
                    buf.append(" and ");
                }
                buf.append(scheduleTableSchema.masterId);
                buf.append("=?");
            }
            if(executorTypes != null && executorTypes.length != 0){
                if(!isAppendWhere){
                    buf.append(" where (");
                    isAppendWhere = true;
                }else{
                    buf.append(" and (");
                }
                buf.append(scheduleTableSchema.executorType);
                buf.append(" is null or ");
                for(int i = 0; i < executorTypes.length; i++){
                    buf.append(scheduleTableSchema.executorType);
                    buf.append("=?");
                    if(i != executorTypes.length - 1){
                        buf.append(" or ");
                    }
                }
                buf.append(')');
            }
            if(executorKey != null){
                if(!isAppendWhere){
                    buf.append(" where (");
                    isAppendWhere = true;
                }else{
                    buf.append(" and (");
                }
                buf.append(scheduleTableSchema.executorKey);
                buf.append(" is null or ");
                buf.append(scheduleTableSchema.executorKey);
                buf.append(" =?)");
            }
            if(isLock){
                buf.append(" for update");
            }
            st = con.prepareStatement(buf.toString());
            buf = null;
            final SimpleDateFormat format = new SimpleDateFormat(
                dateFormat + timeFormat
            );
            int index = 0;
            if(from != null){
                st.setString(++index, format.format(from));
            }
            if(to != null){
                st.setString(++index, format.format(to));
            }
            if(states != null && states.length != 0){
                for(int i = 0; i < states.length; i++){
                    st.setString(
                        ++index,
                        scheduleTableSchema.getStateString(
                            states[i]
                        )
                    );
                }
            }
            if(masterId != null){
                st.setString(++index, masterId);
            }
            if(executorTypes != null && executorTypes.length != 0){
                for(int i = 0; i < executorTypes.length; i++){
                    st.setString(++index, executorTypes[i]);
                }
            }
            if(executorKey != null){
                st.setString(++index, executorKey);
            }
            rs = st.executeQuery();
            final List<Schedule> result = new ArrayList<Schedule>();
            while(rs.next()){
                result.add(createSchedule(rs));
            }
            setDependsOnSchedules(con, result);
            Collections.sort(result);
            return result;
        }catch(ParseException e){
            throw new ScheduleManageException(e);
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }catch(IOException e){
            throw new ScheduleManageException(e);
        }catch(ClassNotFoundException e){
            throw new ScheduleManageException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public List<Schedule> findExecutableSchedules(Date date, String[] executorTypes)
     throws ScheduleManageException{
        return findExecutableSchedules(date, executorTypes, null);
    }
     
    // ScheduleManagerのJavaDoc
    public List<Schedule> findExecutableSchedules(Date date, String[] executorTypes, String executorKey)
     throws ScheduleManageException{
        final List<Schedule> result = findSchedules(
            null,
            date,
            new int[]{Schedule.STATE_INITIAL, Schedule.STATE_RETRY},
            null,
            executorTypes,
            executorKey,
            isLockForFindExecutable
        );
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select count(1) from " + scheduleTableSchema.table
                    + " A, (select " + scheduleDependsTableSchema.dependsId
                    + " from " + scheduleDependsTableSchema.table
                    + " where " + scheduleDependsTableSchema.id
                    + "=?) B where A." + scheduleTableSchema.masterId
                    + "=B." + scheduleDependsTableSchema.dependsId
                    + " and A." + scheduleTableSchema.state
                    + "<>'" + scheduleTableSchema.stateString_END +'\''
                    + " and " + concatQuery(new StringBuilder(), "A." + scheduleTableSchema.initialDate, "A." + scheduleTableSchema.initialTime)
                    + "<=?"
            );
            final SimpleDateFormat format = new SimpleDateFormat(
                dateFormat + timeFormat
            );
            final Iterator<Schedule> itr = result.iterator();
            while(itr.hasNext()){
                final Schedule schedule = itr.next();
                st.setString(1, schedule.getId());
                final Date initialTime = schedule.getInitialTime() == null
                    ? schedule.getTime() : schedule.getInitialTime();
                st.setString(2, format.format(initialTime));
                rs = st.executeQuery();
                rs.next();
                if(rs.getInt(1) != 0){
                    itr.remove();
                }
            }
            Collections.sort(result);
            return result;
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public List<Schedule> findDependedSchedules(String id) throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement ps = null;
        Statement st = null;
        ResultSet rs = null;
        try{
            StringBuilder buf = new StringBuilder();
            buf.append("select distinct ");
            buf.append(scheduleDependsTableSchema.id);
            buf.append(" from ");
            buf.append(scheduleDependsTableSchema.table);
            buf.append(" where ");
            buf.append(scheduleDependsTableSchema.dependsId);
            buf.append("=(select ");
            buf.append(scheduleTableSchema.masterId);
            buf.append(" from ");
            buf.append(scheduleTableSchema.table);
            buf.append(" where ");
            buf.append(scheduleTableSchema.id);
            buf.append("=?)");
            ps = con.prepareStatement(buf.toString());
            ps.setString(1, id);
            rs = ps.executeQuery();
            final List<String> idList = new ArrayList<String>();
            while(rs.next()){
                idList.add(rs.getString(1));
            }
            rs.close();
            rs = null;
            ps.close();
            ps = null;
            final List<Schedule> result = new ArrayList<Schedule>();
            if(idList.size() == 0){
                return result;
            }
            st = con.createStatement();
            int to = idList.size() % 1000 == 0 ? idList.size() / 1000 : idList.size() / 1000 + 1; 
            for(int i = 0; i < to; i++){
                buf.setLength(0);
                buf.append("select * from ");
                buf.append(scheduleTableSchema.table);
                buf.append(" where ");
                buf.append(scheduleTableSchema.id);
                buf.append(" in (");
                int startIndex = i * 1000;
                for(int j = startIndex, jmax = Math.min(startIndex + 1000, idList.size()); j < jmax; j++){
                    buf.append('\'').append(idList.get(j)).append('\'');
                    if(j != jmax - 1){
                        buf.append(',');
                    }
                }
                buf.append(')');
                rs = st.executeQuery(buf.toString());
                while(rs.next()){
                    result.add(createSchedule(rs));
                }
                rs.close();
                rs = null;
            }
            st.close();
            st = null;
            setDependsOnSchedules(con, result);
            Collections.sort(result);
            return result;
        }catch(ParseException e){
            throw new ScheduleManageException(e);
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }catch(IOException e){
            throw new ScheduleManageException(e);
        }catch(ClassNotFoundException e){
            throw new ScheduleManageException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(ps != null){
                try{
                    ps.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public void addSchedule(Schedule schedule) throws ScheduleManageException{
        addSchedule(schedule, true);
    }
    
    // DatabaseScheduleManagerServiceMBeanのJavaDoc
    public void addSchedule(
        String masterId,
        Date time,
        String taskName,
        Object input,
        String[] depends,
        String executorKey,
        String executorType,
        long retryInterval,
        Date retryEndTime,
        long maxDelayTime
    ) throws ScheduleManageException{
        addSchedule(
            new DefaultSchedule(
                masterId,
                time,
                taskName,
                input,
                depends,
                executorKey,
                executorType,
                retryInterval,
                retryEndTime,
                maxDelayTime
            )
        );
    }
    
    // ScheduleManagerのJavaDoc
    public boolean reschedule(String id, Date time)
     throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select " + scheduleTableSchema.rowVersion + " from "
                    + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, id);
            rs = st.executeQuery();
            if(!rs.next()){
                throw new ScheduleStateControlException("Schedule not found : " + id);
            }
            final int rowVersion = rs.getInt(1);
            rs.close();
            rs = null;
            st = con.prepareStatement(
                "update " + scheduleTableSchema.table
                    + " set " + scheduleTableSchema.date + "=?, "
                    + scheduleTableSchema.time + "=?,"
                    + scheduleTableSchema.checkState + "='"
                    + scheduleTableSchema.getCheckStateString(Schedule.CHECK_STATE_INITIAL)
                    + "'," + scheduleTableSchema.rowVersion + "='" + (rowVersion + 1) + "',"
                    + scheduleTableSchema.updateUserId + "='" + updateUserId + "',"
                    + scheduleTableSchema.updateTime + "=?"
                    + " where " + scheduleTableSchema.id + "=? and "
                    + scheduleTableSchema.rowVersion + "='" + rowVersion + '\''
            );
            final SimpleDateFormat format = new SimpleDateFormat(dateFormat);
            st.setString(1, format.format(time));
            format.applyPattern(timeFormat);
            st.setString(2, format.format(time));
            st.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
            st.setString(4, id);
            return st.executeUpdate() != 0;
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public boolean removeSchedule(String id) throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        try{
            st = con.prepareStatement(
                "delete from " + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, id);
            return st.executeUpdate() != 0;
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }finally{
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public boolean removeScheduleByMasterId(String masterId)
     throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        try{
            st = con.prepareStatement(
                "delete from " + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.masterId + "=?"
            );
            st.setString(1, masterId);
            return st.executeUpdate() != 0;
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }finally{
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public boolean removeSchedule(Date date) throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        try{
            st = con.prepareStatement(
                "delete from " + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.date + "=?"
            );
            st.setString(1, new SimpleDateFormat(dateFormat).format(date));
            return st.executeUpdate() != 0;
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }finally{
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public boolean removeSchedule(Date from, Date to, int[] states, String masterId) throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        try{
            StringBuilder buf = new StringBuilder();
            buf.append("delete from ");
            buf.append(scheduleTableSchema.table);
            boolean isAppendWhere = false;
            if(from != null){
                if(!isAppendWhere){
                    buf.append(" where ");
                    isAppendWhere = true;
                }
                concatQuery(buf, scheduleTableSchema.date, scheduleTableSchema.time);
                buf.append(">=?");
            }
            if(to != null){
                if(!isAppendWhere){
                    buf.append(" where ");
                    isAppendWhere = true;
                }else{
                    buf.append(" and ");
                }
                concatQuery(buf, scheduleTableSchema.date, scheduleTableSchema.time);
                buf.append("<=?");
            }
            if(states != null && states.length != 0){
                if(!isAppendWhere){
                    buf.append(" where (");
                    isAppendWhere = true;
                }else{
                    buf.append(" and (");
                }
                for(int i = 0; i < states.length; i++){
                    buf.append(scheduleTableSchema.state);
                    buf.append("=?");
                    if(i != states.length - 1){
                        buf.append(" or ");
                    }
                }
                buf.append(')');
            }
            if(masterId != null){
                if(!isAppendWhere){
                    buf.append(" where ");
                    isAppendWhere = true;
                }else{
                    buf.append(" and ");
                }
                buf.append(scheduleTableSchema.masterId);
                buf.append("=?");
            }
            st = con.prepareStatement(buf.toString());
            buf = null;
            final SimpleDateFormat format = new SimpleDateFormat(
                dateFormat + timeFormat
            );
            int index = 0;
            if(from != null){
                st.setString(++index, format.format(from));
            }
            if(to != null){
                st.setString(++index, format.format(to));
            }
            if(states != null && states.length != 0){
                for(int i = 0; i < states.length; i++){
                    st.setString(
                        ++index,
                        scheduleTableSchema.getStateString(
                            states[i]
                        )
                    );
                }
            }
            if(masterId != null){
                st.setString(++index, masterId);
            }
            return st.executeUpdate() != 0;
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }finally{
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public void setExecutorKey(String id, String key)
     throws ScheduleManageException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleManageException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "update " + scheduleTableSchema.table
                    + " set " + scheduleTableSchema.executorKey + "=?,"
                    + scheduleTableSchema.updateUserId + "='" + updateUserId + "',"
                    + scheduleTableSchema.updateTime + "=?"
                    + " where " + scheduleTableSchema.id + "=?"
            );
            if(key == null){
                st.setNull(1, Types.VARCHAR);
            }else{
                st.setString(1, key);
            }
            st.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
            st.setString(3, id);
            st.executeUpdate();
        }catch(SQLException e){
            throw new ScheduleManageException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    
    // ScheduleManagerのJavaDoc
    public int getState(String id) throws ScheduleStateControlException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleStateControlException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select " + scheduleTableSchema.state + " from "
                    + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, id);
            rs = st.executeQuery();
            if(!rs.next()){
                throw new ScheduleStateControlException("Schedule not found : " + id);
            }
            return scheduleTableSchema.getState(rs.getString(1));
        }catch(SQLException e){
            throw new ScheduleStateControlException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public int getControlState(String id) throws ScheduleStateControlException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleStateControlException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select " + scheduleTableSchema.controlState + " from "
                    + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, id);
            rs = st.executeQuery();
            if(!rs.next()){
                throw new ScheduleStateControlException("Schedule not found : " + id);
            }
            return scheduleTableSchema.getControlState(rs.getString(1));
        }catch(SQLException e){
            throw new ScheduleStateControlException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public boolean changeState(String id, int state)
     throws ScheduleStateControlException{
        
        boolean isUpdateExecuteStartTime = false;
        boolean isUpdateExecuteEndTime = false;
        Date executeStartTime = null;
        Date executeEndTime = null;
        switch(state){
        case Schedule.STATE_RUN:
            executeStartTime = new Date();
            isUpdateExecuteStartTime = true;
            break;
        case Schedule.STATE_END:
        case Schedule.STATE_FAILED:
        case Schedule.STATE_ABORT:
            executeEndTime = new Date();
            isUpdateExecuteEndTime = true;
            break;
        case Schedule.STATE_INITIAL:
        case Schedule.STATE_ENTRY:
            isUpdateExecuteStartTime = true;
            isUpdateExecuteEndTime = true;
            break;
        case Schedule.STATE_RETRY:
        case Schedule.STATE_PAUSE:
            break;
        default:
            throw new ScheduleStateControlException("Unknown state : " + state);
        }
        
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleStateControlException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select " + scheduleTableSchema.state + ','
                    + scheduleTableSchema.rowVersion + " from "
                    + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, id);
            rs = st.executeQuery();
            if(!rs.next()){
                throw new ScheduleStateControlException("Schedule not found : " + id);
            }
            String oldStateStr = rs.getString(1);
            final int rowVersion = rs.getInt(2);
            rs.close();
            rs = null;
            final String newStateStr = scheduleTableSchema.getStateString(state);
            if(oldStateStr.equals(newStateStr)){
                return false;
            }
            st = con.prepareStatement(
                "update " + scheduleTableSchema.table
                    + " set " + scheduleTableSchema.state + "=?,"
                    + (isUpdateExecuteStartTime ? scheduleTableSchema.executeStartTime + "=?," : "")
                    + (isUpdateExecuteEndTime ? scheduleTableSchema.executeEndTime + "=?," : "")
                    + scheduleTableSchema.rowVersion + "='" + (rowVersion + 1) + "',"
                    + scheduleTableSchema.updateUserId + "='" + updateUserId + "',"
                    + scheduleTableSchema.updateTime + "=?"
                    + " where " + scheduleTableSchema.id + "=? and "
                    + scheduleTableSchema.rowVersion + "='" + rowVersion + '\''
            );
            int i = 0;
            st.setString(++i, newStateStr);
            final SimpleDateFormat format = new SimpleDateFormat(
                dateFormat + timeFormat
            );
            if(isUpdateExecuteStartTime){
                st.setString(++i, executeStartTime == null ? null : format.format(executeStartTime));
            }
            if(isUpdateExecuteEndTime){
                st.setString(++i, executeEndTime == null ? null : format.format(executeEndTime));
            }
            st.setTimestamp(++i, new Timestamp(System.currentTimeMillis()));
            st.setString(++i, id);
            return st.executeUpdate() != 0;
        }catch(SQLException e){
            throw new ScheduleStateControlException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public boolean changeState(String id, int oldState, int newState)
     throws ScheduleStateControlException{
        
        boolean isUpdateExecuteStartTime = false;
        boolean isUpdateExecuteEndTime = false;
        Date executeStartTime = null;
        Date executeEndTime = null;
        switch(newState){
        case Schedule.STATE_RUN:
            executeStartTime = new Date();
            isUpdateExecuteStartTime = true;
            break;
        case Schedule.STATE_END:
        case Schedule.STATE_FAILED:
        case Schedule.STATE_ABORT:
            executeEndTime = new Date();
            isUpdateExecuteEndTime = true;
            break;
        case Schedule.STATE_INITIAL:
        case Schedule.STATE_ENTRY:
            isUpdateExecuteStartTime = true;
            isUpdateExecuteEndTime = true;
            break;
        case Schedule.STATE_RETRY:
        case Schedule.STATE_PAUSE:
            break;
        default:
            throw new ScheduleStateControlException("Unknown state : " + newState);
        }
        
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleStateControlException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select " + scheduleTableSchema.state + ','
                    + scheduleTableSchema.rowVersion + " from "
                    + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, id);
            rs = st.executeQuery();
            if(!rs.next()){
                throw new ScheduleStateControlException("Schedule not found : " + id);
            }
            String oldStateStr = rs.getString(1);
            final int rowVersion = rs.getInt(2);
            rs.close();
            rs = null;
            final String newStateStr = scheduleTableSchema.getStateString(newState);
            if(oldStateStr.equals(newStateStr)){
                return false;
            }
            st = con.prepareStatement(
                "update " + scheduleTableSchema.table
                    + " set " + scheduleTableSchema.state + "=?,"
                    + (isUpdateExecuteStartTime ? scheduleTableSchema.executeStartTime + "=?," : "")
                    + (isUpdateExecuteEndTime ? scheduleTableSchema.executeEndTime + "=?," : "")
                    + scheduleTableSchema.rowVersion + "='" + (rowVersion + 1) + "',"
                    + scheduleTableSchema.updateUserId + "='" + updateUserId + "',"
                    + scheduleTableSchema.updateTime + "=?"
                    + " where " + scheduleTableSchema.id + "=? and "
                    + scheduleTableSchema.state + "=? and "
                    + scheduleTableSchema.rowVersion + "='" + rowVersion + '\''
            );
            int i = 0;
            st.setString(++i, newStateStr);
            final SimpleDateFormat format = new SimpleDateFormat(
                dateFormat + timeFormat
            );
            if(isUpdateExecuteStartTime){
                st.setString(++i, executeStartTime == null ? null : format.format(executeStartTime));
            }
            if(isUpdateExecuteEndTime){
                st.setString(++i, executeEndTime == null ? null : format.format(executeEndTime));
            }
            st.setTimestamp(++i, new Timestamp(System.currentTimeMillis()));
            st.setString(++i, id);
            st.setString(++i, scheduleTableSchema.getStateString(oldState));
            return st.executeUpdate() != 0;
        }catch(SQLException e){
            throw new ScheduleStateControlException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public boolean changeState(String id, int state, Object output)
     throws ScheduleStateControlException{
        
        boolean isUpdateExecuteStartTime = false;
        boolean isUpdateExecuteEndTime = false;
        Date executeStartTime = null;
        Date executeEndTime = null;
        switch(state){
        case Schedule.STATE_RUN:
            executeStartTime = new Date();
            isUpdateExecuteStartTime = true;
            break;
        case Schedule.STATE_END:
        case Schedule.STATE_FAILED:
        case Schedule.STATE_ABORT:
            executeEndTime = new Date();
            isUpdateExecuteEndTime = true;
            break;
        case Schedule.STATE_INITIAL:
        case Schedule.STATE_ENTRY:
            isUpdateExecuteStartTime = true;
            isUpdateExecuteEndTime = true;
            break;
        case Schedule.STATE_RETRY:
        case Schedule.STATE_PAUSE:
            break;
        default:
            throw new ScheduleStateControlException("Unknown state : " + state);
        }
        
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleStateControlException(e);
        }
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select " + scheduleTableSchema.state + ','
                    + scheduleTableSchema.rowVersion + " from "
                    + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, id);
            rs = st.executeQuery();
            if(!rs.next()){
                throw new ScheduleStateControlException("Schedule not found : " + id);
            }
            final String oldStateStr = rs.getString(1);
            final int rowVersion = rs.getInt(2);
            rs.close();
            rs = null;
            final String newStateStr = scheduleTableSchema.getStateString(state);
            if(oldStateStr.equals(newStateStr)){
                return false;
            }
            st = con.prepareStatement(
                "update " + scheduleTableSchema.table
                    + " set " + scheduleTableSchema.state + "=?,"
                    + scheduleTableSchema.output + "=?,"
                    + (isUpdateExecuteStartTime ? scheduleTableSchema.executeStartTime + "=?," : "")
                    + (isUpdateExecuteEndTime ? scheduleTableSchema.executeEndTime + "=?," : "")
                    + scheduleTableSchema.rowVersion + "='" + (rowVersion + 1) + "',"
                    + scheduleTableSchema.updateUserId + "='" + updateUserId + "',"
                    + scheduleTableSchema.updateTime + "=?"
                    + " where " + scheduleTableSchema.id + "=? and "
                    + scheduleTableSchema.rowVersion + "='" + rowVersion + '\''
            );
            int i = 0;
            st.setString(++i, newStateStr);
            if(output == null){
                st.setNull(++i, Types.VARCHAR);
            }else{
                st.setString(++i, output.toString());
            }
            final SimpleDateFormat format = new SimpleDateFormat(
                dateFormat + timeFormat
            );
            if(isUpdateExecuteStartTime){
                st.setString(++i, executeStartTime == null ? null : format.format(executeStartTime));
            }
            if(isUpdateExecuteEndTime){
                st.setString(++i, executeEndTime == null ? null : format.format(executeEndTime));
            }
            st.setTimestamp(++i, new Timestamp(System.currentTimeMillis()));
            st.setString(++i, id);
            return st.executeUpdate() != 0 && !newStateStr.equals(oldStateStr);
        }catch(SQLException e){
            throw new ScheduleStateControlException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
    }
    
    // ScheduleManagerのJavaDoc
    public boolean changeControlState(String id, int state)
     throws ScheduleStateControlException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleStateControlException(e);
        }
        boolean result = false;
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select " + scheduleTableSchema.controlState + ','
                    + scheduleTableSchema.rowVersion + " from "
                    + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, id);
            rs = st.executeQuery();
            if(!rs.next()){
                throw new ScheduleStateControlException("Schedule not found : " + id);
            }
            final String oldStateStr = rs.getString(1);
            final int nowOldState = scheduleTableSchema.getControlState(oldStateStr);
            final int rowVersion = rs.getInt(2);
            rs.close();
            rs = null;
            
            if(nowOldState == state){
                return false;
            }
            switch(state){
            case Schedule.CONTROL_STATE_PAUSE:
                if(nowOldState != Schedule.STATE_RUN){
                    return false;
                }
                break;
            case Schedule.CONTROL_STATE_RESUME:
                if(nowOldState != Schedule.STATE_PAUSE){
                    return false;
                }
                break;
            case Schedule.CONTROL_STATE_ABORT:
                if(nowOldState != Schedule.STATE_RUN){
                    return false;
                }
                break;
            default:
                throw new ScheduleStateControlException("Unknown state : " + state);
            }
            
            final String newStateStr
                = scheduleTableSchema.getControlStateString(state);
            st = con.prepareStatement(
                "update " + scheduleTableSchema.table
                    + " set " + scheduleTableSchema.controlState + "=?,"
                    + scheduleTableSchema.rowVersion + "='" + (rowVersion + 1) + "',"
                    + scheduleTableSchema.updateUserId + "='" + updateUserId + "',"
                    + scheduleTableSchema.updateTime + "=?"
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, newStateStr);
            st.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
            st.setString(3, id);
            result = st.executeUpdate() != 0;
            if(result){
                try{
                    if(scheduleControlListeners != null
                         && scheduleControlListeners.size() != 0){
                        synchronized(scheduleControlListeners){
                            final Iterator<ScheduleControlListener> itr
                                = scheduleControlListeners.iterator();
                            while(itr.hasNext()){
                                final ScheduleControlListener listener
                                    = itr.next();
                                listener.changedControlState(id, state);
                            }
                        }
                    }
                }catch(ScheduleStateControlException e){
                    st.setString(
                        1,
                        scheduleTableSchema.getControlStateString(
                            Schedule.CONTROL_STATE_FAILED
                        )
                    );
                    st.executeUpdate();
                    throw e;
                }
            }
        }catch(SQLException e){
            throw new ScheduleStateControlException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
        return result;
    }
    
    // ScheduleManagerのJavaDoc
    public boolean changeControlState(String id, int oldState, int newState)
     throws ScheduleStateControlException{
        Connection con = null;
        try{
            con = connectionFactory.getConnection();
        }catch(ConnectionFactoryException e){
            throw new ScheduleStateControlException(e);
        }
        boolean result = false;
        PreparedStatement st = null;
        ResultSet rs = null;
        try{
            st = con.prepareStatement(
                "select " + scheduleTableSchema.controlState + ','
                    + scheduleTableSchema.rowVersion + " from "
                    + scheduleTableSchema.table
                    + " where " + scheduleTableSchema.id + "=?"
            );
            st.setString(1, id);
            rs = st.executeQuery();
            if(!rs.next()){
                throw new ScheduleStateControlException("Schedule not found : " + id);
            }
            final String oldStateStr = rs.getString(1);
            final int nowOldState = scheduleTableSchema.getControlState(oldStateStr);
            final int rowVersion = rs.getInt(2);
            rs.close();
            rs = null;
            
            if(nowOldState == newState){
                return false;
            }
            switch(newState){
            case Schedule.CONTROL_STATE_PAUSE:
                if(nowOldState != Schedule.STATE_RUN){
                    return false;
                }
                break;
            case Schedule.CONTROL_STATE_RESUME:
                if(nowOldState != Schedule.STATE_PAUSE){
                    return false;
                }
                break;
            case Schedule.CONTROL_STATE_ABORT:
                if(nowOldState != Schedule.STATE_RUN){
                    return false;
                }
                break;
            default:
                throw new ScheduleStateControlException("Unknown state : " + newState);
            }
            
            final String newStateStr
                = scheduleTableSchema.getControlStateString(newState);
            st = con.prepareStatement(
                "update " + scheduleTableSchema.table
                    + " set " + scheduleTableSchema.controlState + "=?,"
                    + scheduleTableSchema.rowVersion + "='" + (rowVersion + 1) + "',"
                    + scheduleTableSchema.updateUserId + "='" + updateUserId + "',"
                    + scheduleTableSchema.updateTime + "=?"
                    + " where " + scheduleTableSchema.id + "=? and "
                    + scheduleTableSchema.controlState + "=?"
            );
            st.setString(1, newStateStr);
            st.setTimestamp(2, new Timestamp(System.currentTimeMillis()));
            st.setString(3, id);
            st.setString(4, scheduleTableSchema.getControlStateString(oldState));
            result = st.executeUpdate() != 0;
            if(result){
                try{
                    if(scheduleControlListeners != null
                         && scheduleControlListeners.size() != 0){
                        synchronized(scheduleControlListeners){
                            final Iterator<ScheduleControlListener> itr
                                = scheduleControlListeners.iterator();
                            while(itr.hasNext()){
                                final ScheduleControlListener listener
                                    = itr.next();
                                listener.changedControlState(id, newState);
                            }
                        }
                    }
                }catch(ScheduleStateControlException e){
                    st.setString(
                        1,
                        scheduleTableSchema.getControlStateString(
                            Schedule.CONTROL_STATE_FAILED
                        )
                    );
                    st.executeUpdate();
                    throw e;
                }
            }
        }catch(SQLException e){
            throw new ScheduleStateControlException(e);
        }finally{
            if(rs != null){
                try{
                    rs.close();
                }catch(SQLException e){
                }
            }
            if(st != null){
                try{
                    st.close();
                }catch(SQLException e){
                }
            }
            if(con != null){
                try{
                    con.close();
                }catch(SQLException e){
                }
            }
        }
        return result;
    }
    
    // ScheduleManagerのJavaDoc
    public void addScheduleControlListener(ScheduleControlListener listener){
        if((scheduleControlListeners == null
              || scheduleControlListeners.size() == 0)
             && controlStateChecker == null
             && getState() == State.STARTED
             && controlStateCheckInterval > 0
        ){
            controlStateChecker = new Daemon(new ControlStateChecker());
            controlStateChecker.setName("Nimbus ControlStateChecker " + getServiceNameObject());
            controlStateChecker.start();
        }
        scheduleControlListeners.add(listener);
    }
    
    // ScheduleManagerのJavaDoc
    public void removeScheduleControlListener(ScheduleControlListener listener){
        scheduleControlListeners.remove(listener);
    }
    
    private static final Object getInOutObject(int type, String name, ResultSet rs) throws IOException, ClassNotFoundException, SQLException{
        int length = 0;
        switch(type){
        case Types.CLOB:
            Reader reader = rs.getCharacterStream(name);
            StringWriter writer = new StringWriter();
            char[] chars = new char[1024];
            while((length = reader.read(chars)) > 0){
                writer.write(chars, 0, length);
            }
            return writer.toString();
        case Types.BLOB:
            InputStream is = rs.getBinaryStream(name);
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            byte[] bytes = new byte[1024];
            while((length = is.read(bytes)) > 0){
                baos.write(bytes, 0, length);
            }
            ObjectInputStream ois = new ObjectInputStream(
                new ByteArrayInputStream(baos.toByteArray())
            );
            return ois.readObject();
        default:
            return rs.getObject(name);
        }
    }
    
    private static final void setInOutObject(int type, int index, PreparedStatement ps, Object value) throws IOException, SQLException{
        if(value == null){
            ps.setNull(index, type);
            return;
        }
        switch(type){
        case Types.CLOB:
            char[] chars = value.toString().toCharArray();
            ps.setCharacterStream(index, new CharArrayReader(chars), chars.length);
            break;
        case Types.BLOB:
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            ObjectOutputStream oos = new ObjectOutputStream(baos);
            oos.writeObject(value);
            oos.flush();
            byte[] bytes = baos.toByteArray();
            ps.setBinaryStream(index, new ByteArrayInputStream(bytes), bytes.length);
            break;
        default:
            ps.setObject(index, value);
        }
    }
    
    /**
     * スケジュールマスタテーブルスキーマ情報。<p>
     * スケジュールマスタを登録するテーブルのスキーマ情報を定義するクラス。<br>
     *
     * @author M.Takata
     */
    public static class ScheduleMasterTableSchema{
        
        /**
         * デフォルトのテーブル名。<p>
         */
        public static final String DEFAULT_TABLE           = "SCHEDULE_MST";
        
        /**
         * デフォルトの「スケジュールマスタID」のカラム名。<p>
         */
        public static final String DEFAULT_ID              = "ID";
        
        /**
         * デフォルトの「タスク名」のカラム名。<p>
         */
        public static final String DEFAULT_TASK_NAME       = "TASK_NAME";
        
        /**
         * デフォルトの「スケジュール種別」のカラム名。<p>
         */
        public static final String DEFAULT_SCHEDULE_TYPE   = "SCHEDULE_TYPE";
        
        /**
         * デフォルトの「入力」のカラム名。<p>
         */
        public static final String DEFAULT_INPUT           = "INPUT";
        
        /**
         * デフォルトの「開始時刻」のカラム名。<p>
         */
        public static final String DEFAULT_START_TIME      = "START_TIME";
        
        /**
         * デフォルトの「終了時刻」のカラム名。<p>
         */
        public static final String DEFAULT_END_TIME        = "END_TIME";
        
        /**
         * デフォルトの「繰り返し間隔」のカラム名。<p>
         */
        public static final String DEFAULT_REPEAT_INTERVAL = "REPEAT_INTERVAL";
        
        /**
         * デフォルトの「リトライ間隔」のカラム名。<p>
         */
        public static final String DEFAULT_RETRY_INTERVAL  = "RETRY_INTERVAL";
        
        /**
         * デフォルトの「リトライ終了時刻」のカラム名。<p>
         */
        public static final String DEFAULT_RETRY_END_TIME  = "RETRY_END_TIME";
        
        /**
         * デフォルトの「最大遅延時間」のカラム名。<p>
         */
        public static final String DEFAULT_MAX_DELAY_TIME  = "MAX_DELAY_TIME";
        
        /**
         * デフォルトの「有効フラグ」のカラム名。<p>
         */
        public static final String DEFAULT_ENABLE          = "ENABLE";
        
        /**
         * デフォルトの「実行キー」のカラム名。<p>
         */
        public static final String DEFAULT_EXECUTOR_KEY   = "EXECUTOR_KEY";
        
        /**
         * デフォルトの「実行種類」のカラム名。<p>
         */
        public static final String DEFAULT_EXECUTOR_TYPE   = "EXECUTOR_TYPE";
        
        /**
         * テーブル名。<p>
         */
        public String table = DEFAULT_TABLE;
        
        /**
         * 「スケジュールマスタID」のカラム名。<p>
         */
        public String id = DEFAULT_ID;
        
        /**
         * 「タスク名」のカラム名。<p>
         */
        public String taskName = DEFAULT_TASK_NAME;
        
        /**
         * 「スケジュール種別」のカラム名。<p>
         */
        public String scheduleType = DEFAULT_SCHEDULE_TYPE;
        
        /**
         * 「入力」のカラム名。<p>
         */
        public String input = DEFAULT_INPUT;
        
        /**
         * 「開始時刻」のカラム名。<p>
         */
        public String startTime = DEFAULT_START_TIME;
        
        /**
         * 「終了時刻」のカラム名。<p>
         */
        public String endTime = DEFAULT_END_TIME;
        
        /**
         * 「繰り返し間隔」のカラム名。<p>
         */
        public String repeatInterval = DEFAULT_REPEAT_INTERVAL;
        
        /**
         * 「リトライ間隔」のカラム名。<p>
         */
        public String retryInterval = DEFAULT_RETRY_INTERVAL;
        
        /**
         * 「リトライ終了時刻」のカラム名。<p>
         */
        public String retryEndTime = DEFAULT_RETRY_END_TIME;
        
        /**
         * 「最大遅延時間」のカラム名。<p>
         */
        public String maxDelayTime = DEFAULT_MAX_DELAY_TIME;
        
        /**
         * 「有効フラグ」のカラム名。<p>
         */
        public String enable = DEFAULT_ENABLE;
        
        /**
         * 「実行キー」のカラム名。<p>
         */
        public String executorKey = DEFAULT_EXECUTOR_KEY;
        
        /**
         * 「実行種類」のカラム名。<p>
         */
        public String executorType = DEFAULT_EXECUTOR_TYPE;
        
        /**
         * 「入力」のカラムの型。<p>
         * デフォルトは、java.sq.Types.VARCHAR。<br>
         */
        public int inputColumnType = Types.VARCHAR;
        
        /**
         * 検索結果から「入力」のカラムの値を取得する。<p>
         * 
         * @param rs 検索結果
         * @return 「入力」のカラムの値
         * @exception SQLException SQL例外
         * @exception IOException 入力オブジェクトの読み込みに失敗した場合
         * @exception ClassNotFoundException 入力オブジェクトの読み込みに失敗した場合
         */
        protected Object getInputObject(ResultSet rs) throws IOException, ClassNotFoundException, SQLException{
            return DatabaseScheduleManagerService.getInOutObject(inputColumnType, input, rs);
        }
    }
    
    /**
     * スケジュール依存関係マスタテーブルスキーマ情報。<p>
     * スケジュールマスタの依存関係を登録するテーブルのスキーマ情報を定義するクラス。<br>
     *
     * @author M.Takata
     */
    public static class ScheduleDependsMasterTableSchema{
        
        /**
         * デフォルトのテーブル名。<p>
         */
        public static final String DEFAULT_TABLE      = "SCHEDULE_DEPENDS_MST";
        
        /**
         * デフォルトの「依存されるスケジュールマスタID」のカラム名。<p>
         */
        public static final String DEFAULT_ID         = "ID";
        
        /**
         * デフォルトの「依存するスケジュールマスタID」のカラム名。<p>
         */
        public static final String DEFAULT_DEPENDS_ID = "DEPENDS_ID";
        
        /**
         * テーブル名。<p>
         */
        public String table = DEFAULT_TABLE;
        
        /**
         * 「依存されるスケジュールマスタID」のカラム名。<p>
         */
        public String id = DEFAULT_ID;
        
        /**
         * 「依存するスケジュールマスタID」のカラム名。<p>
         */
        public String dependsId = DEFAULT_DEPENDS_ID;
    }
    
    /**
     * スケジュールテーブルスキーマ情報。<p>
     * スケジュールを登録するテーブルのスキーマ情報を定義するクラス。<br>
     *
     * @author M.Takata
     */
    public static class ScheduleTableSchema{
        
        /**
         * デフォルトのテーブル名。<p>
         */
        public static final String DEFAULT_TABLE      = "SCHEDULE";
        
        /**
         * デフォルトの「スケジュールID」のカラム名。<p>
         */
        public static final String DEFAULT_ID             = "ID";
        
        /**
         * デフォルトの「スケジュールマスタID」のカラム名。<p>
         */
        public static final String DEFAULT_MASTER_ID      = "MASTER_ID";
        
        /**
         * デフォルトの「スケジュール日付」のカラム名。<p>
         */
        public static final String DEFAULT_DATE           = "S_DATE";
        
        /**
         * デフォルトの「開始時刻」のカラム名。<p>
         */
        public static final String DEFAULT_TIME           = "S_TIME";
        
        /**
         * デフォルトの「タスク名」のカラム名。<p>
         */
        public static final String DEFAULT_TASK_NAME      = "TASK_NAME";
        
        /**
         * デフォルトの「入力」のカラム名。<p>
         */
        public static final String DEFAULT_INPUT          = "INPUT";
        
        /**
         * デフォルトの「出力」のカラム名。<p>
         */
        public static final String DEFAULT_OUTPUT         = "OUTPUT";
        
        /**
         * デフォルトの「初期スケジュール日付」のカラム名。<p>
         */
        public static final String DEFAULT_INITIAL_DATE   = "INITIAL_DATE";
        
        /**
         * デフォルトの「初期開始時刻」のカラム名。<p>
         */
        public static final String DEFAULT_INITIAL_TIME   = "INITIAL_TIME";
        
        /**
         * デフォルトの「リトライ間隔」のカラム名。<p>
         */
        public static final String DEFAULT_RETRY_INTERVAL = "RETRY_INTERVAL";
        
        /**
         * デフォルトの「リトライ終了時刻」のカラム名。<p>
         */
        public static final String DEFAULT_RETRY_END_TIME = "RETRY_END_TIME";
        
        /**
         * デフォルトの「最大遅延時間」のカラム名。<p>
         */
        public static final String DEFAULT_MAX_DELAY_TIME  = "MAX_DELAY_TIME";
        
        /**
         * デフォルトの「状態」のカラム名。<p>
         */
        public static final String DEFAULT_STATE          = "STATE";
        
        /**
         * デフォルトの「制御状態」のカラム名。<p>
         */
        public static final String DEFAULT_CONTROL_STATE  = "CONTROL_STATE";
        
        /**
         * デフォルトの「チェック状態」のカラム名。<p>
         */
        public static final String DEFAULT_CHECK_STATE  = "CHECK_STATE";
        
        /**
         * デフォルトの「実行キー」のカラム名。<p>
         */
        public static final String DEFAULT_EXECUTOR_KEY   = "EXECUTOR_KEY";
        
        /**
         * デフォルトの「実行種類」のカラム名。<p>
         */
        public static final String DEFAULT_EXECUTOR_TYPE   = "EXECUTOR_TYPE";
        
        /**
         * デフォルトの「実行開始日時」のカラム名。<p>
         */
        public static final String DEFAULT_EXECUTE_START_TIME     = "EXEC_S_TIME";
        
        /**
         * デフォルトの「実行終了日時」のカラム名。<p>
         */
        public static final String DEFAULT_EXECUTE_END_TIME     = "EXEC_E_TIME";
        
        /**
         * デフォルトの「レコード更新バージョン番号」のカラム名。<p>
         */
        public static final String DEFAULT_ROWVERSION     = "ROWVERSION";
        
        /**
         * デフォルトの「レコード更新ユーザID」のカラム名。<p>
         */
        public static final String DEFAULT_UPDATEUSERID   = "UPDATEUSERID";
        
        /**
         * デフォルトの「レコード更新日時」のカラム名。<p>
         */
        public static final String DEFAULT_UPDATETIME     = "UPDATETIME";
        
        
        /**
         * デフォルトのカラム「状態」の値：初期。<p>
         */
        public static final String DEFAULT_STATE_STRING_INITIAL = "I";
        
        /**
         * デフォルトのカラム「状態」の値：投入。<p>
         */
        public static final String DEFAULT_STATE_STRING_ENTRY   = "E";
        
        /**
         * デフォルトのカラム「状態」の値：実行中。<p>
         */
        public static final String DEFAULT_STATE_STRING_RUN     = "R";
        
        /**
         * デフォルトのカラム「状態」の値：正常終了。<p>
         */
        public static final String DEFAULT_STATE_STRING_END     = "N";
         
        /**
         * デフォルトのカラム「状態」の値：異常終了。<p>
         */
        public static final String DEFAULT_STATE_STRING_FAILED  = "F";
        
        /**
         * デフォルトのカラム「状態」の値：中断中。<p>
         */
        public static final String DEFAULT_STATE_STRING_PAUSE   = "P";
        
        /**
         * デフォルトのカラム「状態」の値：強制終了。<p>
         */
        public static final String DEFAULT_STATE_STRING_ABORT   = "A";
        
        /**
         * デフォルトのカラム「状態」の値：リトライ中。<p>
         */
        public static final String DEFAULT_STATE_STRING_RETRY   = "T";
        
        /**
         * デフォルトのカラム「状態」の値：不明。<p>
         */
        public static final String DEFAULT_STATE_STRING_UNKNOWN = "U";
        
        
        /**
         * デフォルトのカラム「制御状態」の値：初期。<p>
         */
        public static final String DEFAULT_CONTROL_STATE_STRING_INITIAL = "I";
        
        /**
         * デフォルトのカラム「制御状態」の値：中断。<p>
         */
        public static final String DEFAULT_CONTROL_STATE_STRING_PAUSE   = "P";
        
        /**
         * デフォルトのカラム「制御状態」の値：再開。<p>
         */
        public static final String DEFAULT_CONTROL_STATE_STRING_RESUME  = "R";
        
        /**
         * デフォルトのカラム「制御状態」の値：強制終了。<p>
         */
        public static final String DEFAULT_CONTROL_STATE_STRING_ABORT   = "A";
        
        /**
         * デフォルトのカラム「制御状態」の値：制御失敗。<p>
         */
        public static final String DEFAULT_CONTROL_STATE_STRING_FAILED  = "F";
        
        /**
         * デフォルトのカラム「制御状態」の値：不明。<p>
         */
        public static final String DEFAULT_CONTROL_STATE_STRING_UNKNOWN = "U";
        
        
        /**
         * デフォルトのカラム「チェック状態」の値：初期。<p>
         */
        public static final String DEFAULT_CHECK_STATE_STRING_INITIAL = "I";
        
        /**
         * デフォルトのカラム「チェック状態」の値：タイムオーバー。<p>
         */
        public static final String DEFAULT_CHECK_STATE_STRING_TIMEOVER   = "O";
        
        /**
         * デフォルトのカラム「チェック状態」の値：不明。<p>
         */
        public static final String DEFAULT_CHECK_STATE_STRING_UNKNOWN = "U";
        
        
        /**
         * テーブル名。<p>
         */
        public String table = DEFAULT_TABLE;
        
        
        /**
         * 「スケジュールID」のカラム名。<p>
         */
        public String id = DEFAULT_ID;
        
        /**
         * 「スケジュールマスタID」のカラム名。<p>
         */
        public String masterId = DEFAULT_MASTER_ID;
        
        /**
         * 「スケジュール日付」のカラム名。<p>
         */
        public String date = DEFAULT_DATE;
        
        /**
         * 「開始時刻」のカラム名。<p>
         */
        public String time = DEFAULT_TIME;
        
        /**
         * 「タスク名」のカラム名。<p>
         */
        public String taskName = DEFAULT_TASK_NAME;
        
        /**
         * 「入力」のカラム名。<p>
         */
        public String input = DEFAULT_INPUT;
        
        /**
         * 「出力」のカラム名。<p>
         */
        public String output = DEFAULT_OUTPUT;
        
        /**
         * 「初期スケジュール日付」のカラム名。<p>
         */
        public String initialDate = DEFAULT_INITIAL_DATE;
        
        /**
         * 「初期開始時刻」のカラム名。<p>
         */
        public String initialTime = DEFAULT_INITIAL_TIME;
        
        /**
         * 「リトライ間隔」のカラム名。<p>
         */
        public String retryInterval = DEFAULT_RETRY_INTERVAL;
        
        /**
         * 「リトライ終了時刻」のカラム名。<p>
         */
        public String retryEndTime = DEFAULT_RETRY_END_TIME;
        
        /**
         * 「最大遅延時間」のカラム名。<p>
         */
        public String maxDelayTime = DEFAULT_MAX_DELAY_TIME;
        
        /**
         * 「状態」のカラム名。<p>
         */
        public String state = DEFAULT_STATE;
        
        /**
         * 「制御状態」のカラム名。<p>
         */
        public String controlState = DEFAULT_CONTROL_STATE;
        
        /**
         * 「チェック状態」のカラム名。<p>
         */
        public String checkState = DEFAULT_CHECK_STATE;
        
        /**
         * 「実行キー」のカラム名。<p>
         */
        public String executorKey = DEFAULT_EXECUTOR_KEY;
        
        /**
         * 「実行種類」のカラム名。<p>
         */
        public String executorType = DEFAULT_EXECUTOR_TYPE;
        
        /**
         * 「実行開始時刻」のカラム名。<p>
         */
        public String executeStartTime = DEFAULT_EXECUTE_START_TIME;
        
        /**
         * 「実行終了時刻」のカラム名。<p>
         */
        public String executeEndTime = DEFAULT_EXECUTE_END_TIME;
        
        /**
         * 「レコード更新バージョン番号」のカラム名。<p>
         */
        public String rowVersion = DEFAULT_ROWVERSION;
        
        /**
         * 「レコード更新ユーザID」のカラム名。<p>
         */
        public String updateUserId = DEFAULT_UPDATEUSERID;
        
        /**
         * 「レコード更新日時」のカラム名。<p>
         */
        public String updateTime = DEFAULT_UPDATETIME;
        
        
        /**
         * カラム「状態」の値：初期。<p>
         */
        public String stateString_INITIAL = DEFAULT_STATE_STRING_INITIAL;
        
        /**
         * カラム「状態」の値：投入。<p>
         */
        public String stateString_ENTRY = DEFAULT_STATE_STRING_ENTRY;
        
        /**
         * カラム「状態」の値：実行中。<p>
         */
        public String stateString_RUN = DEFAULT_STATE_STRING_RUN;
        
        /**
         * カラム「状態」の値：正常終了。<p>
         */
        public String stateString_END = DEFAULT_STATE_STRING_END;
        
        /**
         * カラム「状態」の値：異常終了。<p>
         */
        public String stateString_FAILED = DEFAULT_STATE_STRING_FAILED;
        
        /**
         * カラム「状態」の値：中断中。<p>
         */
        public String stateString_PAUSE = DEFAULT_STATE_STRING_PAUSE;
        
        /**
         * カラム「状態」の値：強制終了。<p>
         */
        public String stateString_ABORT = DEFAULT_STATE_STRING_ABORT;
        
        /**
         * カラム「状態」の値：リトライ中。<p>
         */
        public String stateString_RETRY = DEFAULT_STATE_STRING_RETRY;
        
        /**
         * カラム「状態」の値：不明。<p>
         */
        public String stateString_UNKNOWN = DEFAULT_STATE_STRING_UNKNOWN;
        
        
        /**
         * カラム「制御状態」の値：初期。<p>
         */
        public String controlStateString_INITIAL = DEFAULT_CONTROL_STATE_STRING_INITIAL;
        
        /**
         * カラム「制御状態」の値：中断。<p>
         */
        public String controlStateString_PAUSE = DEFAULT_CONTROL_STATE_STRING_PAUSE;
        
        /**
         * カラム「制御状態」の値：再開。<p>
         */
        public String controlStateString_RESUME = DEFAULT_CONTROL_STATE_STRING_RESUME;
        
        /**
         * カラム「制御状態」の値：強制終了。<p>
         */
        public String controlStateString_ABORT = DEFAULT_CONTROL_STATE_STRING_ABORT;
        
        /**
         * カラム「制御状態」の値：制御失敗。<p>
         */
        public String controlStateString_FAILED = DEFAULT_CONTROL_STATE_STRING_FAILED;
        
        /**
         * カラム「制御状態」の値：不明。<p>
         */
        public String controlStateString_UNKNOWN = DEFAULT_CONTROL_STATE_STRING_UNKNOWN;
        
        
        /**
         * カラム「チェック状態」の値：初期。<p>
         */
        public String checkStateString_INITIAL = DEFAULT_CHECK_STATE_STRING_INITIAL;
        
        /**
         * カラム「チェック状態」の値：タイムオーバー。<p>
         */
        public String checkStateString_TIMEOVER = DEFAULT_CHECK_STATE_STRING_TIMEOVER;
        
        /**
         * カラム「チェック状態」の値：不明。<p>
         */
        public String checkStateString_UNKNOWN = DEFAULT_CHECK_STATE_STRING_UNKNOWN;
        
        /**
         * 「入力」のカラムの型。<p>
         * デフォルトは、java.sq.Types.VARCHAR。<br>
         */
        public int inputColumnType = Types.VARCHAR;
        
        /**
         * 「出力」のカラムの型。<p>
         * デフォルトは、java.sq.Types.VARCHAR。<br>
         */
        public int outputColumnType = Types.VARCHAR;
        
        /**
         * 検索結果から「入力」のカラムの値を取得する。<p>
         * 
         * @param rs 検索結果
         * @return 「入力」のカラムの値
         * @exception SQLException SQL例外
         * @exception IOException 入力オブジェクトの読み込みに失敗した場合
         * @exception ClassNotFoundException 入力オブジェクトの読み込みに失敗した場合
         */
        protected Object getInputObject(ResultSet rs) throws IOException, ClassNotFoundException, SQLException{
            return DatabaseScheduleManagerService.getInOutObject(inputColumnType, input, rs);
        }
        
        /**
         * 指定されたPreparedStatementに「入力」のカラムの値を設定する。<p>
         * 
         * @param index インデックス
         * @param ps PreparedStatement
         * @param value 「入力」のカラムの値
         * @exception SQLException SQL例外
         * @exception IOException 入力オブジェクトの書込みに失敗した場合
         */
        protected void setInputObject(int index, PreparedStatement ps, Object value) throws IOException, SQLException{
            DatabaseScheduleManagerService.setInOutObject(inputColumnType, index, ps, value);
        }
        
        /**
         * 検索結果から「出力」のカラムの値を取得する。<p>
         * 
         * @param rs 検索結果
         * @return 「出力」のカラムの値
         * @exception SQLException SQL例外
         * @exception IOException 出力オブジェクトの読み込みに失敗した場合
         * @exception ClassNotFoundException 出力オブジェクトの読み込みに失敗した場合
         */
        protected Object getOutputObject(ResultSet rs) throws IOException, ClassNotFoundException, SQLException{
            return DatabaseScheduleManagerService.getInOutObject(outputColumnType, output, rs);
        }
        
        /**
         * 指定されたPreparedStatementに「出力」のカラムの値を設定する。<p>
         * 
         * @param index インデックス
         * @param ps PreparedStatement
         * @param value 「出力」のカラムの値
         * @exception SQLException SQL例外
         * @exception IOException 出力オブジェクトの書込みに失敗した場合
         */
        protected void setOutputObject(int index, PreparedStatement ps, Object value) throws IOException, SQLException{
            DatabaseScheduleManagerService.setInOutObject(outputColumnType, index, ps, value);
        }
        
        /**
         * 指定された状態値に該当する状態文字列を取得する。<p>
         *
         * @param state 状態値
         * @return 状態文字列
         */
        public String getStateString(int state){
            switch(state){
            case Schedule.STATE_INITIAL:
                return stateString_INITIAL;
            case Schedule.STATE_ENTRY:
                return stateString_ENTRY;
            case Schedule.STATE_RUN:
                return stateString_RUN;
            case Schedule.STATE_END:
                return stateString_END;
            case Schedule.STATE_FAILED:
                return stateString_FAILED;
            case Schedule.STATE_PAUSE:
                return stateString_PAUSE;
            case Schedule.STATE_ABORT:
                return stateString_ABORT;
            case Schedule.STATE_RETRY:
                return stateString_RETRY;
            default:
                return stateString_UNKNOWN;
            }
        }
        
        /**
         * 指定された状態文字列に該当する状態値を取得する。<p>
         *
         * @param state 状態文字列
         * @return 状態値
         */
        public int getState(String state){
            if(stateString_INITIAL.equals(state)){
                return Schedule.STATE_INITIAL;
            }
            if(stateString_ENTRY.equals(state)){
                return Schedule.STATE_ENTRY;
            }
            if(stateString_RUN.equals(state)){
                return Schedule.STATE_RUN;
            }
            if(stateString_END.equals(state)){
                return Schedule.STATE_END;
            }
            if(stateString_FAILED.equals(state)){
                return Schedule.STATE_FAILED;
            }
            if(stateString_PAUSE.equals(state)){
                return Schedule.STATE_PAUSE;
            }
            if(stateString_ABORT.equals(state)){
                return Schedule.STATE_ABORT;
            }
            if(stateString_RETRY.equals(state)){
                return Schedule.STATE_RETRY;
            }else{
                return Schedule.STATE_UNKNOWN;
            }
        }
        
        /**
         * 指定された制御状態値に該当する制御状態文字列を取得する。<p>
         *
         * @param state 制御状態値
         * @return 制御状態文字列
         */
        public String getControlStateString(int state){
            switch(state){
            case Schedule.CONTROL_STATE_INITIAL:
                return controlStateString_INITIAL;
            case Schedule.CONTROL_STATE_PAUSE:
                return controlStateString_PAUSE;
            case Schedule.CONTROL_STATE_RESUME:
                return controlStateString_RESUME;
            case Schedule.CONTROL_STATE_ABORT:
                return controlStateString_ABORT;
            case Schedule.CONTROL_STATE_FAILED:
                return controlStateString_FAILED;
            default:
                return controlStateString_UNKNOWN;
            }
        }
        
        /**
         * 指定された制御状態文字列に該当する制御状態値を取得する。<p>
         *
         * @param state 制御状態文字列
         * @return 制御状態値
         */
        public int getControlState(String state){
            if(controlStateString_INITIAL.equals(state)){
                return Schedule.CONTROL_STATE_INITIAL;
            }
            if(controlStateString_PAUSE.equals(state)){
                return Schedule.CONTROL_STATE_PAUSE;
            }
            if(controlStateString_ABORT.equals(state)){
                return Schedule.CONTROL_STATE_ABORT;
            }
            if(controlStateString_FAILED.equals(state)){
                return Schedule.CONTROL_STATE_FAILED;
            }
            if(controlStateString_RESUME.equals(state)){
                return Schedule.CONTROL_STATE_RESUME;
            }else{
                return Schedule.CONTROL_STATE_UNKNOWN;
            }
        }
        
        /**
         * 指定されたチェック状態値に該当するチェック状態文字列を取得する。<p>
         *
         * @param state チェック状態値
         * @return チェック状態文字列
         */
        public String getCheckStateString(int state){
            switch(state){
            case Schedule.CHECK_STATE_INITIAL:
                return checkStateString_INITIAL;
            case Schedule.CHECK_STATE_TIMEOVER:
                return checkStateString_TIMEOVER;
            default:
                return checkStateString_UNKNOWN;
            }
        }
        
        /**
         * 指定されたチェック状態文字列に該当するチェック状態値を取得する。<p>
         *
         * @param state チェック状態文字列
         * @return チェック状態値
         */
        public int getCheckState(String state){
            if(checkStateString_INITIAL.equals(state)){
                return Schedule.CHECK_STATE_INITIAL;
            }
            if(checkStateString_TIMEOVER.equals(state)){
                return Schedule.CHECK_STATE_TIMEOVER;
            }else{
                return Schedule.CHECK_STATE_UNKNOWN;
            }
        }
    }
    
    /**
     * スケジュール依存関係テーブルスキーマ情報。<p>
     * スケジュールの依存関係を登録するテーブルのスキーマ情報を定義するクラス。<br>
     *
     * @author M.Takata
     */
    public static class ScheduleDependsTableSchema
     extends ScheduleDependsMasterTableSchema{
        
        /**
         * デフォルトのテーブル名。<p>
         */
        public static final String DEFAULT_TABLE = "SCHEDULE_DEPENDS";
        
        /**
         * デフォルトの「レコード更新バージョン番号」のカラム名。<p>
         */
        public static final String DEFAULT_ROWVERSION     = "ROWVERSION";
        
        /**
         * デフォルトの「レコード更新ユーザID」のカラム名。<p>
         */
        public static final String DEFAULT_UPDATEUSERID   = "UPDATEUSERID";
        
        /**
         * デフォルトの「レコード更新日時」のカラム名。<p>
         */
        public static final String DEFAULT_UPDATETIME     = "UPDATETIME";
        
        
        /**
         * テーブル名。<p>
         */
        public String table = DEFAULT_TABLE;
        
        
        /**
         * 「レコード更新バージョン番号」のカラム名。<p>
         */
        public String rowVersion = DEFAULT_ROWVERSION;
        
        /**
         * 「レコード更新ユーザID」のカラム名。<p>
         */
        public String updateUserId = DEFAULT_UPDATEUSERID;
        
        /**
         * 「レコード更新日時」のカラム名。<p>
         */
        public String updateTime = DEFAULT_UPDATETIME;
    }
    
    /**
     * 制御状態監視。<p>
     *
     * @author M.Takata
     */
    protected class ControlStateChecker implements DaemonRunnable<Object>{
        
        /**
         * デーモンが開始した時に呼び出される。<p>
         * 
         * @return 常にtrueを返す
         */
        public boolean onStart() {
            return true;
        }
        
        /**
         * デーモンが停止した時に呼び出される。<p>
         * 
         * @return 常にtrueを返す
         */
        public boolean onStop() {
            return true;
        }
        
        /**
         * デーモンが中断した時に呼び出される。<p>
         * 
         * @return 常にtrueを返す
         */
        public boolean onSuspend() {
            return true;
        }
        
        /**
         * デーモンが再開した時に呼び出される。<p>
         * 
         * @return 常にtrueを返す
         */
        public boolean onResume() {
            return true;
        }
        
        /**
         * 一定時間空ける。<p>
         * 
         * @param ctrl DaemonControlオブジェクト
         * @return スケジュールの配列
         */
        public Object provide(DaemonControl ctrl) throws Throwable{
            Thread.sleep(getControlStateCheckInterval());
            return null;
        }
        
        /**
         * 制御状態のチェックをする。<p>
         *
         * @param input null
         * @param ctrl DaemonControlオブジェクト
         */
        public void consume(Object input, DaemonControl ctrl)
         throws Throwable{
            if(scheduleControlListeners == null
                || scheduleControlListeners.size() == 0){
                return;
            }
            Connection con = null;
            try{
                con = connectionFactory.getConnection();
            }catch(ConnectionFactoryException e){
                getLogger().write(MSG_ID_CONTROL_STATE_CHECK_ERROR, e);
                return;
            }
            Statement st = null;
            ResultSet rs = null;
            try{
                st = con.createStatement();
                rs = st.executeQuery(
                    "select " + scheduleTableSchema.id + ','
                        + scheduleTableSchema.controlState
                        + " from " + scheduleTableSchema.table
                        + " where ("
                        + scheduleTableSchema.state + "='"
                        + scheduleTableSchema.getStateString(Schedule.STATE_RUN)
                        + "' or " + scheduleTableSchema.state + "='"
                        + scheduleTableSchema.getStateString(Schedule.STATE_PAUSE)
                        + "') and (" + scheduleTableSchema.controlState + "='"
                        + scheduleTableSchema.getControlStateString(Schedule.CONTROL_STATE_PAUSE)
                        + "' or " + scheduleTableSchema.controlState + "='"
                        + scheduleTableSchema.getControlStateString(Schedule.CONTROL_STATE_RESUME)
                        + "' or " + scheduleTableSchema.controlState + "='"
                        + scheduleTableSchema.getControlStateString(Schedule.CONTROL_STATE_ABORT)
                        + "')"
                );
                while(rs.next()){
                    final String id = rs.getString(1);
                    final String controlStateStr = rs.getString(2);
                    final int controlState = scheduleTableSchema
                        .getControlState(controlStateStr);
                    try{
                        if(scheduleControlListeners != null
                             && scheduleControlListeners.size() != 0){
                            synchronized(scheduleControlListeners){
                                final Iterator<ScheduleControlListener> itr
                                    = scheduleControlListeners.iterator();
                                while(itr.hasNext()){
                                    final ScheduleControlListener listener
                                        = itr.next();
                                    listener.changedControlState(id, controlState);
                                }
                            }
                        }
                    }catch(ScheduleStateControlException e){
                        try{
                            changeControlState(id, Schedule.CONTROL_STATE_FAILED);
                        }catch(ScheduleStateControlException e2){
                        }
                        getLogger().write(MSG_ID_CONTROL_STATE_CHECK_ERROR, e);
                    }
                }
            }catch(SQLException e){
                getLogger().write(MSG_ID_CONTROL_STATE_CHECK_ERROR, e);
                return;
            }finally{
                if(rs != null){
                    try{
                        rs.close();
                    }catch(SQLException e){
                    }
                }
                if(st != null){
                    try{
                        st.close();
                    }catch(SQLException e){
                    }
                }
                if(con != null){
                    try{
                        con.close();
                    }catch(SQLException e){
                    }
                }
            }
        }
        
        /**
         * 何もしない。<p>
         */
        public void garbage(){
        }
    }
    
    /**
     * タイムオーバー監視。<p>
     *
     * @author M.Takata
     */
    protected class TimeoverChecker implements DaemonRunnable<Object>{
        
        /**
         * デーモンが開始した時に呼び出される。<p>
         * 
         * @return 常にtrueを返す
         */
        public boolean onStart() {
            return true;
        }
        
        /**
         * デーモンが停止した時に呼び出される。<p>
         * 
         * @return 常にtrueを返す
         */
        public boolean onStop() {
            return true;
        }
        
        /**
         * デーモンが中断した時に呼び出される。<p>
         * 
         * @return 常にtrueを返す
         */
        public boolean onSuspend() {
            return true;
        }
        
        /**
         * デーモンが再開した時に呼び出される。<p>
         * 
         * @return 常にtrueを返す
         */
        public boolean onResume() {
            return true;
        }
        
        /**
         * 一定時間空ける。<p>
         * 
         * @param ctrl DaemonControlオブジェクト
         * @return スケジュールの配列
         */
        public Object provide(DaemonControl ctrl) throws Throwable{
            Thread.sleep(getTimeoverCheckInterval());
            return null;
        }
        
        /**
         * 最大遅延時間をチェックする。<p>
         *
         * @param dequeued null
         * @param ctrl DaemonControlオブジェクト
         */
        public void consume(Object dequeued, DaemonControl ctrl)
         throws Throwable{
            Connection con = null;
            try{
                con = connectionFactory.getConnection();
            }catch(ConnectionFactoryException e){
                getLogger().write(MSG_ID_TIMEOVER_CHECK_ERROR, e);
                return;
            }
            Statement st = null;
            PreparedStatement ps = null;
            ResultSet rs = null;
            try{
                st = con.createStatement();
                final SimpleDateFormat format = new SimpleDateFormat(
                    dateFormat + timeFormat
                );
                final Calendar nowCal = Calendar.getInstance();
                rs = st.executeQuery(
                    "select " + scheduleTableSchema.id
                        + ',' + scheduleTableSchema.date
                        + ',' + scheduleTableSchema.time
                        + ',' + scheduleTableSchema.maxDelayTime
                        + ',' + scheduleTableSchema.state
                        + ',' + scheduleTableSchema.taskName
                        + " from " + scheduleTableSchema.table
                        + " where "
                        + scheduleTableSchema.checkState + "<>'"
                        + scheduleTableSchema.getCheckStateString(Schedule.CHECK_STATE_TIMEOVER)
                        + "' and " + concatQuery(new StringBuilder(), scheduleTableSchema.date, scheduleTableSchema.time) + "<'"
                        + format.format(nowCal.getTime())
                        + "' and " + scheduleTableSchema.maxDelayTime + " is not null"
                        + " and " + scheduleTableSchema.maxDelayTime + ">0"
                        + " and " + scheduleTableSchema.state + "<>'"
                        + scheduleTableSchema.getStateString(Schedule.STATE_END)
                        + "' and " + scheduleTableSchema.state + "<>'"
                        + scheduleTableSchema.getStateString(Schedule.STATE_FAILED)
                        + "' and " + scheduleTableSchema.state + "<>'"
                        + scheduleTableSchema.getStateString(Schedule.STATE_ABORT) + '\''
                );
                final Calendar tmpCal = Calendar.getInstance();
                while(rs.next()){
                    final Date time = format.parse(rs.getString(2) + rs.getString(3));
                    tmpCal.clear();
                    final long maxDelayTime = rs.getLong(4);
                    tmpCal.setTimeInMillis(time.getTime() + maxDelayTime);
                    if(tmpCal.after(nowCal) || tmpCal.equals(nowCal)){
                        continue;
                    }
                    final String id = rs.getString(1);
                    if(ps == null){
                        String checkStateStr = scheduleTableSchema.getCheckStateString(Schedule.CHECK_STATE_TIMEOVER);
                        ps = con.prepareStatement(
                            "update " + scheduleTableSchema.table
                                + " set " + scheduleTableSchema.checkState + "='"
                                + checkStateStr + "',"
                                + scheduleTableSchema.updateUserId + "='" + updateUserId + "',"
                                + scheduleTableSchema.updateTime + "=?"
                                + " where " + scheduleTableSchema.id + "=?"
                                + " and " + scheduleTableSchema.checkState + "<>'" + checkStateStr + '\''
                        );
                    }
                    ps.setTimestamp(1, new Timestamp(System.currentTimeMillis()));
                    ps.setString(2, id);
                    
                    if(ps.executeUpdate() != 0){
                        final String state = rs.getString(5);
                        final String taskName = rs.getString(6);
                        getLogger().write(
                            MSG_ID_TIMEOVER_ERROR,
                            id,
                            taskName,
                            state
                        );
                    }
                }
            }catch(SQLException e){
                getLogger().write(MSG_ID_TIMEOVER_CHECK_ERROR, e);
                return;
            }finally{
                if(rs != null){
                    try{
                        rs.close();
                    }catch(SQLException e){
                    }
                }
                if(st != null){
                    try{
                        st.close();
                    }catch(SQLException e){
                    }
                }
                if(con != null){
                    try{
                        con.close();
                    }catch(SQLException e){
                    }
                }
            }
        }
        
        /**
         * 何もしない。<p>
         */
        public void garbage(){
        }
    }
    
    protected class ClusterListener implements jp.ossc.nimbus.service.distribute.ClusterListener{
        
        public void memberInit(Object myId, List<? extends Object> members){}
        
        public void memberChange(List<? extends Object> oldMembers, List<? extends Object> newMembers){}
        
        public void changeMain() throws Exception{
            if(controlStateChecker != null){
                controlStateChecker.resume();
            }
            if(timeoverChecker != null){
                timeoverChecker.resume();
            }
        }
        
        public void changeSub(){
            if(controlStateChecker != null){
                controlStateChecker.suspend();
            }
            if(timeoverChecker != null){
                timeoverChecker.suspend();
            }
        }
    }
}