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

import java.beans.*;
import java.net.*;
import java.io.*;
import java.util.*;
import javax.xml.parsers.*;
import org.w3c.dom.*;
import org.xml.sax.*;

import jp.ossc.nimbus.beans.*;
import jp.ossc.nimbus.service.log.*;
import jp.ossc.nimbus.service.message.MessageRecordFactory;
import jp.ossc.nimbus.service.repository.Repository;
import jp.ossc.nimbus.util.*;

/**
 * サービスローダ。<p>
 * サービス定義を読み込み、サービスを登録するローダである。<br>
 * サービスローダは、サービス{@link Service}として実装され、サービスの生成、起動と共に、サービス基盤を起動し、そこに配置される各サービスをホスティングする。<br>
 * 
 * @author M.Takata
 * @see <a href="nimbus-service_2_0.xsd">サービス定義ファイルスキーマ定義</a>
 */
public class DefaultServiceLoaderService extends ServiceBase
 implements ServiceLoader, DefaultServiceLoaderServiceMBean{
    
    private static final long serialVersionUID = 7335188900913701079L;
    
    // メッセージID定義
    private static final String SVCL_ = "SVCL_";
    private static final String SVCL_0 = SVCL_ + 0;
    private static final String SVCL_00 = SVCL_0 + 0;
    private static final String SVCL_000 = SVCL_00 + 0;
    private static final String SVCL_0000 = SVCL_000 + 0;
    private static final String SVCL_00001 = SVCL_0000 + 1;
    private static final String SVCL_00002 = SVCL_0000 + 2;
    private static final String SVCL_00003 = SVCL_0000 + 3;
    private static final String SVCL_00004 = SVCL_0000 + 4;
    private static final String SVCL_00005 = SVCL_0000 + 5;
    private static final String SVCL_00006 = SVCL_0000 + 6;
    private static final String SVCL_00007 = SVCL_0000 + 7;
    private static final String SVCL_00008 = SVCL_0000 + 8;
    private static final String SVCL_00009 = SVCL_0000 + 9;
    private static final String SVCL_00010 = SVCL_000 + 10;
    private static final String SVCL_00011 = SVCL_000 + 11;
    private static final String SVCL_00012 = SVCL_000 + 12;
    private static final String SVCL_00013 = SVCL_000 + 13;
    private static final String SVCL_00014 = SVCL_000 + 14;
    private static final String SVCL_00015 = SVCL_000 + 15;
    private static final String SVCL_00016 = SVCL_000 + 16;
    private static final String SVCL_00017 = SVCL_000 + 17;
    private static final String SVCL_00018 = SVCL_000 + 18;
    private static final String SVCL_00019 = SVCL_000 + 19;
    private static final String SVCL_00020 = SVCL_000 + 20;
    private static final String SVCL_00024 = SVCL_000 + 24;
    private static final String SVCL_00025 = SVCL_000 + 25;
    private static final String SVCL_00026 = SVCL_000 + 26;
    private static final String SVCL_00028 = SVCL_000 + 28;
    private static final String SVCL_00030 = SVCL_000 + 30;
    private static final String SVCL_00031 = SVCL_000 + 31;
    private static final String SVCL_00032 = SVCL_000 + 32;
    private static final String SVCL_00033 = SVCL_000 + 33;
    
    public static final String PROPERTY_DOCUMENT_BUILDER_FACTORY_CLASSNAME = DefaultServiceLoaderService.class.getName() + ".documentBuilderFactoryClassName";
    
    /**
     * デフォルトのサービス名。<p>
     * {@link #setServiceName(String)}されない場合は、このデフォルト名の後に、"{サービス定義のURL}"を付加したサービス名となる。<br>
     *
     * @see #setServiceName(String)
     */
    protected static final String DEFAULT_NAME
     = DefaultServiceLoaderService.class.getName();
    
    /**
     * デフォルトの{@link ServiceManager}インタフェース実装クラス。<p>
     * 
     * @see #setServiceManagerClassName(String)
     */
    private static final String DEFAULT_SERVICE_MANAGER_CLASS_NAME
     = jp.ossc.nimbus.core.DefaultServiceManagerService.class.getName();
    
    /**
     * VMに登録したシャットダウンフックのマッピング。<p>
     * &lt;manager&gt;要素のshutdown-hook属性にtrueが設定されていた場合、{@link Runtime#addShutdownHook(Thread)}で、ServiceManager毎に停止、破棄を行うスレッドを登録する。<br>
     * VMが起動したままで、ServiceManagerを破棄した場合などは、登録したスレッドが不要になるため、登録したスレッドをstaticに管理している。<br>
     */
    protected static final Map<String, Thread> shutdownHooks = new HashMap<String, Thread>();
    
    /**
     * このローダがロードするサービス定義ファイルのURL。<p>
     *
     * @see #setServiceURL(URL)
     * @see #getServiceURL()
     */
    protected URL serviceURL;
    
    /**
     * このローダでロードしたサービス定義のルートメタデータ。<p>
     *
     * @see #getServerMetaData()
     */
    protected NimbusMetaData serverData;
    
    /**
     * &lt;manager&gt;要素で定義されたServiceManagerの名前と、そのServiceManagerを格納するマップ。<p>
     * 
     * @see #getServiceManagers()
     */
    protected Map<String, ServiceManager> managerMap;
    
    /**
     * &lt;property-editors&gt;要素で設定されたjava.beans.PropertyEditorを保持するマップ。<p>
     */
    protected ClassMappingTree<Class<PropertyEditor>> propertyEditors;
    
    /**
     * サービス定義XMLをDTDで評価するかどうかを示すフラグ。<p>
     * 評価する場合true。<br>
     */
    protected boolean isValidate;
    
    /**
     * ここまでにロードしたサービスが全て正常に開始できているかをチェックするかどうかを示すフラグ。<p>
     * チェックする場合true。
     */
    protected boolean isCheckLoadManagerCompleted;
    
    /**
     * サービスが全て正常に開始できているかをチェックするマネージャ名の集合。<p>
     */
    protected Set<String> checkLoadManagerNames;
    
    /**
     * サービスロード構成情報。<p>
     */
    protected ServiceLoaderConfig loaderConfig;
    
    /**
     * ロード前のdefault-log要素の情報。<p>
     */
    protected DefaultLogMetaData preDefaultLogData;
    
    /**
     * ServiceManagerインタフェースの実装クラス名。<p>
     * デフォルトは、DEFAULT_SERVICE_MANAGER_CLASS_NAME。<br>
     *
     * @see #setServiceManagerClassName(String)
     * @see #getServiceManagerClassName()
     */
    protected String serviceManagerClassName = DEFAULT_SERVICE_MANAGER_CLASS_NAME;
    
    protected String documentBuilderFactoryClassName;
    
    // DefaultServiceLoaderServiceMBeanのJavaDoc
    @Override
    public void setCheckLoadManagerCompleted(boolean isCheck){
        isCheckLoadManagerCompleted = isCheck;
    }
    
    // DefaultServiceLoaderServiceMBeanのJavaDoc
    @Override
    public boolean isCheckLoadManagerCompleted(){
        return isCheckLoadManagerCompleted;
    }
    
    // DefaultServiceLoaderServiceMBeanのJavaDoc
    @Override
    public void setCheckLoadManagerCompletedBy(String[] managerNames){
        if(managerNames != null && managerNames.length != 0){
            checkLoadManagerNames = new HashSet<String>();
            for(int i = 0; i < managerNames.length; i++){
                checkLoadManagerNames.add(managerNames[i]);
            }
        }else{
            checkLoadManagerNames = null;
        }
    }
    
    // DefaultServiceLoaderServiceMBeanのJavaDoc
    @Override
    public String[] getCheckLoadManagerCompletedBy(){
        return checkLoadManagerNames == null
             ? new String[0] : checkLoadManagerNames
                .toArray(new String[checkLoadManagerNames.size()]);
    }
    
    @Override
    public void setDocumentBuilderFactoryClassName(String name){
        documentBuilderFactoryClassName = name;
    }
    @Override
    public String getDocumentBuilderFactoryClassName(){
        return documentBuilderFactoryClassName;
    }
    
    /**
     * コンストラクタ。<p>
     */
    public DefaultServiceLoaderService(){
        super();
        setServiceName(DEFAULT_NAME);
    }
    
    /**
     * ローダの初期化処理を行う。<p>
     * ここでは、以下の処理を行う。<br>
     * <ol>
     *   <li>{@link ServiceManagerFactory}へ自分自身を登録する。</li>
     *   <li>インスタンス変数を生成する。</li>
     * </ol>
     */
    @Override
    public void createService(){
        final Logger logger = getLogger();
        
        if(serverData == null && serviceURL == null){
            serviceURL = Utility.getDefaultServiceURL();
            logger.write(SVCL_00009, serviceURL);
        }
        
        if(serviceURL != null){
            final String myName = getServiceName();
            setServiceName(
                myName == null ? (DEFAULT_NAME + '{' + serviceURL + '}') : myName
            );
        }
        
        if(serviceURL != null){
            ServiceManagerFactory.registerLoader(this);
        }
        
        managerMap = new LinkedHashMap<String, ServiceManager>();
        propertyEditors = new ClassMappingTree<Class<PropertyEditor>>();
    }
    
    /**
     * ローダの開始処理を行う。<p>
     * ここでは、以下の処理を行う。<br>
     * <ol>
     *   <li>{@link #loadServerMetaData()}を呼び出す。</li>
     *   <li>インスタンス変数を初期化する。</li>
     *   <li>{@link #deployServerMetaData(NimbusMetaData)}を呼び出す。</li>
     *   <li>&lt;manager&gt;要素で定義された{@link ServiceManager}を起動する。</li>
     * </ol>
     * @exception Exception サービス定義の読み込み、配置に失敗した場合。または、ServiceManagerの生成、開始に失敗した場合
     */
    @Override
    public void startService() throws Exception{
        final Logger logger = getLogger();
        
        try{
            loadServerMetaData();
        }catch(Exception e){
            logger.write(SVCL_00010, e, serviceURL);
            throw e;
        }
        
        managerMap.clear();
        propertyEditors.clear();
        
        try{
            deployServerMetaData(serverData);
        }catch(Exception e){
            logger.write(SVCL_00024, e, serviceURL);
            throw e;
        }
        
        for(ServiceManager manager : managerMap.values()){
            try{
                startupServiceManager(manager);
            }catch(Exception e){
                logger.write(SVCL_00025, e, manager.getServiceName());
                manager.destroy();
            }
        }
        if(isCheckLoadManagerCompleted){
            if(checkLoadManagerNames == null){
                ServiceManagerFactory.checkLoadManagerCompleted();
            }else{
                ServiceManagerFactory.checkLoadManagerCompletedBy(
                    checkLoadManagerNames
                );
            }
        }
    }
    
    /**
     * ローダの停止処理を行う。<p>
     * <ol>
     *   <li>&lt;manager&gt;要素で定義された{@link ServiceManager}を停止する。</li>
     *   <li>&lt;manager&gt;要素のshutdown-hook属性で定義された値に従って、{@link Runtime#removeShutdownHook(Thread)}を行う。</li>
     *   <li>&lt;manager&gt;要素で定義された{@link ServiceManager}を破棄する。</li>
     *   <li>インスタンス変数を初期化する。</li>
     * </ol>
     *
     * @exception Exception ServiceManagerの停止に失敗した場合
     */
    @Override
    public void stopService() throws Exception{
        
        undeployServerMetaData(serverData);
        
        managerMap.clear();
        propertyEditors.clear();
    }
    
    /**
     * ローダの破棄処理を行う。<p>
     * <ol>
     *   <li>インスタンス変数を破棄する。</li>
     *   <li>{@link ServiceManagerFactory}から自分自身を削除する。</li>
     * </ol>
     *
     * @exception Exception ServiceManagerの破棄に失敗した場合
     */
    @Override
    public void destroyService() throws Exception{
        
        managerMap = null;
        propertyEditors = null;
        
        ServiceManagerFactory.unregisterLoader(this);
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public NimbusMetaData getServerMetaData(){
        return serverData;
    }
    
    /**
     * &lt;nimbus&gt;要素のメタデータを設定する。<p>
     *
     * @param data サービス定義&lt;nimbus&gt;要素メタデータ
     */
    public void setServerMetaData(NimbusMetaData data){
        serverData = data;
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public void setServiceURL(URL url) throws IllegalArgumentException{
        final Logger logger = getLogger();
        try{
            url.openConnection();
        }catch(IOException e){
            final MessageRecordFactory message = getMessageRecordFactory();
            throw new IllegalArgumentException(
                message.findMessage(SVCL_00004, url)
            );
        }
        serviceURL = url;
        logger.write(SVCL_00005, serviceURL);
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public URL getServiceURL(){
        return serviceURL;
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public Set<ServiceManager> getServiceManagers(){
        return new HashSet<ServiceManager>(managerMap.values());
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public PropertyEditor findEditor(Class<?> type){
        final Logger logger = getLogger();
        if(type == null){
            return null;
        }
        PropertyEditor editor = null;
        Class<PropertyEditor> clazz = propertyEditors.getValue(type);
        if(clazz == null){
            editor = NimbusPropertyEditorManager.findEditor(type);
        }else{
            try{
                editor = clazz.newInstance();
            }catch(InstantiationException e){
                logger.write(SVCL_00028, e, type, clazz);
                return null;
            }catch(IllegalAccessException e){
                logger.write(SVCL_00028, e, type, clazz);
                return null;
            }
        }
        return editor;
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public void setServicePath(String path) throws IllegalArgumentException{
        final Logger logger = getLogger();
        final URL url = Utility.convertServicePathToURL(path);
        try{
            setServiceURL(url);
            logger.write(SVCL_00006, url);
        }catch(IllegalArgumentException e){
            final MessageRecordFactory message = getMessageRecordFactory();
            throw new IllegalArgumentException(
                message.findMessage(SVCL_00007, path)
            );
        }
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public void setValidate(boolean validate){
        isValidate = validate;
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public boolean isValidate(){
        return isValidate;
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public void setConfig(ServiceLoaderConfig config){
        loaderConfig = config;
        if(loaderConfig != null){
            String prop = loaderConfig.getProperty(PROPERTY_DOCUMENT_BUILDER_FACTORY_CLASSNAME);
            if(prop != null){
                documentBuilderFactoryClassName = prop;
            }
        }
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public ServiceLoaderConfig getConfig(){
        return loaderConfig;
    }
    
    // ServiceLoaderのJavaDoc
    @SuppressWarnings("unchecked")
    @Override
    public void setServiceManagerClassName(String className)
     throws ClassNotFoundException, IllegalArgumentException{
        final Logger logger = getLogger();
        if(className != null && className.length() != 0){
            Class<ServiceManager> clazz = null;
            try{
                clazz = (Class<ServiceManager>)Class.forName(
                    className,
                    true,
                    NimbusClassLoader.getInstance()
                );
            }catch(ClassNotFoundException e){
                logger.write(
                    SVCL_00001,
                    e,
                    ServiceManager.class, className
                );
                throw e;
            }
            if(ServiceManager.class.isAssignableFrom(clazz)){
                serviceManagerClassName = className;
                logger.write(SVCL_00002, className);
            }else{
                final MessageRecordFactory message = getMessageRecordFactory();
                throw new IllegalArgumentException(
                    message.findMessage(SVCL_00003, className)
                );
            }
        }
    }
    
    // ServiceLoaderのJavaDoc
    @Override
    public String getServiceManagerClassName(){
        return serviceManagerClassName;
    }
    
    /**
     * サービス定義を読み込み、サービス定義&lt;nimbus&gt;要素メタデータを構築する。<p>
     * ここでは、以下の処理を行う。<br>
     * <ol>
     *   <li>{@link #setServiceURL(URL)}で設定されたURLからサービス定義XMLを読み込み、パースする。</li>
     *   <li>パースしたサービス定義XMLから、{@link NimbusMetaData}を生成する。ServerMetaDataの生成の過程で、各要素に対応するメタデータも生成される。</li>
     * </ol>
     *
     * @exception ParserConfigurationException XMLパーサの生成に失敗した場合
     * @exception IOException {@link #setServiceURL(URL)}で設定されたURLにサービス定義XMLが見つからない場合
     * @exception SAXException サービス定義XMLのパースに失敗した場合
     * @exception DeploymentException サービス定義の設定に誤りがある場合
     */
    public void loadServerMetaData()
     throws IOException, ParserConfigurationException, SAXException,
            DeploymentException{
        final Logger logger = getLogger();
        
        logger.write(SVCL_00008);
        
        if(serverData != null && serviceURL == null){
            return;
        }
        
        if(serviceURL == null){
            serviceURL = Utility.getDefaultServiceURL();
            logger.write(SVCL_00009, serviceURL);
        }
        serverData = loadServerMetaData(serviceURL.openStream());
    }
    
    /**
     * サービス定義を読み込み、サービス定義&lt;nimbus&gt;要素メタデータを構築する。<p>
     * ここでは、以下の処理を行う。<br>
     * <ol>
     *   <li>指定されたURLからサービス定義XMLを読み込み、パースする。</li>
     *   <li>パースしたサービス定義XMLから、{@link NimbusMetaData}を生成する。ServerMetaDataの生成の過程で、各要素に対応するメタデータも生成される。</li>
     * </ol>
     *
     * @param url サービス定義URL
     * @exception ParserConfigurationException XMLパーサの生成に失敗した場合
     * @exception IOException {@link #setServiceURL(URL)}で設定されたURLにサービス定義XMLが見つからない場合
     * @exception SAXException サービス定義XMLのパースに失敗した場合
     * @exception DeploymentException サービス定義の設定に誤りがある場合
     */
    protected NimbusMetaData loadServerMetaData(URL url)
     throws IOException, ParserConfigurationException, SAXException,
            DeploymentException{
        
        if(url == null){
            return null;
        }
        return loadServerMetaData(url.openStream());
    }
    
    /**
     * サービス定義を読み込み、サービス定義&lt;nimbus&gt;要素メタデータを構築する。<p>
     * ここでは、以下の処理を行う。<br>
     * <ol>
     *   <li>指定されたストリームからサービス定義XMLを読み込み、パースする。</li>
     *   <li>パースしたサービス定義XMLから、{@link NimbusMetaData}を生成する。ServerMetaDataの生成の過程で、各要素に対応するメタデータも生成される。</li>
     * </ol>
     *
     * @param is サービス定義入力ストリーム
     * @exception ParserConfigurationException XMLパーサの生成に失敗した場合
     * @exception IOException {@link #setServiceURL(URL)}で設定されたURLにサービス定義XMLが見つからない場合
     * @exception SAXException サービス定義XMLのパースに失敗した場合
     * @exception DeploymentException サービス定義の設定に誤りがある場合
     */
    public NimbusMetaData loadServerMetaData(InputStream is)
     throws IOException, ParserConfigurationException, SAXException,
            DeploymentException{
        final InputSource inputSource = new InputSource(is);
        DocumentBuilderFactory domFactory = null;
        String className = System.getProperty(PROPERTY_DOCUMENT_BUILDER_FACTORY_CLASSNAME);
        if(className == null){
            className = documentBuilderFactoryClassName;
        }
        if(className == null){
            domFactory = DocumentBuilderFactory.newInstance();
        }else{
            domFactory = DocumentBuilderFactory.newInstance(
                className,
                NimbusClassLoader.getInstance()
            );
        }
        domFactory.setValidating(isValidate());
        final DocumentBuilder builder = domFactory.newDocumentBuilder();
        final NimbusEntityResolver resolver = new NimbusEntityResolver();
        builder.setEntityResolver(resolver);
        final MyErrorHandler handler = new MyErrorHandler();
        builder.setErrorHandler(handler);
        final Document doc = builder.parse(inputSource);
        if(handler.isError()){
            final MessageRecordFactory message = getMessageRecordFactory();
            throw new DeploymentException(
                message.findMessage(SVCL_00033, serviceURL)
            );
        }
        
        final NimbusMetaData serverData = new NimbusMetaData(this, serviceURL);
        final DocumentType docType = doc.getDoctype();
        if(docType != null){
            serverData.setDocType(
                "<!DOCTYPE " + docType.getName() + " PUBLIC \""
                     + docType.getPublicId() + "\" \""
                     + docType.getSystemId() + "\">"
            );
        }
        if(inputSource.getEncoding() != null){
            serverData.setEncoding(inputSource.getEncoding());
        }
        serverData.importXML(doc.getDocumentElement());
        
        final Map<String, String> props = serverData.getProperties();
        final Object[] propKeys = props.keySet().toArray();
        for(int i = 0; i < propKeys.length; i++){
            final String propKey = (String)propKeys[i];
            String prop = props.get(propKey);
            // システムプロパティの置換
            prop = Utility.replaceSystemProperty(prop, false);
            // サービスローダ構成プロパティの置換
            prop = Utility.replaceServiceLoderConfig(
                getConfig(),
                prop,
                false
            );
            // サーバプロパティの置換
            prop = Utility.replaceServerProperty(prop, true);
            ServiceManagerFactory.setProperty(propKey, prop);
        }
        return serverData;
    }
    
    /**
     * サービス定義&lt;nimbus&gt;要素メタデータを配置する。<p>
     *
     * @param serverData サービス定義&lt;nimbus&gt;要素メタデータ
     * @exception ParserConfigurationException XMLパーサの生成に失敗した場合
     * @exception IOException &lt;ref-url&gt;要素で指定されたURLのサービス定義XMLが見つからない場合
     * @exception SAXException サービス定義XMLのパースに失敗した場合
     * @exception DeploymentException サービス定義の設定に誤りがある場合
     */
    protected void deployServerMetaData(NimbusMetaData serverData)
     throws IOException, ParserConfigurationException, SAXException,
            DeploymentException{
        final Logger logger = getLogger();
        
        logger.write(SVCL_00011, serverData);
        
        final DefaultLogMetaData defaultLogData
             = serverData.getDefaultLog();
        deployDefaultLogMetaData(defaultLogData);
        
        deployPropertyEditors();
        
        // サーバLoggerの設定
        final ServiceNameMetaData logData = serverData.getLog();
        if(logData != null){
            final String managerName = logData.getManagerName();
            final String logName = logData.getServiceName();
            if(managerName != null && logName != null){
                ServiceManagerFactory.setLogger(managerName, logName);
            }
        }
        
        // サーバMessageRecordFactoryの設定
        final ServiceNameMetaData messageData = serverData.getMessage();
        if(messageData != null){
            final String managerName = messageData.getManagerName();
            final String messageName = messageData.getServiceName();
            if(managerName != null && messageName != null){
                ServiceManagerFactory.setMessageRecordFactory(
                    managerName,
                    messageName
                );
            }
        }
        
        // サーバRepositoryの設定
        final ServiceNameMetaData repositoryData = serverData.getRepository();
        if(repositoryData != null){
            final String managerName = repositoryData.getManagerName();
            final String repositoryName = repositoryData.getServiceName();
            if(managerName != null && repositoryName != null){
                ServiceManagerFactory.setManagerRepository(
                    managerName,
                    repositoryName
                );
            }
        }
        
        for(ManagerMetaData managerData : serverData.getManagers()){
            deployManagerMetaData(managerData);
        }
    }
    
    /**
     * サービス定義&lt;nimbus&gt;要素メタデータを削除する。<p>
     *
     * @param serverData サービス定義&lt;nimbus&gt;要素メタデータ
     * @exception DeploymentException サービス定義の設定に誤りがある場合
     */
    protected void undeployServerMetaData(NimbusMetaData serverData)
     throws DeploymentException{
        
        for(ServiceManager manager : managerMap.values()){
            shutdownServiceManager(manager);
        }
        
        // サーバRepositoryの設定解除
        final ServiceNameMetaData repositoryData = serverData.getRepository();
        if(repositoryData != null){
            final String managerName = repositoryData.getManagerName();
            final String repositoryName = repositoryData.getServiceName();
            if(managerName != null && repositoryName != null){
                if(ServiceManagerFactory
                    .isRegisteredService(managerName, repositoryName)){
                    ServiceManagerFactory.setManagerRepository(
                        (Repository)null
                    );
                }
            }
        }
        
        final DefaultLogMetaData defaultLogData
             = serverData.getDefaultLog();
        undeployDefaultLogMetaData(defaultLogData);
    }
    
    /**
     * サーバLoggerの設定を行う。<p>
     *
     * @param defaultLogData サービス定義&lt;default-log&gt;要素メタデータ
     */
    protected void deployDefaultLogMetaData(DefaultLogMetaData defaultLogData){
        if(defaultLogData == null){
            return;
        }
        
        preDefaultLogData = new DefaultLogMetaData(defaultLogData.getParent());
        
        final Logger logger = getLogger();
        DefaultLogMetaData.LogCategoryMetaData categoryData
             = defaultLogData.getDebug();
        if(categoryData != null){
            final DefaultLogMetaData.LogCategoryMetaData preDebugData
                 = new DefaultLogMetaData.LogCategoryMetaData(preDefaultLogData);
            preDebugData.setOutput(
                ServiceManagerFactory.DEFAULT_LOGGER.isSystemDebugEnabled()
            );
            ServiceManagerFactory.DEFAULT_LOGGER
                .setSystemDebugEnabled(categoryData.isOutput());
            preDefaultLogData.setDebug(preDebugData);
            if(ServiceManagerFactory.DEFAULT_LOGGER.isSystemDebugEnabled()){
                logger.write(SVCL_00012, DefaultLoggerService.SYSTEM_DEBUG_CATEGORY_LABEL);
            }else{
                logger.write(SVCL_00013, DefaultLoggerService.SYSTEM_DEBUG_CATEGORY_LABEL);
            }
        }else{
            ServiceManagerFactory.DEFAULT_LOGGER.setSystemDebugEnabled(false);
        }
        categoryData = defaultLogData.getInformation();
        if(categoryData != null){
            final DefaultLogMetaData.LogCategoryMetaData preInfoData
                 = new DefaultLogMetaData.LogCategoryMetaData(preDefaultLogData);
            preInfoData.setOutput(
                ServiceManagerFactory.DEFAULT_LOGGER.isSystemInfoEnabled()
            );
            ServiceManagerFactory.DEFAULT_LOGGER
                .setSystemInfoEnabled(categoryData.isOutput());
            preDefaultLogData.setInformation(preInfoData);
            if(ServiceManagerFactory.DEFAULT_LOGGER.isSystemInfoEnabled()){
                logger.write(SVCL_00012, DefaultLoggerService.SYSTEM_INFO_CATEGORY_LABEL);
            }else{
                logger.write(SVCL_00013, DefaultLoggerService.SYSTEM_INFO_CATEGORY_LABEL);
            }
        }else{
            ServiceManagerFactory.DEFAULT_LOGGER.setSystemInfoEnabled(true);
        }
        categoryData = defaultLogData.getWarning();
        if(categoryData != null){
            final DefaultLogMetaData.LogCategoryMetaData preWarnData
                 = new DefaultLogMetaData.LogCategoryMetaData(preDefaultLogData);
            preWarnData.setOutput(
                ServiceManagerFactory.DEFAULT_LOGGER.isSystemWarnEnabled()
            );
            ServiceManagerFactory.DEFAULT_LOGGER
                .setSystemWarnEnabled(categoryData.isOutput());
            preDefaultLogData.setWarning(preWarnData);
            if(ServiceManagerFactory.DEFAULT_LOGGER.isSystemWarnEnabled()){
                logger.write(SVCL_00012, DefaultLoggerService.SYSTEM_WARN_CATEGORY_LABEL);
            }else{
                logger.write(SVCL_00013, DefaultLoggerService.SYSTEM_WARN_CATEGORY_LABEL);
            }
        }else{
            ServiceManagerFactory.DEFAULT_LOGGER.setSystemWarnEnabled(true);
        }
        categoryData = defaultLogData.getError();
        if(categoryData != null){
            final DefaultLogMetaData.LogCategoryMetaData preErrorData
                 = new DefaultLogMetaData.LogCategoryMetaData(preDefaultLogData);
            preErrorData.setOutput(
                ServiceManagerFactory.DEFAULT_LOGGER.isSystemErrorEnabled()
            );
            ServiceManagerFactory.DEFAULT_LOGGER
                .setSystemErrorEnabled(categoryData.isOutput());
            preDefaultLogData.setError(preErrorData);
            if(ServiceManagerFactory.DEFAULT_LOGGER.isSystemErrorEnabled()){
                logger.write(SVCL_00012, DefaultLoggerService.SYSTEM_ERROR_CATEGORY_LABEL);
            }else{
                logger.write(SVCL_00013, DefaultLoggerService.SYSTEM_ERROR_CATEGORY_LABEL);
            }
        }else{
            ServiceManagerFactory.DEFAULT_LOGGER.setSystemErrorEnabled(true);
        }
        categoryData = defaultLogData.getFatal();
        if(categoryData != null){
            final DefaultLogMetaData.LogCategoryMetaData preFatalData
                 = new DefaultLogMetaData.LogCategoryMetaData(preDefaultLogData);
            preFatalData.setOutput(
                ServiceManagerFactory.DEFAULT_LOGGER.isSystemFatalEnabled()
            );
            ServiceManagerFactory.DEFAULT_LOGGER
                .setSystemFatalEnabled(categoryData.isOutput());
            preDefaultLogData.setFatal(preFatalData);
            if(ServiceManagerFactory.DEFAULT_LOGGER.isSystemFatalEnabled()){
                logger.write(SVCL_00012, DefaultLoggerService.SYSTEM_FATAL_CATEGORY_LABEL);
            }else{
                logger.write(SVCL_00013, DefaultLoggerService.SYSTEM_FATAL_CATEGORY_LABEL);
            }
        }else{
            ServiceManagerFactory.DEFAULT_LOGGER.setSystemFatalEnabled(true);
        }
    }
    
    /**
     * サーバLoggerの設定をデフォルトの設定に戻す。<p>
     *
     * @param defaultLogData サービス定義&lt;default-log&gt;要素メタデータ
     */
    protected void undeployDefaultLogMetaData(
        DefaultLogMetaData defaultLogData
    ){
        if(defaultLogData == null && preDefaultLogData == null){
            return;
        }
        if(preDefaultLogData.getDebug() != null){
            ServiceManagerFactory.DEFAULT_LOGGER.setSystemDebugEnabled(
                preDefaultLogData.getDebug().isOutput()
            );
        }
        if(preDefaultLogData.getInformation() != null){
            ServiceManagerFactory.DEFAULT_LOGGER.setSystemInfoEnabled(
                preDefaultLogData.getInformation().isOutput()
            );
        }
        if(preDefaultLogData.getWarning() != null){
            ServiceManagerFactory.DEFAULT_LOGGER.setSystemWarnEnabled(
                preDefaultLogData.getWarning().isOutput()
            );
        }
        if(preDefaultLogData.getError() != null){
            ServiceManagerFactory.DEFAULT_LOGGER.setSystemErrorEnabled(
                preDefaultLogData.getError().isOutput()
            );
        }
        if(preDefaultLogData.getFatal() != null){
            ServiceManagerFactory.DEFAULT_LOGGER.setSystemFatalEnabled(
                preDefaultLogData.getFatal().isOutput()
            );
        }
    }
    
    /**
     * サービス定義&lt;property-editors&gt;要素で定義されたjava.beans.PropertyEditorをロードして、java.beans.PropertyEditorManagerに登録する。<p>
     */
    @SuppressWarnings("unchecked")
    protected void deployPropertyEditors(){
        final Logger logger = getLogger();
        
        logger.write(SVCL_00014);
        
        final Map<String, String> propertyEditors = serverData.getPropertyEditors();
        final ClassLoader loader = NimbusClassLoader.getInstance();
        for(Map.Entry<String, String> entry : propertyEditors.entrySet()){
            final String typeName = entry.getKey();
            final String editorClassName = entry.getValue();
            
            Class<?> type = null;
            Class<PropertyEditor> editorClass = null;
            try{
                type = Class.forName(typeName, true, loader);
            }catch(ClassNotFoundException e){
                logger.write(SVCL_00015, e, typeName);
                continue;
            }
            try{
                editorClass = (Class<PropertyEditor>)Class.forName(editorClassName, true, loader);
            }catch(ClassNotFoundException e){
                logger.write(SVCL_00016, e, editorClassName);
                continue;
            }
            this.propertyEditors.add(type, editorClass);
            logger.write(SVCL_00017, type, editorClass);
        }
    }
    
    /**
     * サービス定義&lt;manager&gt;要素メタデータを配置する。<p>
     *
     * @param managerData サービス定義&lt;manager&gt;要素メタデータ
     * @exception DeploymentException サービス定義の設定に誤りがある場合
     */
    @SuppressWarnings("unchecked")
    protected void deployManagerMetaData(ManagerMetaData managerData)
     throws DeploymentException{
        final Logger logger = getLogger();
        
        logger.write(SVCL_00018, managerData);
        
        final String name = managerData.getName();
        ServiceManager manager = ServiceManagerFactory.findManager(name);
        if(manager == null){
            manager = managerMap.get(name);
        }
        if(manager == null){
            String managerClassName = managerData.getCode();
            if(managerClassName == null){
                managerClassName = serviceManagerClassName;
            }
            try{
                final Class<ServiceManager> clazz = (Class<ServiceManager>)Class.forName(
                    managerClassName,
                    true,
                    NimbusClassLoader.getInstance()
                );
                manager = clazz.newInstance();
            }catch(Exception e){
                final MessageRecordFactory message = getMessageRecordFactory();
                throw new DeploymentException(
                    message.findMessage(SVCL_00019, name),
                    e
                );
            }
            manager.setServiceName(name);
            manager.setServiceManagerName(name);
            logger.write(SVCL_00020, name);
        }
        managerMap.put(name, manager);
        
        manager.deploy(managerData);
    }
    
    /**
     * このローダでロードされたサービスの内で、指定されたServiceManagerに登録されたサービスの生成、開始を行う。<p>
     * ServiceManagerが破棄されている状態の場合は、{@link ServiceManager#create()}、{@link ServiceManager#start()}を順次呼び出す。<br>
     * ServiceManagerが停止されている状態の場合は、{@link ServiceManager#start()}を呼び出す。<br>
     * ServiceManagerが開始されている状態の場合は、このローダでロードされたサービスを順次{@link ServiceManager#createService(Set)}、{@link ServiceManager#startService(Set)}を呼び出して生成、開始する。<br>
     * ServiceManagerが生成されている状態の場合は、{@link ServiceManager#start()}を呼び出す。<br>
     * また、ServiceManagerが開始されていない状態の場合で、&lt;manager&gt;要素のshutdown-hook属性がtrueの場合は、VMの終了をフックして{@link ServiceManager#stop()}、{@link ServiceManager#destroy()}を呼び出すスレッドを、{@link Runtime#addShutdownHook(Thread)}で設定する。<br>
     *
     * @param manager 生成、開始するServiceManager
     * @exception Exception ServiceManagerの生成、開始に失敗した場合
     */
    private void startupServiceManager(final ServiceManager manager)
     throws Exception{
        final Logger logger = getLogger();
        final State state = manager.getState();
        if(state != State.STARTED){
            if(state == State.DESTROYED){
                manager.create();
            }
            manager.start();
            
            final ManagerMetaData managerData
                 = serverData.getManager(manager.getServiceName());
            if(managerData.isExistShutdownHook()){
                final String managerName = manager.getServiceName();
                if(shutdownHooks.containsKey(managerName)){
                    Runtime.getRuntime().removeShutdownHook(
                        (Thread)shutdownHooks.get(managerName)
                    );
                }
                final Thread shutdownHook = new Thread(
                    new Runnable(){
                        public void run(){
                            manager.stop();
                            manager.destroy();
                        }
                    }
                );
                Runtime.getRuntime().addShutdownHook(shutdownHook);
                shutdownHooks.put(manager.getServiceName(), shutdownHook);
                logger.write(SVCL_00026, manager.getServiceName());
            }
        }else{
            final ManagerMetaData managerData
                 = serverData.getManager(manager.getServiceName());
            final Set<String> serviceNames = new HashSet<String>(
                managerData.getServices().keySet()
            );
            manager.createService(serviceNames);
            manager.startService(serviceNames);
        }
    }
    
    /**
     * このローダでロードされたサービスの内で、指定されたServiceManagerに登録されたサービスの停止、破棄を行う。<p>
     * このローダでロードされたサービスの内で、指定されたServiceManagerに登録されたサービスを{@link ServiceManager#destroyService(Set)}で停止、破棄する。<br>
     * その後、指定されたServiceManagerに、他のローダでロードされたサービスが登録されていない場合は、{@link ServiceManager#stop()}、{@link ServiceManager#destroy()}を呼び出し、ServiceManager自身も停止、破棄する。また、シャットダウンフックが設定されている場合は、除去する。<br>
     *
     * @param manager ServiceManagerオブジェクト
     */
    private void shutdownServiceManager(ServiceManager manager){
        final ManagerMetaData managerData
             = serverData.getManager(manager.getServiceName());
        final Set<String> serviceNames = new HashSet<String>(
            managerData.getServices().keySet()
        );
        manager.destroyService(serviceNames);
        manager.undeploy(managerData);
        
        if(manager.getManagerMetaDataSet().size() == 0){
            manager.stop();
            final Thread hook
                 = (Thread)shutdownHooks.remove(manager.getServiceName());
            if(hook != null){
                try{
                    Runtime.getRuntime().removeShutdownHook(hook);
                }catch(IllegalStateException e){}
            }
            manager.destroy();
        }
    }
    
    protected class MyErrorHandler implements ErrorHandler{
        
        private boolean isError;
        
        @Override
        public void warning(SAXParseException e) throws SAXException{
            getLogger().write(
                SVCL_00030,
                e.getMessage(), e.getLineNumber(), e.getColumnNumber()
            );
        }
        @Override
        public void error(SAXParseException e) throws SAXException{
            isError = true;
            getLogger().write(
                SVCL_00031,
                e.getMessage(), e.getLineNumber(), e.getColumnNumber()
            );
        }
        @Override
        public void fatalError(SAXParseException e) throws SAXException{
            isError = true;
            getLogger().write(
                SVCL_00032,
                e.getMessage(),e.getLineNumber(), e.getColumnNumber()
            );
        }
        public boolean isError(){
            return isError;
        }
    }
}
