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

import java.io.*;
import java.util.*;
import java.lang.reflect.*;
import java.beans.PropertyEditor;
import java.sql.ResultSet;
import java.math.*;
import javax.transaction.*;

import org.w3c.dom.*;

import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.MapContext;

import jp.ossc.nimbus.beans.NestedProperty;
import jp.ossc.nimbus.beans.NoSuchPropertyException;
import jp.ossc.nimbus.beans.NullIndexPropertyException;
import jp.ossc.nimbus.beans.NullKeyPropertyException;
import jp.ossc.nimbus.beans.NullNestPropertyException;
import jp.ossc.nimbus.beans.Property;
import jp.ossc.nimbus.beans.PropertyAccess;
import jp.ossc.nimbus.beans.PropertyFactory;
import jp.ossc.nimbus.beans.ServiceNameEditor;
import jp.ossc.nimbus.beans.SimpleProperty;
import jp.ossc.nimbus.core.MetaData;
import jp.ossc.nimbus.core.Utility;
import jp.ossc.nimbus.core.DeploymentException;
import jp.ossc.nimbus.core.ServiceManagerFactory;
import jp.ossc.nimbus.core.ServiceManager;
import jp.ossc.nimbus.core.ServiceName;
import jp.ossc.nimbus.service.journal.Journal;
import jp.ossc.nimbus.service.semaphore.DefaultSemaphoreService;
import jp.ossc.nimbus.service.semaphore.Semaphore;
import jp.ossc.nimbus.service.resource.Resource;
import jp.ossc.nimbus.service.resource.ClosableResource;
import jp.ossc.nimbus.service.resource.ResourceException;
import jp.ossc.nimbus.service.resource.ResourceFactory;
import jp.ossc.nimbus.service.resource.TransactionResource;
import jp.ossc.nimbus.service.context.Context;
import jp.ossc.nimbus.service.aop.interceptor.ThreadContextKey;
import jp.ossc.nimbus.service.interpreter.Interpreter;
import jp.ossc.nimbus.service.interpreter.CompiledInterpreter;
import jp.ossc.nimbus.service.interpreter.EvaluateException;
import jp.ossc.nimbus.service.sql.PersistentManager;
import jp.ossc.nimbus.service.transaction.TransactionManagerFactory;
import jp.ossc.nimbus.service.transaction.TransactionManagerFactoryException;
import jp.ossc.nimbus.service.queue.AsynchContext;
import jp.ossc.nimbus.service.queue.Queue;
import jp.ossc.nimbus.service.queue.QueueHandlerContainer;
import jp.ossc.nimbus.service.queue.DefaultQueueService;

/**
 * 業務フロー実行クラス。<p>
 * 業務フロー定義ファイルにて、以下のような記述が可能である。<br>
 * <ul>
 *   <li>POJOのコンストラクタインジェクション</li>
 *   <li>POJOへのフィールドインジェクション</li>
 *   <li>POJOへのプロパティインジェクション</li>
 *   <li>POJOへのメソッドインジェクション</li>
 *   <li>POJOへのサービスインジェクション</li>
 *   <li>条件分岐</li>
 *   <li>繰り返し</li>
 *   <li>四則演算</li>
 *   <li>子フロー呼び出し</li>
 *   <li>フロー及びステップの流量制御</li>
 *   <li>トランザクションリソースの制御</li>
 *   <li>JTAによるトランザクションの制御</li>
 *   <li>例外処理</li>
 * </ul>
 *
 * @author M.Takata
 * @see <a href="beanflow_1_0.dtd">業務フロー定義ファイルDTD</a>
 */
public class DefaultBeanFlow extends MetaData implements BeanFlow{
    
    private static final long serialVersionUID = 7290088961425225122L;

    /**
     * トランザクションタイプ Required。<p>
     * 既存トランザクションがなければ新しく開始する。あれば何もしない。
     */
    public static final String REQUIRED = "Required";
    
    /**
     * トランザクションタイプ RequiresNew。<p>
     * 既存トランザクションがなければ新しく開始する。あればsuspendして新しく開始する。
     */
    public static final String REQUIRESNEW = "RequiresNew";
    
    /**
     * トランザクションタイプ Supports。<p>
     * 既存トランザクションがあっても、なくても何もしない。
     */
    public static final String SUPPORTS = "Supports";
    
    /**
     * トランザクションタイプ Mandatory。<p>
     * 既存トランザクションがなければエラー。あれば何もしない。
     */
    public static final String MANDATORY = "Mandatory";
    
    /**
     * トランザクションタイプ Never。<p>
     * 既存トランザクションがなければ何もしない。あればエラー。
     */
    public static final String NEVER = "Never";
    
    /**
     * トランザクションタイプ NotSupported。<p>
     * 既存トランザクションがなければ何もしない。あれば停止する。
     */
    public static final String NOT_SUPPORTED = "NotSupported";
    
    protected static final String ARRAY_CLASS_SUFFIX = "[]";
    protected static final String ALIAS_ELEMENT = "alias";
    protected static final String RESOURCE_ELEMENT = "resource";
    protected static final String RESOURCE_REF_ELEMENT = "resource-ref";
    protected static final String STEP_ELEMENT = "step";
    protected static final String SWITCH_ELEMENT = "switch";
    protected static final String CASE_ELEMENT = "case";
    protected static final String DEFAULT_ELEMENT = "default";
    protected static final String IF_ELEMENT = "if";
    protected static final String TARGET_ELEMENT = "target";
    protected static final String INPUT_ELEMENT = "input";
    protected static final String RESULT_ELEMENT = "result";
    protected static final String THIS_ELEMENT = "this";
    protected static final String CALL_FLOW_ELEMENT = "callflow";
    protected static final String STEP_REF_ELEMENT = "step-ref";
    protected static final String RETURN_ELEMENT = "return";
    protected static final String FOR_ELEMENT = "for";
    protected static final String VAR_ELEMENT = "var";
    protected static final String CONTINUE_ELEMENT = "continue";
    protected static final String BREAK_ELEMENT = "break";
    protected static final String CATCH_ELEMENT = "catch";
    protected static final String FINALLY_ELEMENT = "finally";
    protected static final String THROW_ELEMENT = "throw";
    protected static final String EXPRESSION_ELEMENT = "expression";
    protected static final String WHILE_ELEMENT = "while";
    protected static final String INTERPRETER_ELEMENT = "interpreter";
    protected static final String OVERRIDE_ELEMENT = "override";
    protected static final String REPLY_ELEMENT = "reply";
    protected static final String CALLBACK_ELEMENT = "callback";
    
    protected static final String NAME_ATTRIBUTE = "name";
    protected static final String STEPNAME_ATTRIBUTE = "stepname";
    protected static final String KEY_ATTRIBUTE = "key";
    protected static final String SERVICE_ATTRIBUTE = "service";
    protected static final String TRANSACTION_ATTRIBUTE = "transaction";
    protected static final String TRANTIMEOUT_ATTRIBUTE = "trantimeout";
    protected static final String TRANCONTROL_ATTRIBUTE = "trancontrol";
    protected static final String TRANCLOSE_ATTRIBUTE = "tranclose";
    protected static final String TEST_ATTRIBUTE = "test";
    protected static final String EXCEPTION_ATTRIBUTE = "exception";
    protected static final String INDEX_ATTRIBUTE = "index";
    protected static final String BEGIN_ATTRIBUTE = "begin";
    protected static final String END_ATTRIBUTE = "end";
    protected static final String VAR_ATTRIBUTE = "var";
    protected static final String RAW_ATTRIBUTE = "raw";
    protected static final String MAX_RUN_THREADS_ATTRIBUTE = "maxRunThreads";
    protected static final String MAX_WAIT_THREADS_ATTRIBUTE = "maxWaitThreads";
    protected static final String TIMEOUT_ATTRIBUTE = "timeout";
    protected static final String FORCE_FREE_TIMEOUT_ATTRIBUTE = "forceFreeTimeout";
    protected static final String JOURNAL_ATTRIBUTE = "journal";
    protected static final String DO_ATTRIBUTE = "do";
    protected static final String SUSPEND_ATTRIBUTE = "suspend";
    protected static final String STOP_ATTRIBUTE = "stop";
    protected static final String FACTORY_ATTRIBUTE = "factory";
    protected static final String NULLCHECK_ATTRIBUTE = "nullCheck";
    protected static final String NARROWCAST_ATTRIBUTE = "narrowCast";
    protected static final String OVERRIDE_ATTRIBUTE = "override";
    protected static final String ASYNCH_ATTRIBUTE = "asynch";
    protected static final String REPLY_ATTRIBUTE = "reply";
    protected static final String MAX_ASYNCH_WAIT_ATTRIBUTE = "maxAsynchWait";
    protected static final String CANCEL_ATTRIBUTE = "cancel";
    
    protected static final String JOURNAL_KEY_FLOW = "Flow";
    protected static final String JOURNAL_KEY_FLOW_NAME = "Name";
    protected static final String JOURNAL_KEY_FLOW_INPUT = "Input";
    protected static final String JOURNAL_KEY_FLOW_EXCEPTION = "Exception";
    protected static final String JOURNAL_KEY_FLOW_OUTPUT = "Output";
    protected static final String JOURNAL_KEY_RESOURCE = "Resource";
    protected static final String JOURNAL_KEY_STEP = "Step";
    protected static final String JOURNAL_KEY_STEP_NAME = "Name";
    protected static final String JOURNAL_KEY_STEP_TARGET = "Target";
    protected static final String JOURNAL_KEY_STEP_RESULT = "Result";
    protected static final String JOURNAL_KEY_STEP_EXCEPTION = "Exception";
    protected static final String JOURNAL_KEY_IF = "If";
    protected static final String JOURNAL_KEY_TEST = "Test";
    protected static final String JOURNAL_KEY_CATCH = "Catch";
    protected static final String JOURNAL_KEY_CATCH_EXCEPTION = "Exception";
    protected static final String JOURNAL_KEY_FINALLY = "Finally";
    protected static final String JOURNAL_KEY_OBJECT = "Object";
    protected static final String JOURNAL_KEY_INSTANCE = "Instance";
    protected static final String JOURNAL_KEY_CLASS = "Class";
    protected static final String JOURNAL_KEY_FIELD = "Field:";
    protected static final String JOURNAL_KEY_ATTRIBUTE = "Attribute:";
    protected static final String JOURNAL_KEY_INVOKE = "Invoke:";
    protected static final String JOURNAL_KEY_STATIC_INVOKE = "StaticInvoke:";
    protected static final String JOURNAL_KEY_WHILE = "While";
    protected static final String JOURNAL_KEY_FOR = "For";
    protected static final String JOURNAL_KEY_CALLFLOW = "CallFlow";
    protected static final String JOURNAL_KEY_GETASYNCHREPLY = "GetAsynchReply";
    
    /**
     * トランザクションタイプ Required。<p>
     * 既存トランザクションがなければ新しく開始する。あれば何もしない。
     */
    protected static final int REQUIRED_VALUE = 0;
    
    /**
     * トランザクションタイプ RequiresNew。<p>
     * 既存トランザクションがなければ新しく開始する。あればsuspendして新しく開始する。
     */
    protected static final int REQUIRESNEW_VALUE = 1;
    
    /**
     * トランザクションタイプ Supports。<p>
     * 既存トランザクションがあっても、なくても何もしない。
     */
    protected static final int SUPPORTS_VALUE = 2;
    
    /**
     * トランザクションタイプ Mandatory。<p>
     * 既存トランザクションがなければエラー。あれば何もしない。
     */
    protected static final int MANDATORY_VALUE = 3;
    
    /**
     * トランザクションタイプ Never。<p>
     * 既存トランザクションがなければ何もしない。あればエラー。
     */
    protected static final int NEVER_VALUE = 4;
    
    /**
     * トランザクションタイプ NotSupported。<p>
     * 既存トランザクションがなければ何もしない。あれば停止する。
     */
    protected static final int NOT_SUPPORTED_VALUE = 5;
    
    protected String flowName;
    
    protected List<String> aliasNames = new ArrayList<String>();
    
    protected Map<String, ResourceInfo> resources;
    
    protected List<Step> steps;
    
    protected List<CatchMetaData> catchSteps;
    
    protected FinallyMetaData finallyStep;
    
    protected Semaphore semaphore;
    
    protected int maxWaitCount = -1;
    protected long timeout = -1;
    protected long forceFreeTimeout = -1;
    
    protected int transactionType = SUPPORTS_VALUE;
    
    protected int transactionTimeout = -1;
    
    protected static final ThreadLocal<TransactionInfo> transaction = new ThreadLocal<TransactionInfo>(){
        protected TransactionInfo initialValue(){
            return new TransactionInfo();
        }
    };
    
    protected boolean isJournal = true;
    protected boolean isSuspend = true;
    protected boolean isStop = true;
    protected String[] overrideNames;
    
    /**
     * トランザクション制御を行うためにlookupしたTransactionManager。<p>
     */
    protected TransactionManager tranManager;
    
    protected DefaultBeanFlowFactoryService factory;
    
    public DefaultBeanFlow(DefaultBeanFlowFactoryService factory){
        this.factory = factory;
    }
    
    public void importXML(Element element) throws DeploymentException{
        flowName = MetaData.getUniqueAttribute(element, NAME_ATTRIBUTE);
        final String maxThreadsStr = MetaData.getOptionalAttribute(
            element,
            MAX_RUN_THREADS_ATTRIBUTE
        );
        if(maxThreadsStr != null){
            try{
                int maxThreads = Integer.parseInt(maxThreadsStr);
                semaphore = new DefaultSemaphoreService();
                try{
                    ((DefaultSemaphoreService)semaphore).create();
                    semaphore.setResourceCapacity(maxThreads);
                    ((DefaultSemaphoreService)semaphore).start();
                }catch(Exception e){
                    throw new DeploymentException(e);
                }
                semaphore.accept();
            }catch(NumberFormatException e){
                throw new DeploymentException("maxThreads is number " + maxThreadsStr);
            }
        }
        final String timeoutStr = MetaData.getOptionalAttribute(
            element,
            TIMEOUT_ATTRIBUTE
        );
        if(timeoutStr != null){
            try{
                timeout = Long.parseLong(timeoutStr);
            }catch(NumberFormatException e){
                throw new DeploymentException("timeout is number " + timeoutStr);
            }
        }
        final String maxWaitCountStr = MetaData.getOptionalAttribute(
            element,
            MAX_WAIT_THREADS_ATTRIBUTE
        );
        if(maxWaitCountStr != null){
            try{
                maxWaitCount = Integer.parseInt(maxWaitCountStr);
            }catch(NumberFormatException e){
                throw new DeploymentException("maxWaitThreads is number " + maxWaitCountStr);
            }
        }
        final String forceFreeTimeoutStr = MetaData.getOptionalAttribute(
            element,
            FORCE_FREE_TIMEOUT_ATTRIBUTE
        );
        if(forceFreeTimeoutStr != null){
            try{
                forceFreeTimeout = Long.parseLong(forceFreeTimeoutStr);
            }catch(NumberFormatException e){
                throw new DeploymentException("forceFreeTimeout is number " + forceFreeTimeoutStr);
            }
        }
        final String transactionStr = MetaData.getOptionalAttribute(
            element,
            TRANSACTION_ATTRIBUTE
        );
        if(transactionStr != null){
            if(REQUIRED.equals(transactionStr)){
                transactionType = REQUIRED_VALUE;
            }else if(REQUIRESNEW.equals(transactionStr)){
                transactionType = REQUIRESNEW_VALUE;
            }else if(SUPPORTS.equals(transactionStr)){
                transactionType = SUPPORTS_VALUE;
            }else if(MANDATORY.equals(transactionStr)){
                transactionType = MANDATORY_VALUE;
            }else if(NEVER.equals(transactionStr)){
                transactionType = NEVER_VALUE;
            }else if(NOT_SUPPORTED.equals(transactionStr)){
                transactionType = NOT_SUPPORTED_VALUE;
            }else{
                throw new DeploymentException("Invalid transaction : " + transactionStr);
            }
            
            if(transactionType != SUPPORTS_VALUE){
                try{
                    TransactionManagerFactory tranMngFactory = factory.getTransactionManagerFactory();
                    
                    tranManager = tranMngFactory.getTransactionManager();
                }catch(TransactionManagerFactoryException e){
                    throw new DeploymentException(e);
                }
            }
        }
        final String transactionTimeoutStr = MetaData.getOptionalAttribute(
            element,
            TRANTIMEOUT_ATTRIBUTE
        );
        if(transactionTimeoutStr != null){
            try{
                transactionTimeout = Integer.parseInt(transactionTimeoutStr);
            }catch(NumberFormatException e){
                throw new DeploymentException("trantimeout is number " + transactionTimeoutStr);
            }
        }
        final String journalStr = MetaData.getOptionalAttribute(
            element,
            JOURNAL_ATTRIBUTE
        );
        if(journalStr != null){
            isJournal = Boolean.valueOf(journalStr).booleanValue();
        }
        final String suspendStr = MetaData.getOptionalAttribute(
            element,
            SUSPEND_ATTRIBUTE
        );
        if(suspendStr != null){
            isSuspend = Boolean.valueOf(suspendStr).booleanValue();
        }
        final String stopStr = MetaData.getOptionalAttribute(
            element,
            STOP_ATTRIBUTE
        );
        if(stopStr != null){
            isStop = Boolean.valueOf(stopStr).booleanValue();
        }
        
        final Iterator<Element> aliasElements
             = MetaData.getChildrenByTagName(element, ALIAS_ELEMENT);
        while(aliasElements.hasNext()){
            aliasNames.add(
                MetaData.getUniqueAttribute(
                    aliasElements.next(),
                    NAME_ATTRIBUTE
                )
            );
        }
        
        final Iterator<Element> owElements = getChildrenByTagName(
            element,
            OVERRIDE_ELEMENT
        );
        List<String> overrideNameList = null;
        while(owElements.hasNext()){
            Element owElement = owElements.next();
            String overrideName = getUniqueAttribute(owElement, NAME_ATTRIBUTE);
            if(overrideNameList == null){
                overrideNameList = new ArrayList<String>();
            }
            overrideNameList.add(overrideName);
        }
        if(overrideNameList != null){
            overrideNames = overrideNameList
                .toArray(new String[overrideNameList.size()]);
        }
        
        final ServiceNameEditor editor = new ServiceNameEditor();
        final Iterator<Element> resourceElements
             = MetaData.getChildrenByTagName(element, RESOURCE_ELEMENT);
        while(resourceElements.hasNext()){
            final Element resourceElement
                 = resourceElements.next();
            final String name = MetaData.getUniqueAttribute(
                resourceElement,
                NAME_ATTRIBUTE
            );
            final String key = MetaData.getOptionalAttribute(
                resourceElement,
                KEY_ATTRIBUTE
            );
            final String serviceNameStr = MetaData.getUniqueAttribute(
                resourceElement,
                SERVICE_ATTRIBUTE
            );
            editor.setAsText(factory.replaceProperty(serviceNameStr));
            final ServiceName serviceName = (ServiceName)editor.getValue();
            final boolean isTranControl = MetaData.getOptionalBooleanAttribute(
                resourceElement,
                TRANCONTROL_ATTRIBUTE
            );
            final boolean isTranClose = MetaData.getOptionalBooleanAttribute(
                resourceElement,
                TRANCLOSE_ATTRIBUTE,
                true
            );
            ResourceInfo resourceInfo = new ResourceInfo();
            resourceInfo.name = name;
            resourceInfo.key = key;
            resourceInfo.serviceName = serviceName;
            resourceInfo.isTranControl = isTranControl;
            resourceInfo.isTranClose = isTranClose;
            if(resources == null){
                resources = new HashMap<String, ResourceInfo>();
            }
            resources.put(name, resourceInfo);
        }
        
        final Iterator<Element> catchElements
             = MetaData.getChildrenByTagName(element, CATCH_ELEMENT);
        while(catchElements.hasNext()){
            final Element catchElement
                 = catchElements.next();
            CatchMetaData step = new CatchMetaData(this);
            step.importXML(catchElement);
            if(catchSteps == null){
                catchSteps = new ArrayList<CatchMetaData>();
            }
            catchSteps.add(step);
        }
        
        final Element finallyElement = MetaData.getOptionalChild(
            element,
            FINALLY_ELEMENT
        );
        if(finallyElement != null){
            FinallyMetaData step = new FinallyMetaData(this);
            step.importXML(finallyElement);
            finallyStep = step;
        }
        
        Iterator<Element> children = MetaData.getChildrenWithoutTagName(
            element,
            new String[]{ALIAS_ELEMENT, OVERRIDE_ELEMENT, RESOURCE_ELEMENT, CATCH_ELEMENT, FINALLY_ELEMENT}
        );
        boolean isReturn = false;
        while(children.hasNext()){
            final Element currentElement = children.next();
            final String tagName = currentElement.getTagName();
            if(isReturn){
                throw new DeploymentException("Unreachable element : " + tagName);
            }
            Step stepObj = null;
            if(STEP_ELEMENT.equals(tagName)){
                StepMetaData step = new StepMetaData(this);
                step.importXML(currentElement);
                stepObj = step;
            }else if(CALL_FLOW_ELEMENT.equals(tagName)){
                CallFlowMetaData callFlowData = new CallFlowMetaData(this);
                callFlowData.importXML(currentElement);
                stepObj = callFlowData;
            }else if(REPLY_ELEMENT.equals(tagName)){
                GetAsynchReplyMetaData replyData = new GetAsynchReplyMetaData(this);
                replyData.importXML(currentElement);
                stepObj = replyData;
            }else if(SWITCH_ELEMENT.equals(tagName)){
                SwitchMetaData sw = new SwitchMetaData(this);
                sw.importXML(currentElement);
                stepObj = sw;
            }else if(IF_ELEMENT.equals(tagName)){
                IfMetaData ifData = new IfMetaData(this);
                ifData.importXML(currentElement);
                stepObj = ifData;
            }else if(FOR_ELEMENT.equals(tagName)){
                ForMetaData forData = new ForMetaData(this);
                forData.importXML(currentElement);
                stepObj = forData;
            }else if(WHILE_ELEMENT.equals(tagName)){
                WhileMetaData whileData = new WhileMetaData(this);
                whileData.importXML(currentElement);
                stepObj = whileData;
            }else if(RETURN_ELEMENT.equals(tagName)){
                ReturnMetaData returnData = new ReturnMetaData(this);
                returnData.importXML(currentElement);
                stepObj = returnData;
                isReturn = true;
            }else{
                throw new DeploymentException(
                    "Invalid child tag of flow tag : " + tagName
                );
            }
            if(stepObj != null){
                if(steps == null){
                    steps = new ArrayList<Step>();
                }
                steps.add(stepObj);
            }
        }
    }
    
    public String getFlowName(){
        return flowName;
    }
    
    public List<String> getAiliasFlowNames(){
        return aliasNames;
    }
    
    public String[] getOverrideFlowNames(){
        return overrideNames;
    }
    
    public BeanFlowMonitor createMonitor(){
        return new BeanFlowMonitorImpl(flowName);
    }
    
    @Override
    public Object execute(Object input) throws Exception {
        return execute(input, null);
    }
    
    @Override
    public Object executeAsynch(Object obj, BeanFlowMonitor monitor, boolean isReply, int maxAsynchWait) throws Exception{
        QueueHandlerContainer<BeanFlowAsynchContext> qhc = factory.getAsynchExecuteQueueHandlerContainer();
        if(qhc == null){
            throw new UnsupportedOperationException();
        }
        if(maxAsynchWait > 0 && qhc.size() > maxAsynchWait){
            throw new UnavailableFlowException(flowName);
        }
        BeanFlow flow = factory.createFlow(flowName);
        BeanFlowAsynchContext context = null;
        if(isReply){
            final DefaultQueueService<AsynchContext<Object,Object>> replyQueue = new DefaultQueueService<AsynchContext<Object,Object>>();
            replyQueue.create();
            replyQueue.start();
            context = new BeanFlowAsynchContext(flow, obj, monitor, replyQueue);
        }else{
            context = new BeanFlowAsynchContext(flow, obj, monitor);
        }
        if(factory.getContext() != null){
            context.putThreadContextAll(factory.getContext());
        }
        ((BeanFlowMonitorImpl)monitor).addAsynchContext(context);
        qhc.push(context);
        return context;
    }
    
    @Override
    public Object getAsynchReply(Object context, BeanFlowMonitor monitor, long timeout, boolean isCancel) throws BeanFlowAsynchTimeoutException, Exception{
        BeanFlowAsynchContext asynchContext = (BeanFlowAsynchContext)context;
        Queue<AsynchContext<Object,Object>> queue = asynchContext.getResponseQueue();
        if(queue == null){
            return null;
        }
        asynchContext = (BeanFlowAsynchContext)queue.get(timeout);
        if(asynchContext == null){
            if(isCancel){
                if(monitor != null){
                    monitor.cancel();
                    monitor.stop();
                }
                BeanFlow flow = ((BeanFlowAsynchContext)context).getBeanFlow();
                if(flow != null){
                    flow.end();
                }
            }
            throw new BeanFlowAsynchTimeoutException(flowName);
        }
        if(monitor != null){
            ((BeanFlowMonitorImpl)monitor).removeAsynchContext((BeanFlowAsynchContext)context);
        }
        BeanFlow flow = ((BeanFlowAsynchContext)context).getBeanFlow();
        if(flow != null){
            flow.end();
        }
        try{
            asynchContext.checkError();
        }catch(Throwable th){
            if(th instanceof Exception){
                throw (Exception)th;
            }else{
                throw (Error)th;
            }
        }
        return asynchContext.getOutput();
    }
    
    @Override
    public Object executeAsynch(Object obj, BeanFlowMonitor monitor, BeanFlowAsynchCallback callback, int maxAsynchWait) throws Exception{
        QueueHandlerContainer<BeanFlowAsynchContext> qhc = factory.getAsynchExecuteQueueHandlerContainer();
        if(qhc == null){
            throw new UnsupportedOperationException();
        }
        if(maxAsynchWait > 0 && qhc.size() > maxAsynchWait){
            throw new UnavailableFlowException(flowName);
        }
        BeanFlow flow = factory.createFlow(flowName);
        BeanFlowAsynchContext context = new BeanFlowAsynchContext(flow, obj, monitor, callback);
        if(factory.getContext() != null){
            context.putThreadContextAll(factory.getContext());
        }
        ((BeanFlowMonitorImpl)monitor).addAsynchContext(context);
        qhc.push(context);
        return context;
    }
    
    @Override
    public Object execute(Object input, BeanFlowMonitor monitor) throws Exception {
        if(monitor == null){
            monitor = new BeanFlowMonitorImpl();
        }
        if(monitor.getCurrentFlowName() == null){
            ((BeanFlowMonitorImpl)monitor).setFlowName(flowName);
        }
        ((BeanFlowMonitorImpl)monitor).setCurrentFlowName(flowName);
        ((BeanFlowMonitorImpl)monitor).setStartTime(System.currentTimeMillis());
        
        TransactionInfo info = transaction.get();
        final int transactionType = info.transactionType >= 0 ? info.transactionType : this.transactionType;
        final int transactionTimeout = info.transactionTimeout > 0 ? info.transactionTimeout : this.transactionTimeout;
        final TransactionManager tranManager = info.tranManager != null ? info.tranManager : this.tranManager;
        
        FlowContext flowContext = new FlowContext(input, resources, monitor);
        
        try{
            Transaction oldTransaction
                = tranManager == null ? null : tranManager.getTransaction();
            Transaction newTransaction = null;
            switch(transactionType){
            case REQUIRED_VALUE:
                if(transactionTimeout != -1 && oldTransaction == null){
                    tranManager.setTransactionTimeout(transactionTimeout);
                }
                if(oldTransaction == null){
                    tranManager.begin();
                    newTransaction = tranManager.getTransaction();
                }
                try{
                    flowContext = executeInternal(flowContext);
                    flowContext.commitResources(info, newTransaction != null, newTransaction != null ? tranManager : null);
                }catch(Exception e){
                    flowContext.rollbackResources(info, newTransaction != null, newTransaction != null ? tranManager : null);
                    throw e;
                }catch(Error err){
                    flowContext.rollbackResources(info, newTransaction != null, newTransaction != null ? tranManager : null);
                    throw err;
                }
                break;
            case REQUIRESNEW_VALUE:
                tranManager.suspend();
                try{
                    if(transactionTimeout != -1){
                        tranManager.setTransactionTimeout(transactionTimeout);
                    }
                    tranManager.begin();
                    newTransaction = tranManager.getTransaction();
                    try{
                        flowContext = executeInternal(flowContext);
                        flowContext.commitResources(info, true, tranManager);
                    }catch(Exception e){
                        flowContext.rollbackResources(info, true, tranManager);
                        throw e;
                    }catch(Error err){
                        flowContext.rollbackResources(info, true, tranManager);
                        throw err;
                    }
                }finally{
                    if(oldTransaction != null){
                        tranManager.resume(oldTransaction);
                    }
                }
                break;
            case NOT_SUPPORTED_VALUE:
                tranManager.suspend();
                try{
                    flowContext = executeInternal(flowContext);
                    flowContext.commitResources(info, false, null);
                }catch(Exception e){
                    flowContext.rollbackResources(info, false, null);
                    throw e;
                }catch(Error err){
                    flowContext.rollbackResources(info, false, null);
                    throw err;
                }finally{
                    if(oldTransaction != null){
                        tranManager.resume(oldTransaction);
                    }
                }
                break;
            case MANDATORY_VALUE:
                if(oldTransaction == null){
                    throw new BeanFlowTransactionException(
                        flowName,
                        "Require transaction.",
                        new TransactionRequiredException()
                    );
                }
                try{
                    flowContext = executeInternal(flowContext);
                    flowContext.commitResources(info, true, null);
                }catch(Exception e){
                    flowContext.rollbackResources(info, true, null);
                    throw e;
                }catch(Error err){
                    flowContext.rollbackResources(info, true, null);
                    throw err;
                }
                break;
            case NEVER_VALUE:
                if(oldTransaction != null){
                    throw new BeanFlowTransactionException(
                        flowName,
                        "Must not allow transaction."
                    );
                }
                try{
                    flowContext = executeInternal(flowContext);
                    flowContext.commitResources(info, false, null);
                }catch(Exception e){
                    flowContext.rollbackResources(info, false, null);
                    throw e;
                }catch(Error err){
                    flowContext.rollbackResources(info, false, null);
                    throw err;
                }
                break;
            case SUPPORTS_VALUE:
            default:
                try{
                    flowContext = executeInternal(flowContext);
                    flowContext.commitResources(info, oldTransaction != null, null);
                }catch(Exception e){
                    flowContext.rollbackResources(info, oldTransaction != null, null);
                    throw e;
                }catch(Error err){
                    flowContext.rollbackResources(info, oldTransaction != null, null);
                    throw err;
                }
            }
        }catch(HeuristicMixedException e){
            throw new BeanFlowTransactionException(flowName, e);
        }catch(HeuristicRollbackException e){
            throw new BeanFlowTransactionException(flowName, e);
        }catch(NotSupportedException e){
            throw new BeanFlowTransactionException(flowName, e);
        }catch(RollbackException e){
            throw new BeanFlowTransactionException(flowName, e);
        }catch(InvalidTransactionException e){
            throw new BeanFlowTransactionException(flowName, e);
        }catch(SystemException e){
            throw new BeanFlowTransactionException(flowName, e);
        }finally{
            flowContext.closeResources();
            ((BeanFlowMonitorImpl)monitor).end();
        }
        return flowContext.current == null ? null : flowContext.current.result;
    }
    
    public void end(){
    }
    
    protected FlowContext executeInternal(FlowContext context) throws Exception {
        Journal journal = isJournal ? factory.getJournal() : null;
        try{
            if(semaphore != null){
                if(!semaphore.getResource(timeout, maxWaitCount, forceFreeTimeout)){
                    throw new UnavailableFlowException(flowName);
                }
            }
            if(journal != null){
                journal.startJournal(
                    JOURNAL_KEY_FLOW,
                    factory.getEditorFinder()
                );
                final Context<Object, Object> ctx = factory.getContext();
                if(ctx != null){
                    final String requestId = (String)ctx .get(ThreadContextKey.REQUEST_ID);
                    if(requestId != null){
                        journal.setRequestId(requestId);
                    }
                }
                journal.addInfo(JOURNAL_KEY_FLOW_NAME, flowName);
                journal.addInfo(JOURNAL_KEY_FLOW_INPUT, context.input);
            }
            if(resources != null){
                for(Map.Entry<String, ResourceInfo> entry : resources.entrySet()){
                    String name = entry.getKey();
                    if(journal != null){
                        journal.addInfo(JOURNAL_KEY_RESOURCE, name);
                    }
                }
            }
            try{
                if(steps != null){
                    for(int i = 0, max = steps.size(); i < max; i++){
                        Step jobStep = steps.get(i);
                        StepContext stepContext
                             = jobStep.invokeStep(context);
                        if(stepContext == null){
                            break;
                        }
                    }
                }
            }catch(UnavailableFlowException e){
                throw e;
            }catch(BeanFlowMonitorStopException e){
                throw e;
            }catch(BeanFlowAsynchTimeoutException e){
                throw e;
            }catch(Exception e){
                Throwable th = e;
                if(e instanceof InvocationTargetException){
                    th = ((InvocationTargetException)e).getTargetException();
                }
                boolean isCatch = false;
                if(catchSteps != null && th instanceof Exception){
                    try{
                        for(int i = 0, imax = catchSteps.size(); i < imax; i++){
                            final CatchMetaData catchStep
                                 = catchSteps.get(i);
                            if(catchStep.isMatch(context, (Exception)th)){
                                catchStep.invokeStep(context);
                                isCatch = true;
                                break;
                            }
                        }
                    }catch(Throwable th2){
                        throwException(th2);
                    }
                }
                if(!isCatch){
                    throwException(th);
                }
            }catch(Error e){
                throw e;
            }finally{
                if(finallyStep != null){
                    try{
                        finallyStep.invokeStep(context);
                    }catch(Throwable th){
                        throwException(th);
                    }
                }
            }
        }catch(Throwable th){
            if(journal != null){
                Throwable th2 = th;
                if(th2 instanceof InvocationTargetException){
                    th2 = ((InvocationTargetException)th2).getTargetException();
                }
                journal.addInfo(JOURNAL_KEY_FLOW_EXCEPTION, th2);
            }
            throwException(th);
        }finally{
            if(journal != null){
                journal.addInfo(
                    JOURNAL_KEY_FLOW_OUTPUT,
                    context == null ? null
                        : (context.current == null
                            ? null : context.current.result)
                );
                journal.endJournal();
            }
            if(semaphore != null){
                semaphore.freeResource();
            }
        }
        return context;
    }
    
    protected void throwException(Throwable th) throws Exception{
        if(th instanceof InvocationTargetException){
            th = ((InvocationTargetException)th).getTargetException();
        }
        if(th instanceof UnavailableFlowException){
            throw (UnavailableFlowException)th;
        }else if(th instanceof Exception){
            throw (Exception)th;
        }else{
            throw (Error)th;
        }
    }
    
    protected static boolean isNarrowCast(Class<?> from, Class<?> to){
        if(from == null || to == null
            || !Number.class.isAssignableFrom(from)
            || (!Number.class.isAssignableFrom(to)
                    && (!to.isPrimitive() || to.equals(Boolean.TYPE)))
            || from.equals(to)
        ){
            return false;
        }
        if(Byte.class.equals(from)
            && !Byte.TYPE.equals(to)
        ){
            return true;
        }else if(Short.class.equals(from)
            && !Short.TYPE.equals(to)
            && (Byte.TYPE.equals(to)
                || Byte.class.equals(to))
        ){
            return true;
        }else if(Integer.class.equals(from)
            && !Integer.TYPE.equals(to)
            && (Short.TYPE.equals(to)
                || Short.class.equals(to)
                || Byte.TYPE.equals(to)
                || Byte.class.equals(to))
        ){
            return true;
        }else if(Long.class.equals(from)
            && !Long.TYPE.equals(to)
            && (Integer.TYPE.equals(to)
                || Integer.class.equals(to)
                || Short.TYPE.equals(to)
                || Short.class.equals(to)
                || Byte.TYPE.equals(to)
                || Byte.class.equals(to))
        ){
            return true;
        }else if(Float.class.equals(from)
            && !Float.TYPE.equals(to)
            && (Long.TYPE.equals(to)
                || Long.class.equals(to)
                || Integer.TYPE.equals(to)
                || Integer.class.equals(to)
                || Short.TYPE.equals(to)
                || Short.class.equals(to)
                || Byte.TYPE.equals(to)
                || Byte.class.equals(to))
        ){
            return true;
        }else if(Double.class.equals(from)
            && !Double.TYPE.equals(to)
            && (Float.TYPE.equals(to)
                || Float.class.equals(to)
                || Long.TYPE.equals(to)
                || Long.class.equals(to)
                || Integer.TYPE.equals(to)
                || Integer.class.equals(to)
                || Short.TYPE.equals(to)
                || Short.class.equals(to)
                || Byte.TYPE.equals(to)
                || Byte.class.equals(to))
        ){
            return true;
        }else if(BigInteger.class.equals(from)
            && !Double.TYPE.equals(to)
            && (Double.class.equals(to)
                || Float.TYPE.equals(to)
                || Float.class.equals(to)
                || Long.TYPE.equals(to)
                || Long.class.equals(to)
                || Integer.TYPE.equals(to)
                || Integer.class.equals(to)
                || Short.TYPE.equals(to)
                || Short.class.equals(to)
                || Byte.TYPE.equals(to)
                || Byte.class.equals(to))
        ){
            return true;
        }else if(BigDecimal.class.equals(from)
            && !BigInteger.class.equals(to)
            && (Double.TYPE.equals(to)
                || Double.class.equals(to)
                || Float.TYPE.equals(to)
                || Float.class.equals(to)
                || Long.TYPE.equals(to)
                || Long.class.equals(to)
                || Integer.TYPE.equals(to)
                || Integer.class.equals(to)
                || Short.TYPE.equals(to)
                || Short.class.equals(to)
                || Byte.TYPE.equals(to)
                || Byte.class.equals(to))
        ){
            return true;
        }
        return false;
    }
    
    protected static Number castPrimitiveWrapper(Class<?> clazz, Number val){
        if(Byte.TYPE.equals(clazz) || Byte.class.equals(clazz)){
            return new Byte(val.byteValue());
        }else if(Short.TYPE.equals(clazz) || Short.class.equals(clazz)){
            return new Short(val.shortValue());
        }else if(Integer.TYPE.equals(clazz) || Integer.class.equals(clazz)){
            return new Integer(val.intValue());
        }else if(Long.TYPE.equals(clazz) || Long.class.equals(clazz)){
            return new Long(val.longValue());
        }else if(Float.TYPE.equals(clazz) || Float.class.equals(clazz)){
            return new Float(val.floatValue());
        }else if(Double.TYPE.equals(clazz) || Double.class.equals(clazz)){
            return new Double(val.doubleValue());
        }else if(BigInteger.class.equals(clazz)){
            return BigInteger.valueOf(val.longValue());
        }else if(BigDecimal.class.equals(clazz)){
            if(val instanceof BigInteger){
                return new BigDecimal((BigInteger)val);
            }else{
                return new BigDecimal(val.doubleValue());
            }
        }else{
            return val;
        }
    }
    
    protected Journal getJournal(MetaData data){
        if(!isJournal){
            return null;
        }
        if(data instanceof Journaling
            && !((Journaling)data).isJournal()){
            return null;
        }
        MetaData parent = data;
        while((parent = parent.getParent()) != null){
            if(parent instanceof Journaling
                && !((Journaling)parent).isJournal()){
                return null;
            }
        }
        return factory.getJournal();
    }
    
    protected interface Step{
        public StepContext invokeStep(FlowContext context) throws Exception;
    }
    
    protected interface Journaling{
        public boolean isJournal();
    }
    
    protected class StepMetaData extends MetaData implements Step, Journaling{
        
        private static final long serialVersionUID = 3152910895585634923L;
        
        protected String name;
        protected MetaData targetData;
        protected List<MetaData> childDatas;
        protected InterpreterMetaData interpreterData;
        protected Object resultData;
        
        protected Semaphore semaphore;
        protected long timeout = -1;
        protected long forceFreeTimeout = -1;
        protected int maxWaitCount = -1;
        protected boolean isJournal = true;
        protected boolean isTargetJournal = true;
        protected boolean isResultJournal = true;
        
        protected List<CatchMetaData> catchSteps;
        protected FinallyMetaData finallyStep;
        
        public StepMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            name = getOptionalAttribute(element, NAME_ATTRIBUTE);
            final String maxThreadsStr = MetaData.getOptionalAttribute(
                element,
                MAX_RUN_THREADS_ATTRIBUTE
            );
            if(maxThreadsStr != null){
                try{
                    int maxThreads = Integer.parseInt(maxThreadsStr);
                    semaphore = new DefaultSemaphoreService();
                    try{
                        ((DefaultSemaphoreService)semaphore).create();
                        semaphore.setResourceCapacity(maxThreads);
                        ((DefaultSemaphoreService)semaphore).start();
                    }catch(Exception e){
                        throw new DeploymentException(e);
                    }
                    semaphore.accept();
                }catch(NumberFormatException e){
                    throw new DeploymentException("maxThreads is number " + maxThreadsStr);
                }
            }
            final String timeoutStr = MetaData.getOptionalAttribute(
                element,
                TIMEOUT_ATTRIBUTE
            );
            if(timeoutStr != null){
                try{
                    timeout = Long.parseLong(timeoutStr);
                }catch(NumberFormatException e){
                    throw new DeploymentException("timeout is number " + timeoutStr);
                }
            }
            final String maxWaitCountStr = MetaData.getOptionalAttribute(
                element,
                MAX_WAIT_THREADS_ATTRIBUTE
            );
            if(maxWaitCountStr != null){
                try{
                    maxWaitCount = Integer.parseInt(maxWaitCountStr);
                }catch(NumberFormatException e){
                    throw new DeploymentException("maxWaitThreads is number " + maxWaitCountStr);
                }
            }
            final String forceFreeTimeoutStr = MetaData.getOptionalAttribute(
                element,
                FORCE_FREE_TIMEOUT_ATTRIBUTE
            );
            if(forceFreeTimeoutStr != null){
                try{
                    forceFreeTimeout = Long.parseLong(forceFreeTimeoutStr);
                }catch(NumberFormatException e){
                    throw new DeploymentException("forceFreeTimeout is number " + forceFreeTimeoutStr);
                }
            }
            String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            final Element targetElement = getOptionalChild(
                element,
                TARGET_ELEMENT
            );
            if(targetElement == null){
                targetData = new InputMetaData();
            }else{
                journalStr = MetaData.getOptionalAttribute(
                    targetElement,
                    JOURNAL_ATTRIBUTE
                );
                if(journalStr != null){
                    isTargetJournal = Boolean.valueOf(journalStr).booleanValue();
                }
                
                final Element childElement = getUniqueChild(
                    targetElement
                );
                final String tagName = childElement.getTagName();
                if(INPUT_ELEMENT.equals(tagName)){
                    targetData = new InputMetaData();
                    targetData.importXML(childElement);
                }else if(ObjectMetaData.OBJECT_TAG_NAME.equals(tagName)){
                    targetData = new ObjectMetaData(this);
                    targetData.importXML(childElement);
                }else if(ServiceRefMetaData.SERIVCE_REF_TAG_NAME.equals(tagName)){
                    targetData = new ServiceRefMetaData(this);
                    targetData.importXML(childElement);
                }else if(StaticInvokeMetaData.STATIC_INVOKE_TAG_NAME.equals(tagName)){
                    targetData = new StaticInvokeMetaData(this);
                    targetData.importXML(childElement);
                }else if(StaticFieldRefMetaData.STATIC_FIELD_REF_TAG_NAME.equals(tagName)){
                    targetData = new StaticFieldRefMetaData(this);
                    targetData.importXML(childElement);
                }else if(RESOURCE_REF_ELEMENT.equals(tagName)){
                    targetData = new ResourceRefMetaData();
                    targetData.importXML(childElement);
                }else if(STEP_REF_ELEMENT.equals(tagName)){
                    targetData = new StepRefMetaData();
                    targetData.importXML(childElement);
                }else if(VAR_ELEMENT.equals(tagName)){
                    targetData = new VarMetaData();
                    targetData.importXML(childElement);
                }else if(EXPRESSION_ELEMENT.equals(tagName)){
                    targetData = new ExpressionMetaData();
                    targetData.importXML(childElement);
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of target tag : " + tagName
                    );
                }
            }
            
            final Iterator<Element> catchElements
                 = MetaData.getChildrenByTagName(element, CATCH_ELEMENT);
            while(catchElements.hasNext()){
                final Element catchElement = catchElements.next();
                CatchMetaData step = new CatchMetaData(this);
                step.importXML(catchElement);
                if(catchSteps == null){
                    catchSteps = new ArrayList<CatchMetaData>();
                }
                catchSteps.add(step);
            }
            
            final Element finallyElement = MetaData.getOptionalChild(
                element,
                FINALLY_ELEMENT
            );
            if(finallyElement != null){
                FinallyMetaData step = new FinallyMetaData(this);
                step.importXML(finallyElement);
                finallyStep = step;
            }
            
            final Iterator<Element> children = getChildrenWithoutTagName(
                element,
                new String[]{TARGET_ELEMENT, RESULT_ELEMENT, CATCH_ELEMENT, FINALLY_ELEMENT, INTERPRETER_ELEMENT}
            );
            while(children.hasNext()){
                if(childDatas == null){
                    childDatas = new ArrayList<MetaData>();
                }
                final Element childElement = children.next();
                final String tagName = childElement.getTagName();
                MetaData childData = null;
                if(AttributeMetaData.ATTRIBUTE_TAG_NAME.equals(tagName)){
                    childData = new AttributeMetaData(StepMetaData.this);
                    childData.importXML(childElement);
                }else if(FieldMetaData.FIELD_TAG_NAME.equals(tagName)){
                    childData = new FieldMetaData(StepMetaData.this);
                    childData.importXML(childElement);
                }else if(InvokeMetaData.INVOKE_TAG_NAME.equals(tagName)){
                    childData = new InvokeMetaData(this);
                    childData.importXML(childElement);
                }else if(StaticInvokeMetaData.STATIC_INVOKE_TAG_NAME.equals(tagName)){
                    childData = new StaticInvokeMetaData(this);
                    childData.importXML(childElement);
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of step tag : " + tagName
                    );
                }
                childDatas.add(childData);
            }
            
            final Element interpreterElement = getOptionalChild(
                element,
                INTERPRETER_ELEMENT
            );
            if(interpreterElement != null){
                interpreterData = new InterpreterMetaData(StepMetaData.this);
                interpreterData.importXML(interpreterElement);
            }
            
            final Element resultElement = getOptionalChild(
                element,
                RESULT_ELEMENT
            );
            if(resultElement != null){
                journalStr = MetaData.getOptionalAttribute(
                    resultElement,
                    JOURNAL_ATTRIBUTE
                );
                if(journalStr != null){
                    isResultJournal = Boolean.valueOf(journalStr).booleanValue();
                }
                final Element retElement = getOptionalChild(
                    resultElement
                );
                if(retElement == null){
                    resultData = getElementContent(resultElement);
                    if(resultData == null){
                        resultData = "";
                    }
                    if(resultData != null){
                        final PropertyEditor editor
                             = factory.findEditor(String.class);
                        if(editor != null){
                            editor.setAsText(
                                factory.replaceProperty((String)resultData)
                            );
                            resultData = editor.getValue();
                        }
                    }
                }else{
                    final String tagName = retElement.getTagName();
                    if(AttributeMetaData.ATTRIBUTE_TAG_NAME.equals(tagName)){
                        resultData = new AttributeMetaData(StepMetaData.this);
                        ((MetaData)resultData).importXML(retElement);
                    }else if(FieldMetaData.FIELD_TAG_NAME.equals(tagName)){
                        resultData = new FieldMetaData(StepMetaData.this);
                        ((MetaData)resultData).importXML(retElement);
                    }else if(InvokeMetaData.INVOKE_TAG_NAME.equals(tagName)){
                        resultData = new InvokeMetaData(this);
                        ((MetaData)resultData).importXML(retElement);
                    }else if(StaticInvokeMetaData.STATIC_INVOKE_TAG_NAME.equals(tagName)){
                        resultData = new StaticInvokeMetaData(this);
                        ((MetaData)resultData).importXML(retElement);
                    }else if(StaticFieldRefMetaData.STATIC_FIELD_REF_TAG_NAME.equals(tagName)){
                        resultData = new StaticFieldRefMetaData(this);
                        ((MetaData)resultData).importXML(retElement);
                    }else if(THIS_ELEMENT.equals(tagName)){
                        resultData = new ThisMetaData();
                        ((MetaData)resultData).importXML(retElement);
                    }else if(INPUT_ELEMENT.equals(tagName)){
                        resultData = new InputMetaData();
                        ((MetaData)resultData).importXML(retElement);
                    }else if(ObjectMetaData.OBJECT_TAG_NAME.equals(tagName)){
                        resultData = new ObjectMetaData(this);
                        ((MetaData)resultData).importXML(retElement);
                    }else if(EXPRESSION_ELEMENT.equals(tagName)){
                        resultData = new ExpressionMetaData();
                        ((MetaData)resultData).importXML(retElement);
                    }else if(STEP_REF_ELEMENT.equals(tagName)){
                        resultData = new StepRefMetaData();
                        ((MetaData)resultData).importXML(retElement);
                    }else if(VAR_ELEMENT.equals(tagName)){
                        resultData = new VarMetaData();
                        ((MetaData)resultData).importXML(retElement);
                    }else{
                        throw new DeploymentException(
                            "Invalid child tag of result tag : " + tagName
                        );
                    }
                }
            }
        }
        
        public StepContext invokeStep(FlowContext context) throws Exception{
            StepContext stepContext = new StepContext();
            Journal journal = getJournal(StepMetaData.this);
            try{
                ((BeanFlowMonitorImpl)context.monitor).setCurrentStepName(name);
                if(journal != null){
                    journal.startJournal(
                        JOURNAL_KEY_STEP,
                        factory.getEditorFinder()
                    );
                    journal.addInfo(JOURNAL_KEY_STEP_NAME, name);
                }
                if(isSuspend){
                    ((BeanFlowMonitorImpl)context.monitor).checkSuspend();
                }
                if(isStop){
                    if(((BeanFlowMonitorImpl)context.monitor).checkStop()){
                        throw new BeanFlowMonitorStopException(flowName, name);
                    }
                }
                if(semaphore != null){
                    if(!semaphore.getResource(timeout, maxWaitCount, forceFreeTimeout)){
                        throw new UnavailableFlowException(
                            flowName,
                            name,
                            (String)null
                        );
                    }
                }
                context.current = stepContext;
                final Object target = ((ReturnValue)targetData).getValue(context);
                stepContext.target = target;
                if(journal != null && isTargetJournal){
                    journal.addInfo(
                        JOURNAL_KEY_STEP_TARGET,
                        stepContext.target
                    );
                }
                if(childDatas != null){
                    final Iterator<MetaData> children = childDatas.iterator();
                    while(children.hasNext()){
                        MetaData child = children.next();
                        if(child instanceof SetValue){
                            ((SetValue)child).setValue(context);
                        }else if(child instanceof InvokeMetaData){
                            ((InvokeMetaData)child).getValue(context);
                        }else if(child instanceof StaticInvokeMetaData){
                            ((StaticInvokeMetaData)child).getValue(context);
                        }
                    }
                }
                Object interpreterRet = null;
                if(interpreterData != null){
                    interpreterRet = interpreterData.getValue(context);
                }
                if(resultData == null){
                    stepContext.result = interpreterRet;
                }else{
                    if(resultData instanceof ReturnValue){
                        stepContext.result = ((ReturnValue)resultData)
                            .getValue(context);
                    }else{
                        stepContext.result = resultData == null
                             ? null : resultData.toString();
                    }
                }
                if(name != null){
                    context.put(name, stepContext);
                }
                if(journal != null && isResultJournal){
                    journal.addInfo(
                        JOURNAL_KEY_STEP_RESULT,
                        stepContext.result
                    );
                }
            }catch(Exception e){
                Throwable th = e;
                if(th instanceof InvocationTargetException){
                    th = ((InvocationTargetException)th).getTargetException();
                }
                if(journal != null){
                    journal.addInfo(JOURNAL_KEY_STEP_EXCEPTION, th);
                }
                boolean isCatch = false;
                if(catchSteps != null && th instanceof Exception){
                    for(int i = 0, imax = catchSteps.size(); i < imax; i++){
                        final CatchMetaData catchStep
                             = catchSteps.get(i);
                        if(catchStep.isMatch(context, (Exception)th)){
                            catchStep.invokeStep(context);
                            isCatch = true;
                            break;
                        }
                    }
                }
                if(!isCatch){
                    throwException(th);
                }
            }catch(Error e){
                if(journal != null){
                    journal.addInfo(JOURNAL_KEY_STEP_EXCEPTION, e);
                }
                throw e;
            }finally{
                try{
                    if(finallyStep != null){
                        finallyStep.invokeStep(context);
                    }
                }finally{
                    if(semaphore != null){
                        semaphore.freeResource();
                    }
                    if(journal != null){
                        journal.endJournal();
                    }
                }
            }
            return stepContext;
        }
    }
    
    protected interface ReturnValue{
        public Object getValue(FlowContext context) throws Exception;
    }
    
    protected interface SetValue{
        public void setValue(FlowContext context) throws Exception;
    }
    
    protected static class InputMetaData extends MetaData implements ReturnValue{
        
        private static final long serialVersionUID = -3006340756672859712L;
        
        protected Property property;
        
        protected boolean nullCheck = false;
        
        public InputMetaData(){}
        
        public void importXML(Element element) throws DeploymentException{
            final String nullCheckAttribute = getOptionalAttribute(
                element,
                NULLCHECK_ATTRIBUTE
            );
            if(nullCheckAttribute != null){
                nullCheck = Boolean.valueOf(nullCheckAttribute).booleanValue();
            }
            String val = getElementContent(element);
            if(val != null && val.length() != 0){
                try{
                    property = PropertyFactory.createProperty(val);
                    if(!nullCheck){
                        property.setIgnoreNullProperty(true);
                    }
                }catch(Exception e){
                    throw new DeploymentException(e);
                }
            }
        }
        
        public Object getValue(FlowContext context) throws Exception{
            Object input = context.input;
            if(input == null){
                return null;
            }
            if(property == null){
                return input;
            }
            try{
                return property.getProperty(input);
            }catch(NullIndexPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(NullKeyPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(NullNestPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(InvocationTargetException e){
                final Throwable th = e.getCause();
                if(th == null){
                    throw e;
                }
                if(th instanceof Exception){
                    throw (Exception)th;
                }else if(th instanceof Error){
                    throw (Error)th;
                }else{
                    throw e;
                }
            }
            return null;
        }
    }
    
    protected class ObjectMetaData extends jp.ossc.nimbus.core.ObjectMetaData
     implements ReturnValue, Journaling{
        
        private static final long serialVersionUID = -7179571475429331574L;
        protected boolean isJournal = true;
        
        public ObjectMetaData(MetaData parent){
            super(null, parent, null);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            
            if(!element.getTagName().equals(OBJECT_TAG_NAME)){
                throw new DeploymentException(
                    "Tag must be " + OBJECT_TAG_NAME + " : "
                     + element.getTagName()
                );
            }
            
            code = getUniqueAttribute(element, CODE_ATTRIBUTE_NAME);
            final String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            
            final Element constElement = getOptionalChild(
                element,
                ConstructorMetaData.CONSTRUCTOR_TAG_NAME
            );
            if(constElement != null){
                final ConstructorMetaData constData
                     = new ConstructorMetaData(this);
                constData.importXML(constElement);
                constructor = constData;
            }
            final Iterator<Element> fieldElements = getChildrenByTagName(
                element,
                FieldMetaData.FIELD_TAG_NAME
            );
            while(fieldElements.hasNext()){
                final FieldMetaData fieldData
                     = new FieldMetaData(this);
                fieldData.importXML(fieldElements.next());
                addField(fieldData);
            }
            
            final Iterator<Element> attributeElements = getChildrenByTagName(
                element,
                AttributeMetaData.ATTRIBUTE_TAG_NAME
            );
            while(attributeElements.hasNext()){
                final AttributeMetaData attributeData
                     = new AttributeMetaData(this);
                attributeData.importXML(attributeElements.next());
                addAttribute(attributeData);
            }
            
            final Iterator<Element> invokeElements = getChildrenByTagName(
                element,
                InvokeMetaData.INVOKE_TAG_NAME
            );
            while(invokeElements.hasNext()){
                final InvokeMetaData invokeData = new InvokeMetaData(this);
                invokeData.importXML(invokeElements.next());
                addInvoke(invokeData);
            }
        }
        
        public Class<?> getObjectClass() throws Exception{
            return Utility.convertStringToClass(code);
        }
        
        public Object getValue(FlowContext context) throws Exception{
            final Journal journal = getJournal(ObjectMetaData.this);
            if(journal != null){
                journal.startJournal(
                    JOURNAL_KEY_OBJECT,
                    factory.getEditorFinder()
                );
            }
            try{
                Object obj = null;
                if(constructor == null){
                    final Class<?> clazz = getObjectClass();
                    if(journal != null){
                        journal.addInfo(JOURNAL_KEY_CLASS, clazz);
                    }
                    if(clazz.isArray()){
                        final Class<?> elementType = clazz.getComponentType();
                        obj = Array.newInstance(elementType, 0);
                    }else{
                        obj = clazz.newInstance();
                    }
                }else{
                    obj = ((ReturnValue)constructor).getValue(context);
                }
                
                final Iterator<jp.ossc.nimbus.core.FieldMetaData> fields = getFields().iterator();
                while(fields.hasNext()){
                    FieldMetaData field = (FieldMetaData)fields.next();
                    field.setValue(obj, context);
                }
                
                final Iterator<jp.ossc.nimbus.core.AttributeMetaData> attributes = getAttributes().iterator();
                while(attributes.hasNext()){
                    AttributeMetaData attribute = (AttributeMetaData)attributes.next();
                    attribute.setValue(obj, context);
                }
                
                final Iterator<jp.ossc.nimbus.core.InvokeMetaData> invokes = getInvokes().iterator();
                while(invokes.hasNext()){
                    MetaData invokeData = invokes.next();
                    if(invokeData instanceof InvokeMetaData){
                        InvokeMetaData invoke = (InvokeMetaData)invokeData;
                        invoke.getValue(obj, context);
                    }else if(invokeData instanceof StaticInvokeMetaData){
                        StaticInvokeMetaData invoke = (StaticInvokeMetaData)invokeData;
                        invoke.getValue(context);
                    }
                }
                if(journal != null){
                    journal.addInfo(
                        JOURNAL_KEY_INSTANCE,
                        obj
                    );
                }
                return obj;
            }finally{
                if(journal != null){
                    journal.endJournal();
                }
            }
        }
    }
    
    protected class ConstructorMetaData
     extends jp.ossc.nimbus.core.ConstructorMetaData
     implements ReturnValue{
        
        private static final long serialVersionUID = -1958316450069744646L;
        
        public ConstructorMetaData(ObjectMetaData parent){
            super(parent);
        }
        
        public void importXML(Element element) throws DeploymentException{
            
            if(!element.getTagName().equals(CONSTRUCTOR_TAG_NAME)){
                throw new DeploymentException(
                    "Tag must be " + CONSTRUCTOR_TAG_NAME + " : "
                     + element.getTagName()
                );
                
            }
            
            final Element staticInvokeElement = getOptionalChild(
                element,
                StaticInvokeMetaData.STATIC_INVOKE_TAG_NAME
            );
            if(staticInvokeElement != null){
                final StaticInvokeMetaData staticInvokeData
                     = new StaticInvokeMetaData(this);
                staticInvokeData.importXML(staticInvokeElement);
                staticInvoke = staticInvokeData;
                return;
            }
            
            final Element staticFieldRefElement = getOptionalChild(
                element,
                StaticFieldRefMetaData.STATIC_FIELD_REF_TAG_NAME
            );
            if(staticFieldRefElement != null){
                final StaticFieldRefMetaData staticFieldRefData
                     = new StaticFieldRefMetaData(this);
                staticFieldRefData.importXML(staticFieldRefElement);
                staticFieldRef = staticFieldRefData;
                return;
            }
            
            final Iterator<Element> argElements = getChildrenByTagName(
                element,
                ArgumentMetaData.ARGUMENT_TAG_NAME
            );
            ObjectMetaData objectData = (ObjectMetaData)getParent();
            Class<?> clazz = null;
            try{
                clazz = objectData.getObjectClass();
            }catch(Exception e){
            }
            while(argElements.hasNext()){
                final ArgumentMetaData argData
                     = new ArgumentMetaData(this, objectData);
                argData.importXML(argElements.next());
                if(clazz != null && !clazz.isArray() && argData.isNullValue()){
                    Class<?> typeClass = null;
                    try{
                        typeClass = argData.getTypeClass();
                        if(typeClass == null){
                            throw new DeploymentException(
                                "Type is unknown : " + argData
                            );
                        }
                    }catch(Exception e){}
                }
                addArgument(argData);
            }
        }
        
        public Object getValue(FlowContext context) throws Exception{
            if(getStaticFieldRef() != null){
                return ((StaticFieldRefMetaData)getStaticFieldRef())
                    .getValue(context);
            }else if(getStaticInvoke() != null){
                return ((StaticInvokeMetaData)getStaticInvoke())
                    .getValue(context);
            }
            ObjectMetaData objectData = (ObjectMetaData)getParent();
            final Class<?> clazz = objectData.getObjectClass();
            if(clazz.isArray()){
                final Class<?> elementType = clazz.getComponentType();
                final Collection<jp.ossc.nimbus.core.ArgumentMetaData> argCollection = getArguments();
                Object argVals = Array.newInstance(
                    elementType,
                    argCollection.size()
                );
                final Iterator<jp.ossc.nimbus.core.ArgumentMetaData> args = argCollection.iterator();
                int i = 0;
                while(args.hasNext()){
                    final ArgumentMetaData argData
                         = (ArgumentMetaData)args.next();
                    Array.set(argVals, i, argData.getValue(context));
                    i++;
                }
                return argVals;
            }else{
                List<Class<?>> paramTypes = new ArrayList<Class<?>>();
                List<Object> params = new ArrayList<Object>();
                final Iterator<jp.ossc.nimbus.core.ArgumentMetaData> argDatas = getArguments().iterator();
                while(argDatas.hasNext()){
                    ArgumentMetaData argData = (ArgumentMetaData)argDatas.next();
                    Object arg = argData.getValue(context);
                    Class<?> typeClass = argData.getTypeClass();
                    if(typeClass == null){
                        if(arg == null){
                            throw new DeploymentException(
                                "Type is unknown : " + argData
                            );
                        }
                        typeClass = arg.getClass();
                    }
                    params.add(arg);
                    paramTypes.add(typeClass);
                }
                
                final Constructor<?> c = clazz.getConstructor(
                    paramTypes.toArray(new Class[paramTypes.size()])
                );
                return c.newInstance(params.toArray());
            }
        }
     }
    
    protected class ArgumentMetaData
     extends jp.ossc.nimbus.core.ArgumentMetaData
     implements ReturnValue{
        
        private static final long serialVersionUID = 3844640387973215685L;
        protected boolean isNarrowCast;
        
        public ArgumentMetaData(MetaData parent, ObjectMetaData objData){
            super(parent, objData);
        }
        
        public Class<?> getTypeClass() throws Exception{
            if(type != null){
                return Utility.convertStringToClass(type);
            }
            if(valueType != null){
                return Utility.convertStringToClass(valueType);
            }
            if(value instanceof String){
                return String.class;
            }
            return null;
        }
        
        public Class<?> getValueTypeClass() throws Exception{
            if(valueType != null){
                return Utility.convertStringToClass(valueType);
            }
            if(type != null){
                return Utility.convertStringToClass(type);
            }
            if(value instanceof String){
                return String.class;
            }
            return null;
        }
        
        public void importXML(Element element) throws DeploymentException{
            
            if(!element.getTagName().equals(ARGUMENT_TAG_NAME)){
                throw new DeploymentException(
                    "Tag must be " + ARGUMENT_TAG_NAME + " : "
                     + element.getTagName()
                );
            }
            
            type = getOptionalAttribute(element, TYPE_ATTRIBUTE_NAME);
            valueType = getOptionalAttribute(element, VALUE_TYPE_ATTRIBUTE_NAME);
            isNullValue = getOptionalBooleanAttribute(
                element,
                TYPE_ATTRIBUTE_NULL_VALUE,
                false
            );
            isNarrowCast = getOptionalBooleanAttribute(
                element,
                NARROWCAST_ATTRIBUTE,
                false
            );
            
            final Element serviceRefElement = getOptionalChild(
                element,
                ServiceRefMetaData.SERIVCE_REF_TAG_NAME
            );
            if(serviceRefElement != null){
                final ServiceRefMetaData serviceRefData
                     = new ServiceRefMetaData(this);
                serviceRefData.importXML(serviceRefElement);
                value = serviceRefData;
                return;
            }
            
            final Element staticInvokeElement = getOptionalChild(
                element,
                StaticInvokeMetaData.STATIC_INVOKE_TAG_NAME
            );
            if(staticInvokeElement != null){
                final StaticInvokeMetaData staticInvokeData
                     = new StaticInvokeMetaData(this);
                staticInvokeData.importXML(staticInvokeElement);
                value = staticInvokeData;
                return;
            }
            
            final Element staticFieldRefElement = getOptionalChild(
                element,
                StaticFieldRefMetaData.STATIC_FIELD_REF_TAG_NAME
            );
            if(staticFieldRefElement != null){
                final StaticFieldRefMetaData staticFieldRefData
                     = new StaticFieldRefMetaData(this);
                staticFieldRefData.importXML(staticFieldRefElement);
                value = staticFieldRefData;
                return;
            }
            
            final Element objectElement = getOptionalChild(
                element,
                ObjectMetaData.OBJECT_TAG_NAME
            );
            if(objectElement != null){
                final ObjectMetaData objectData = new ObjectMetaData(this);
                objectData.importXML(objectElement);
                value = objectData;
                return;
            }
            
            final Element inputElement = getOptionalChild(
                element,
                INPUT_ELEMENT
            );
            if(inputElement != null){
                final InputMetaData inputData = new InputMetaData();
                inputData.importXML(inputElement);
                value = inputData;
                return;
            }
            
            final Element stepRefElement = getOptionalChild(
                element,
                STEP_REF_ELEMENT
            );
            if(stepRefElement != null){
                final StepRefMetaData stepRefData = new StepRefMetaData();
                stepRefData.importXML(stepRefElement);
                value = stepRefData;
                return;
            }
            
            final Element thisElement = getOptionalChild(
                element,
                THIS_ELEMENT
            );
            if(thisElement != null){
                final ThisMetaData thisData = new ThisMetaData();
                thisData.importXML(thisElement);
                value = thisData;
                return;
            }
            
            final Element resourceRefElement = getOptionalChild(
                element,
                RESOURCE_REF_ELEMENT
            );
            if(resourceRefElement != null){
                final ResourceRefMetaData resourceRefData = new ResourceRefMetaData();
                resourceRefData.importXML(resourceRefElement);
                value = resourceRefData;
                return;
            }
            
            final Element varElement = getOptionalChild(
                element,
                VAR_ELEMENT
            );
            if(varElement != null){
                final VarMetaData varData = new VarMetaData();
                varData.importXML(varElement);
                value = varData;
                return;
            }
            
            final Element expElement = getOptionalChild(
                element,
                EXPRESSION_ELEMENT
            );
            if(expElement != null){
                final ExpressionMetaData expData = new ExpressionMetaData();
                expData.importXML(expElement);
                value = expData;
                return;
            }
            value = getElementContent(element);
            if(value == null){
                value = "";
            }
        }
        
        public Object getValue(FlowContext context) throws Exception{
            if(isNullValue){
                return null;
            }
            if(value instanceof ReturnValue){
                Object ret = ((ReturnValue)value).getValue(context);
                if(ret != null && isNarrowCast){
                    Class<?> typeClass = getTypeClass();
                    if(typeClass != null
                        && isNarrowCast(ret.getClass(), typeClass)
                    ){
                        ret = castPrimitiveWrapper(typeClass, (Number)ret);
                    }
                }
                return ret;
            }else{
                Class<?> valueTypeClass = getValueTypeClass();
                if(valueTypeClass == null){
                    throw new DeploymentException(
                        "Type is unknown : " + this
                    );
                }
                final PropertyEditor editor
                     = factory.findEditor(valueTypeClass);
                if(editor == null){
                    throw new DeploymentException(
                        "PropertyEditor not found : " + valueTypeClass.getName()
                    );
                }
                editor.setAsText(
                    factory.replaceProperty((String)value)
                );
                Object ret = editor.getValue();
                if(ret != null && isNarrowCast){
                    Class<?> typeClass = getTypeClass();
                    if(typeClass != null
                        && isNarrowCast(ret.getClass(), typeClass)
                    ){
                        ret = castPrimitiveWrapper(typeClass, (Number)ret);
                    }
                }
                return ret;
            }
        }
    }
    
    protected class FieldMetaData
     extends jp.ossc.nimbus.core.FieldMetaData
     implements ReturnValue, SetValue, Journaling{
        
        private static final long serialVersionUID = 8319123524651491880L;
        
        protected static final String TYPE_ATTRIBUTE_NULL_VALUE = "nullValue";
        
        protected boolean isNullValue;
        protected boolean isJournal = true;
        protected transient Field field;
        
        public FieldMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            
            if(!element.getTagName().equals(FIELD_TAG_NAME)){
                throw new DeploymentException(
                    "Tag must be " + FIELD_TAG_NAME + " : "
                     + element.getTagName()
                );
            }
            
            name = getUniqueAttribute(element, NAME_ATTRIBUTE_NAME);
            type = getOptionalAttribute(element, TYPE_ATTRIBUTE_NAME);
            isNullValue = getOptionalBooleanAttribute(
                element,
                TYPE_ATTRIBUTE_NULL_VALUE,
                false
            );
            final String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            final Element serviceRefElement = getOptionalChild(
                element,
                ServiceRefMetaData.SERIVCE_REF_TAG_NAME
            );
            if(serviceRefElement != null){
                final ServiceRefMetaData serviceRefData
                     = new ServiceRefMetaData(this);
                serviceRefData.importXML(serviceRefElement);
                value = serviceRefData;
                return;
            }
            
            final Element staticInvokeElement = getOptionalChild(
                element,
                StaticInvokeMetaData.STATIC_INVOKE_TAG_NAME
            );
            if(staticInvokeElement != null){
                final StaticInvokeMetaData staticInvokeData
                     = new StaticInvokeMetaData(this);
                staticInvokeData.importXML(staticInvokeElement);
                value = staticInvokeData;
                return;
            }
            
            final Element staticFieldRefElement = getOptionalChild(
                element,
                StaticFieldRefMetaData.STATIC_FIELD_REF_TAG_NAME
            );
            if(staticFieldRefElement != null){
                final StaticFieldRefMetaData staticFieldRefData
                     = new StaticFieldRefMetaData(this);
                staticFieldRefData.importXML(staticFieldRefElement);
                value = staticFieldRefData;
                return;
            }
            
            final Element objectElement = getOptionalChild(
                element,
                ObjectMetaData.OBJECT_TAG_NAME
            );
            if(objectElement != null){
                final ObjectMetaData objectData = new ObjectMetaData(this);
                objectData.importXML(objectElement);
                value = objectData;
                return;
            }
            
            final Element inputElement = getOptionalChild(
                element,
                INPUT_ELEMENT
            );
            if(inputElement != null){
                final InputMetaData inputData = new InputMetaData();
                inputData.importXML(inputElement);
                value = inputData;
                return;
            }
            
            final Element stepRefElement = getOptionalChild(
                element,
                STEP_REF_ELEMENT
            );
            if(stepRefElement != null){
                final StepRefMetaData stepRefData = new StepRefMetaData();
                stepRefData.importXML(stepRefElement);
                value = stepRefData;
                return;
            }
            
            final Element thisElement = getOptionalChild(
                element,
                THIS_ELEMENT
            );
            if(thisElement != null){
                final ThisMetaData thisData = new ThisMetaData();
                thisData.importXML(thisElement);
                value = thisData;
                return;
            }
            
            final Element resourceRefElement = getOptionalChild(
                element,
                RESOURCE_REF_ELEMENT
            );
            if(resourceRefElement != null){
                final ResourceRefMetaData resourceRefData = new ResourceRefMetaData();
                resourceRefData.importXML(resourceRefElement);
                value = resourceRefData;
                return;
            }
            
            final Element varElement = getOptionalChild(
                element,
                VAR_ELEMENT
            );
            if(varElement != null){
                final VarMetaData varData = new VarMetaData();
                varData.importXML(varElement);
                value = varData;
                return;
            }
            
            final Element expElement = getOptionalChild(
                element,
                EXPRESSION_ELEMENT
            );
            if(expElement != null){
                final ExpressionMetaData expData = new ExpressionMetaData();
                expData.importXML(expElement);
                value = expData;
                return;
            }
            value = getElementContent(element);
            if(value == null){
                value = "";
            }
        }
        
        public void setValue(FlowContext context) throws Exception{
            setValue(context.current.target, context);
        }
        
        public void setValue(Object target, FlowContext context) throws Exception{
            Object val = getSetValue(target, context);
            final Journal journal = getJournal(FieldMetaData.this);
            if(journal != null){
                journal.addInfo(
                    JOURNAL_KEY_FIELD + getName(),
                    val
                );
            }
            getField(target).set(target, val);
        }
        
        protected Field getField(Object target) throws Exception{
            if(field != null){
                return field;
            }
            final String name = getName();
            final Class<?> targetClazz = target.getClass();
            try{
                field = targetClazz.getField(name);
            }catch(NoSuchFieldException e){
                if(name.length() != 0 && Character.isUpperCase(name.charAt(0))){
                    StringBuilder tmpName = new StringBuilder();
                    tmpName.append(Character.toLowerCase(name.charAt(0)));
                    if(name.length() > 1){
                        tmpName.append(name.substring(1));
                    }
                    field = targetClazz.getField(tmpName.toString());
                }else{
                    throw e;
                }
            }
            return field;
        }
        
        public Object getSetValue(FlowContext context) throws Exception{
            return getSetValue(context.current.target, context);
        }
        
        protected Object getSetValue(Object target, FlowContext context) throws Exception{
            if(isNullValue){
                return null;
            }
            Object value = this.value;
            if(value instanceof ReturnValue){
                value = ((ReturnValue)value).getValue(context);
            }else{
                Class<?> type = null;
                if(getType() != null){
                    type = Utility.convertStringToClass(getType());
                }else{
                    type = getField(target).getType();
                }
                if(type == null || Object.class.equals(type)){
                    type = String.class;
                }
                final PropertyEditor editor
                     = factory.findEditor(type);
                if(editor == null){
                    throw new DeploymentException(
                        "PropertyEditor not found : " + type.getName()
                    );
                }
                editor.setAsText(
                    factory.replaceProperty((String)value)
                );
                value = editor.getValue();
            }
            return value;
        }
        
        public Object getValue(FlowContext context) throws Exception{
            return getValue(context.current.target);
        }
        
        protected Object getValue(Object target) throws Exception{
            final String name = getName();
            final Class<?> targetClazz = target.getClass();
            Field f = null;
            try{
                f = targetClazz.getField(name);
            }catch(NoSuchFieldException e){
                if(name.length() != 0 && Character.isUpperCase(name.charAt(0))){
                    StringBuilder tmpName = new StringBuilder();
                    tmpName.append(Character.toLowerCase(name.charAt(0)));
                    if(name.length() > 1){
                        tmpName.append(name.substring(1));
                    }
                    f = targetClazz.getField(tmpName.toString());
                }else{
                    throw e;
                }
            }
            return f.get(target);
        }
     }
    
    protected class AttributeMetaData
     extends jp.ossc.nimbus.core.AttributeMetaData
     implements ReturnValue, SetValue, Journaling{
        
        private static final long serialVersionUID = -5735499988722712700L;
        
        protected static final String TYPE_ATTRIBUTE_NULL_VALUE = "nullValue";
        
        protected boolean isNullValue;
        protected boolean isJournal = true;
        protected Property property;
        
        public AttributeMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            
            if(!element.getTagName().equals(ATTRIBUTE_TAG_NAME)){
                throw new DeploymentException(
                    "Tag must be " + ATTRIBUTE_TAG_NAME + " : "
                     + element.getTagName()
                );
            }
            
            name = getUniqueAttribute(element, NAME_ATTRIBUTE_NAME);
            property = PropertyFactory.createProperty(name);
            type = getOptionalAttribute(element, TYPE_ATTRIBUTE_NAME);
            isNullValue = getOptionalBooleanAttribute(
                element,
                TYPE_ATTRIBUTE_NULL_VALUE,
                false
            );
            final String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            if(Element.class.getName().equals(type)){
                Element valueElement = getOptionalChild(element);
                if(valueElement != null){
                    value = valueElement.cloneNode(true);
                }
                return;
            }
            final Element serviceRefElement = getOptionalChild(
                element,
                ServiceRefMetaData.SERIVCE_REF_TAG_NAME
            );
            if(serviceRefElement != null){
                final ServiceRefMetaData serviceRefData
                     = new ServiceRefMetaData(this);
                serviceRefData.importXML(serviceRefElement);
                value = serviceRefData;
                return;
            }
            
            final Element staticInvokeElement = getOptionalChild(
                element,
                StaticInvokeMetaData.STATIC_INVOKE_TAG_NAME
            );
            if(staticInvokeElement != null){
                final StaticInvokeMetaData staticInvokeData
                     = new StaticInvokeMetaData(this);
                staticInvokeData.importXML(staticInvokeElement);
                value = staticInvokeData;
                return;
            }
            
            final Element staticFieldRefElement = getOptionalChild(
                element,
                StaticFieldRefMetaData.STATIC_FIELD_REF_TAG_NAME
            );
            if(staticFieldRefElement != null){
                final StaticFieldRefMetaData staticFieldRefData
                     = new StaticFieldRefMetaData(this);
                staticFieldRefData.importXML(staticFieldRefElement);
                value = staticFieldRefData;
                return;
            }
            
            final Element objectElement = getOptionalChild(
                element,
                ObjectMetaData.OBJECT_TAG_NAME
            );
            if(objectElement != null){
                final ObjectMetaData objectData = new ObjectMetaData(this);
                objectData.importXML(objectElement);
                value = objectData;
                return;
            }
            
            final Element inputElement = getOptionalChild(
                element,
                INPUT_ELEMENT
            );
            if(inputElement != null){
                final InputMetaData inputData = new InputMetaData();
                inputData.importXML(inputElement);
                value = inputData;
                return;
            }
            
            final Element stepRefElement = getOptionalChild(
                element,
                STEP_REF_ELEMENT
            );
            if(stepRefElement != null){
                final StepRefMetaData stepRefData = new StepRefMetaData();
                stepRefData.importXML(stepRefElement);
                value = stepRefData;
                return;
            }
            
            final Element thisElement = getOptionalChild(
                element,
                THIS_ELEMENT
            );
            if(thisElement != null){
                final ThisMetaData thisData = new ThisMetaData();
                thisData.importXML(thisElement);
                value = thisData;
                return;
            }
            
            final Element resourceRefElement = getOptionalChild(
                element,
                RESOURCE_REF_ELEMENT
            );
            if(resourceRefElement != null){
                final ResourceRefMetaData resourceRefData = new ResourceRefMetaData();
                resourceRefData.importXML(resourceRefElement);
                value = resourceRefData;
                return;
            }
            
            final Element varElement = getOptionalChild(
                element,
                VAR_ELEMENT
            );
            if(varElement != null){
                final VarMetaData varData = new VarMetaData();
                varData.importXML(varElement);
                value = varData;
                return;
            }
            
            final Element expElement = getOptionalChild(
                element,
                EXPRESSION_ELEMENT
            );
            if(expElement != null){
                final ExpressionMetaData expData = new ExpressionMetaData();
                expData.importXML(expElement);
                value = expData;
                return;
            }
            value = getElementContent(element);
            if(value == null){
                value = "";
            }
        }
        
        public void setValue(FlowContext context) throws Exception{
            setValue(context.current.target, context);
        }
        
        public void setValue(Object target, FlowContext context) throws Exception{
            Object val = getSetValue(target, context);
            final Journal journal = getJournal(AttributeMetaData.this);
            if(journal != null){
                journal.addInfo(
                    JOURNAL_KEY_ATTRIBUTE + getName(),
                    val
                );
            }
            Class<?> type = null;
            if(getType() != null){
                type = Utility.convertStringToClass(getType());
            }else if(val != null){
                type = val.getClass();
            }
            try{
                property.setProperty(target, type, val);
            }catch(InvocationTargetException e){
                final Throwable th = e.getCause();
                if(th == null){
                    throw e;
                }
                if(th instanceof Exception){
                    throw (Exception)th;
                }else if(th instanceof Error){
                    throw (Error)th;
                }else{
                    throw e;
                }
            }
        }
        
        public Object getSetValue(FlowContext context) throws Exception{
            return getSetValue(context.current.target, context);
        }
        
        public Object getSetValue(Object target, FlowContext context) throws Exception{
            if(isNullValue){
                return null;
            }
            if(Element.class.getName().equals(getType())){
                return value;
            }
            Object value = this.value;
            if(value instanceof ReturnValue){
                value = ((ReturnValue)value).getValue(context);
            }else{
                Class<?> type = null;
                if(getType() != null){
                    type = Utility.convertStringToClass(getType());
                }else{
                    try{
                        type = property.getPropertyType(target);
                    }catch(NoSuchPropertyException e){
                    }
                }
                if(type == null || Object.class.equals(type)){
                    type = String.class;
                }
                final PropertyEditor editor
                     = factory.findEditor(type);
                if(editor == null){
                    throw new DeploymentException(
                        "PropertyEditor not found : " + type.getName()
                    );
                }
                editor.setAsText(
                    factory.replaceProperty((String)value)
                );
                value = editor.getValue();
            }
            return value;
        }
        
        public Object getValue(FlowContext context) throws Exception{
            return getValue(context.current.target);
        }
        
        protected Object getValue(Object target) throws Exception{
            try{
                return property.getProperty(target);
            }catch(InvocationTargetException e){
                final Throwable th = e.getCause();
                if(th == null){
                    throw e;
                }
                if(th instanceof Exception){
                    throw (Exception)th;
                }else if(th instanceof Error){
                    throw (Error)th;
                }else{
                    throw e;
                }
            }
        }
     }
    
    protected class InvokeMetaData
     extends jp.ossc.nimbus.core.InvokeMetaData
     implements ReturnValue, Journaling{
        
        private static final long serialVersionUID = 3949770023966053314L;
        
        protected boolean isJournal = true;
        
        protected transient Method method;
        
        public InvokeMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            
            if(!element.getTagName().equals(INVOKE_TAG_NAME)){
                throw new DeploymentException(
                    "Tag must be " + INVOKE_TAG_NAME + " : "
                     + element.getTagName()
                );
                
            }
            name = getUniqueAttribute(element, NAME_ATTRIBUTE_NAME);
            final String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            final Iterator<Element> argElements = getChildrenByTagName(
                element,
                ArgumentMetaData.ARGUMENT_TAG_NAME
            );
            while(argElements.hasNext()){
                final ArgumentMetaData argData
                     = new ArgumentMetaData(this, null);
                argData.importXML(argElements.next());
                if(argData.isNullValue()){
                    Class<?> typeClass = null;
                    try{
                        typeClass = argData.getTypeClass();
                        if(typeClass == null){
                            throw new DeploymentException(
                                "Type is unknown : " + argData
                            );
                        }
                    }catch(Exception e){}
                }
                addArgument(argData);
            }
        }
        
        protected boolean isAccessableClass(Class<?> clazz){
            final int modifier = clazz.getModifiers();
            return Modifier.isPublic(modifier)
                || ((Modifier.isProtected(modifier)
                    || (!Modifier.isPublic(modifier)
                        && !Modifier.isProtected(modifier)
                        && !Modifier.isPrivate(modifier)))
                    && SimpleProperty.class.getPackage().equals(clazz.getPackage()));
        }
        
        public Object getValue(FlowContext context) throws Exception{
            return getValue(context.current.target, context);
        }
        
        public Object getValue(Object target, FlowContext context) throws Exception{
            List<Class<?>> paramTypes = new ArrayList<Class<?>>();
            List<Object> params = new ArrayList<Object>();
            final Iterator<jp.ossc.nimbus.core.ArgumentMetaData> argDatas = getArguments().iterator();
            while(argDatas.hasNext()){
                ArgumentMetaData argData = (ArgumentMetaData)argDatas.next();
                Object arg = argData.getValue(context);
                Class<?> typeClass = argData.getTypeClass();
                if(typeClass == null){
                    if(arg == null){
                        throw new DeploymentException(
                            "Type is unknown : " + argData
                        );
                    }
                    typeClass = arg.getClass();
                }
                params.add(arg);
                paramTypes.add(typeClass);
            }
            if(method == null){
                Class<?> targetClass = target.getClass();
                final Class<?>[] paramTypeArray = (Class[])paramTypes.toArray(new Class[paramTypes.size()]);
                do{
                    if(isAccessableClass(targetClass)){
                        method = targetClass.getMethod(
                            name,
                            paramTypeArray
                        );
                    }else{
                        final Class<?>[] interfaces = targetClass.getInterfaces();
                        for(int i = 0; i < interfaces.length; i++){
                            if(isAccessableClass(interfaces[i])){
                                try{
                                    method = interfaces[i].getMethod(
                                        name,
                                        paramTypeArray
                                    );
                                    break;
                                }catch(NoSuchMethodException e){
                                    continue;
                                }
                            }
                        }
                    }
                }while(method == null && (targetClass = targetClass.getSuperclass()) != null);
                if(method == null){
                    throw new NoSuchMethodException(
                        target.getClass().getName() + '#' + getSignature(params)
                    );
                }
            }
            
            final Journal journal = getJournal(InvokeMetaData.this);
            if(journal != null){
                journal.addInfo(
                    JOURNAL_KEY_INVOKE + getSignature(params),
                    params
                );
            }
            try{
                return method.invoke(target, params.toArray());
            }catch(InvocationTargetException e){
                final Throwable th = e.getCause();
                if(th == null){
                    throw e;
                }
                if(th instanceof Exception){
                    throw (Exception)th;
                }else if(th instanceof Error){
                    throw (Error)th;
                }else{
                    throw e;
                }
            }
        }
        
        protected String getSignature(List<Object> params) throws Exception{
            final StringBuilder buf = new StringBuilder();
            buf.append(getName());
            buf.append('(');
            if(arguments.size() != 0){
                final Iterator<jp.ossc.nimbus.core.ArgumentMetaData> args = arguments.iterator();
                int index = 0;
                while(args.hasNext()){
                    ArgumentMetaData argData = (ArgumentMetaData)args.next();
                    Class<?> type = null;
                    try{
                        type = argData.getTypeClass();
                    }catch(Exception e){
                    }
                    if(type == null){
                        final Object param = params.get(index);
                        if(param != null){
                            type = param.getClass();
                        }
                    }
                    if(type != null){
                        buf.append(type.getName());
                    }
                    if(args.hasNext()){
                        buf.append(',');
                    }
                    index++;
                }
            }
            buf.append(')');
            return buf.toString();
        }
    }
    
    protected class StaticInvokeMetaData
     extends jp.ossc.nimbus.core.StaticInvokeMetaData
     implements ReturnValue, Journaling{
        
        private static final long serialVersionUID = 37922080913464606L;
        
        protected boolean isJournal = true;
        
        protected transient Method method;
        
        public StaticInvokeMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            
            if(!element.getTagName().equals(STATIC_INVOKE_TAG_NAME)){
                throw new DeploymentException(
                    "Tag must be " + STATIC_INVOKE_TAG_NAME + " : "
                     + element.getTagName()
                );
                
            }
            code = getUniqueAttribute(element, CODE_ATTRIBUTE_NAME);
            name = getUniqueAttribute(element, NAME_ATTRIBUTE_NAME);
            final String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            final Iterator<Element> argElements = getChildrenByTagName(
                element,
                ArgumentMetaData.ARGUMENT_TAG_NAME
            );
            while(argElements.hasNext()){
                final ArgumentMetaData argData
                     = new ArgumentMetaData(this, null);
                argData.importXML(argElements.next());
                if(argData.isNullValue()){
                    Class<?> typeClass = null;
                    try{
                        typeClass = argData.getTypeClass();
                        if(typeClass == null){
                            throw new DeploymentException(
                                "Type is unknown : " + argData
                            );
                        }
                    }catch(Exception e){}
                }
                addArgument(argData);
            }
        }
        
        public Object getValue(FlowContext context) throws Exception{
            final Class<?> targetClass = Utility.convertStringToClass(getCode());
            List<Class<?>> paramTypes = method == null ? new ArrayList<Class<?>>() : null;
            List<Object> params = new ArrayList<Object>();
            final Iterator<jp.ossc.nimbus.core.ArgumentMetaData> argDatas = getArguments().iterator();
            while(argDatas.hasNext()){
                ArgumentMetaData argData = (ArgumentMetaData)argDatas.next();
                Object arg = argData.getValue(context);
                Class<?> typeClass = argData.getTypeClass();
                if(typeClass == null){
                    if(arg == null){
                        throw new DeploymentException(
                            "Type is unknown : " + argData
                        );
                    }
                    typeClass = arg.getClass();
                }
                params.add(arg);
                if(paramTypes != null){
                    paramTypes.add(typeClass);
                }
            }
            if(method == null){
                method = targetClass.getMethod(
                    name,
                    paramTypes.toArray(new Class[paramTypes.size()])
                );
            }
            
            final Journal journal = getJournal(StaticInvokeMetaData.this);
            if(journal != null){
                journal.addInfo(
                    JOURNAL_KEY_STATIC_INVOKE + getSignature(params),
                    params
                );
            }
            try{
                return method.invoke(null, params.toArray());
            }catch(InvocationTargetException e){
                final Throwable th = e.getCause();
                if(th == null){
                    throw e;
                }
                if(th instanceof Exception){
                    throw (Exception)th;
                }else if(th instanceof Error){
                    throw (Error)th;
                }else{
                    throw e;
                }
            }
        }
        
        protected String getSignature(List<Object> params) throws Exception{
            final StringBuilder buf = new StringBuilder();
            buf.append(getCode());
            buf.append('#');
            buf.append(getName());
            buf.append('(');
            if(arguments.size() != 0){
                final Iterator<jp.ossc.nimbus.core.ArgumentMetaData> args = arguments.iterator();
                int index = 0;
                while(args.hasNext()){
                    ArgumentMetaData argData = (ArgumentMetaData)args.next();
                    Class<?> type = null;
                    try{
                        type = argData.getTypeClass();
                    }catch(Exception e){
                    }
                    if(type == null){
                        final Object param = params.get(index);
                        if(param != null){
                            type = param.getClass();
                        }
                    }
                    if(type != null){
                        buf.append(type.getName());
                    }
                    if(args.hasNext()){
                        buf.append(',');
                    }
                    index++;
                }
            }
            buf.append(')');
            return buf.toString();
        }
    }
    
    protected class ServiceRefMetaData
     extends jp.ossc.nimbus.core.ServiceRefMetaData
     implements ReturnValue{
        
        private static final long serialVersionUID = -1606807882399104294L;
        
        private ReturnValue serviceNameValue;
        
        public ServiceRefMetaData(MetaData parent){
            super(parent);
        }
        
        public Object getValue(FlowContext context) throws Exception{
            if(serviceNameValue == null){
                return ServiceManagerFactory
                    .getServiceObject(getManagerName(), getServiceName());
            }else{
                Object value = serviceNameValue.getValue(context);
                if(value == null){
                    throw new IllegalArgumentException("Service name is null.");
                }
                ServiceNameEditor editor = new ServiceNameEditor();
                editor.setAsText(value.toString());
                return ServiceManagerFactory
                    .getServiceObject((ServiceName)editor.getValue());
            }
        }
        
        public void importXML(Element element) throws DeploymentException{
            tagName = element.getTagName();
            
            if(!element.getTagName().equals(SERIVCE_REF_TAG_NAME)){
                throw new DeploymentException(
                    "Tag must be " + SERIVCE_REF_TAG_NAME + " : "
                     + element.getTagName()
                );
            }
            
            final Element childElement = getOptionalChild(element);
            if(childElement != null){
                String tagName = childElement.getTagName();
                MetaData childData = null;
                if(INPUT_ELEMENT.equals(tagName)){
                    childData = new InputMetaData();
                    childData.importXML(childElement);
                }else if(ObjectMetaData.OBJECT_TAG_NAME.equals(tagName)){
                    childData = new ObjectMetaData(this);
                    childData.importXML(childElement);
                }else if(StaticInvokeMetaData.STATIC_INVOKE_TAG_NAME.equals(tagName)){
                    childData = new StaticInvokeMetaData(this);
                    childData.importXML(childElement);
                }else if(StaticFieldRefMetaData.STATIC_FIELD_REF_TAG_NAME.equals(tagName)){
                    childData = new StaticFieldRefMetaData(this);
                    childData.importXML(childElement);
                }else if(STEP_REF_ELEMENT.equals(tagName)){
                    childData = new StepRefMetaData();
                    childData.importXML(childElement);
                }else if(VAR_ELEMENT.equals(tagName)){
                    childData = new VarMetaData();
                    childData.importXML(childElement);
                }else if(EXPRESSION_ELEMENT.equals(tagName)){
                    childData = new ExpressionMetaData();
                    childData.importXML(childElement);
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of result tag : " + tagName
                    );
                }
                serviceNameValue = (ReturnValue)childData;
            }else{
                String content = getElementContent(element);
                if(content != null && content.length() != 0){
                    content = factory.replaceProperty(content);
                    if(content.indexOf('#') != -1){
                        final ServiceNameEditor editor = new ServiceNameEditor();
                        try{
                            editor.setAsText(content);
                        }catch(IllegalArgumentException e){
                            throw new DeploymentException("Illegal service name.", e);
                        }
                        final ServiceName editName = (ServiceName)editor.getValue();
                        managerName = editName.getServiceManagerName();
                        serviceName = editName.getServiceName();
                    }else{
                        if(managerName == null){
                            managerName = ServiceManager.DEFAULT_NAME;
                        }
                        serviceName = content;
                    }
                }else{
                    throw new DeploymentException(
                        "Content of '" + tagName + "' element must not be null."
                    );
                }
            }
        }
    }
    
    protected static class StaticFieldRefMetaData
     extends jp.ossc.nimbus.core.StaticFieldRefMetaData
     implements ReturnValue{
        
        private static final long serialVersionUID = 3242136305224262415L;
        
        public StaticFieldRefMetaData(MetaData parent){
            super(parent);
        }
        
        public Object getValue(FlowContext context) throws Exception{
            final Class<?> clazz = Utility.convertStringToClass(getCode());
            final Field field = clazz.getField(getName());
            return field.get(null);
        }
    }
    
    protected class ResourceRefMetaData extends MetaData implements ReturnValue{
        
        private static final long serialVersionUID = -7785759414964084633L;
        
        protected String name;
        protected boolean isRaw;
        
        public ResourceRefMetaData(){
            super(null);
        }
        
        public void importXML(Element element) throws DeploymentException{
            name = getElementContent(element);
            if(name == null){
                throw new DeploymentException("Resource name is null.");
            }
            final PropertyEditor editor
                 = factory.findEditor(String.class);
            if(editor != null){
                editor.setAsText(
                    factory.replaceProperty(name)
                );
                name = (String)editor.getValue();
            }
            isRaw = getOptionalBooleanAttribute(element, RAW_ATTRIBUTE, false);
        }
        
        public Object getValue(FlowContext context) throws Exception{
            final Resource<Object> resource = context.getResource(name);
            return isRaw ? resource : (resource == null ? null : resource.getResource());
        }
    }
    
    protected static class ThisMetaData extends MetaData implements ReturnValue{
        
        private static final long serialVersionUID = -581510918646973596L;
        
        protected Property property;
        protected boolean nullCheck;
        
        public ThisMetaData(){}
        
        public void importXML(Element element) throws DeploymentException{
            final String nullCheckAttribute = getOptionalAttribute(
                element,
                NULLCHECK_ATTRIBUTE
            );
            if(nullCheckAttribute != null){
                nullCheck = Boolean.valueOf(nullCheckAttribute).booleanValue();
            }
            String val = getElementContent(element);
            if(val != null && val.length() != 0){
                try{
                    property = PropertyFactory.createProperty(val);
                    if(!nullCheck){
                        property.setIgnoreNullProperty(true);
                    }
                }catch(Exception e){
                    throw new DeploymentException(e);
                }
            }
        }
        
        public Object getValue(FlowContext context) throws Exception{
            Object target = context.current.target;
            if(target == null){
                return null;
            }
            if(property == null){
                return target;
            }
            try{
                return property.getProperty(target);
            }catch(NullIndexPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(NullKeyPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(NullNestPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(InvocationTargetException e){
                final Throwable th = e.getCause();
                if(th == null){
                    throw e;
                }
                if(th instanceof Exception){
                    throw (Exception)th;
                }else if(th instanceof Error){
                    throw (Error)th;
                }else{
                    throw e;
                }
            }
            return null;
        }
    }
    
    protected static class VarMetaData extends MetaData implements ReturnValue{
        
        private static final long serialVersionUID = -2037473595575858803L;
        
        protected String name;
        protected Property property;
        protected boolean nullCheck;
        
        public VarMetaData(){}
        
        public void importXML(Element element) throws DeploymentException{
            final String nullCheckAttribute = getOptionalAttribute(
                element,
                NULLCHECK_ATTRIBUTE
            );
            if(nullCheckAttribute != null){
                nullCheck = Boolean.valueOf(nullCheckAttribute).booleanValue();
            }
            String val = getElementContent(element);
            if(val == null){
                throw new DeploymentException("Var name is null.");
            }
            int dotIndex = val.indexOf('.');
            int braket1Index = val.indexOf('(');
            int braket2Index = val.indexOf('[');
            if(dotIndex == -1
                && braket1Index == -1
                && braket2Index == -1
            ){
                name = val;
            }else{
                int index = dotIndex;
                int index2 = dotIndex + 1;
                if(index == -1 || (braket1Index != -1 && index > braket1Index)){
                    index = braket1Index;
                    index2 = braket1Index;
                }
                if(index == -1 || (braket2Index != -1 && index > braket2Index)){
                    index = braket2Index;
                    index2 = braket2Index;
                }
                name = val.substring(0, index);
                if(index < val.length() - 1){
                    final String prop = val.substring(index2);
                    try{
                        property = PropertyFactory.createProperty(prop);
                        if(!nullCheck){
                            property.setIgnoreNullProperty(true);
                        }
                    }catch(Exception e){
                        throw new DeploymentException(e);
                    }
                }
            }
        }
        
        public Object getValue(FlowContext context) throws Exception{
            Object var = context.getVar(name);
            if(var == null){
                return null;
            }
            if(property == null){
                return var;
            }
            try{
                return property.getProperty(var);
            }catch(NullIndexPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(NullKeyPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(NullNestPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(InvocationTargetException e){
                final Throwable th = e.getCause();
                if(th == null){
                    throw e;
                }
                if(th instanceof Exception){
                    throw (Exception)th;
                }else if(th instanceof Error){
                    throw (Error)th;
                }else{
                    throw e;
                }
            }
            return null;
        }
    }
    
    protected static class StepRefMetaData extends MetaData implements ReturnValue{
        
        private static final long serialVersionUID = -983973047312150748L;
        
        protected static final String TARGET = "target";
        protected static final String RESULT = "result";
        
        protected Property property;
        protected boolean nullCheck;
        
        public StepRefMetaData(){}
        
        public void importXML(Element element) throws DeploymentException{
            final String nullCheckAttribute = getOptionalAttribute(
                element,
                NULLCHECK_ATTRIBUTE
            );
            if(nullCheckAttribute != null){
                nullCheck = Boolean.valueOf(nullCheckAttribute).booleanValue();
            }
            String val = getElementContent(element);
            if(val == null || val.length() == 0){
                throw new DeploymentException(
                    "Content of step-ref is null."
                );
            }
            int index = 0;
            int dotIndex = val.indexOf('.');
            int braket1Index = val.indexOf('(');
            int braket2Index = val.indexOf('[');
            if(dotIndex == 0
                || braket1Index == 0
                || braket2Index == 0
            ){
                throw new DeploymentException("Step name is null." + val);
            }else if(dotIndex == -1
                && braket1Index == -1
                && braket2Index == -1
            ){
                val = val + '.' + RESULT;
            }else if(dotIndex == val.length() - 1
                || braket1Index == val.length() - 1
                || braket2Index == val.length() - 1
            ){
                throw new DeploymentException(
                    "Invalid content of step-ref : " + val
                );
            }else{
                index = dotIndex;
                if(index == -1
                     || (braket1Index != -1 && index > braket1Index)){
                    index = braket1Index;
                }
                if(index == -1
                     || (braket2Index != -1 && index > braket2Index)){
                    index = braket2Index;
                }
                String tmp = val.substring(index + 1);
                if(!tmp.startsWith(TARGET)
                     && !tmp.startsWith(TARGET + '.')
                     && !tmp.startsWith(TARGET + '(')
                     && !tmp.startsWith(TARGET + '[')
                     && !tmp.startsWith(RESULT)
                     && !tmp.startsWith(RESULT + '.')
                     && !tmp.startsWith(RESULT + '(')
                     && !tmp.startsWith(RESULT + '[')
                ){
                    val = val.substring(0, index)
                        + '.' + RESULT + val.substring(index);
                }
            }
            try{
                property = PropertyFactory.createProperty(val);
                if(!nullCheck){
                    property.setIgnoreNullProperty(true);
                }
            }catch(Exception e){
                throw new DeploymentException(e);
            }
        }
        
        public Object getValue(FlowContext context) throws Exception{
            try{
                Object val = null;
                if(property instanceof NestedProperty){
                    NestedProperty nestedProp = (NestedProperty)property;
                    Property thisProp = nestedProp.getFirstThisProperty();
                    val = thisProp.getProperty(context);
                    if(val != null){
                        val = nestedProp.getProperty(context);
                    }
                }else{
                    val = property.getProperty(context);
                }
                return val;
            }catch(NullIndexPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(NullKeyPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(NullNestPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(InvocationTargetException e){
                final Throwable th = e.getCause();
                if(th == null){
                    throw e;
                }
                if(th instanceof Exception){
                    throw (Exception)th;
                }else if(th instanceof Error){
                    throw (Error)th;
                }else{
                    throw e;
                }
            }
            return null;
        }
    }
    
    protected class CallFlowMetaData extends MetaData implements Step, Journaling{
        
        private static final long serialVersionUID = -1896083944181448036L;
        
        protected String name;
        protected boolean isOverwride = true;
        protected List<String> overrideNames;
        protected String stepName;
        protected List<ReturnValue> inputData;
        protected int transactionType = -1;
        protected int transactionTimeout = -1;
        protected ServiceName factoryName;
        protected boolean isJournal = true;
        protected boolean isAsynch = false;
        protected boolean isReply = false;
        protected int maxAsynchWait = 0;
        protected String callbackName;
        protected List<AttributeMetaData> callbackAttributes;
        protected boolean isCallbackOverwride = true;
        protected List<String> callbackOverrideNames;
        
        public CallFlowMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            name = getUniqueAttribute(element, NAME_ATTRIBUTE);
            stepName = getOptionalAttribute(element, STEPNAME_ATTRIBUTE, name);
            final String overrideAttribute = getOptionalAttribute(
                element,
                OVERRIDE_ATTRIBUTE
            );
            if(overrideAttribute != null){
                isOverwride = Boolean.valueOf(overrideAttribute).booleanValue();
            }
            final String journalStr = getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            final String transactionStr = getOptionalAttribute(
                element,
                TRANSACTION_ATTRIBUTE
            );
            if(transactionStr != null){
                if(REQUIRED.equals(transactionStr)){
                    transactionType = REQUIRED_VALUE;
                }else if(REQUIRESNEW.equals(transactionStr)){
                    transactionType = REQUIRESNEW_VALUE;
                }else if(SUPPORTS.equals(transactionStr)){
                    transactionType = SUPPORTS_VALUE;
                }else if(MANDATORY.equals(transactionStr)){
                    transactionType = MANDATORY_VALUE;
                }else if(NEVER.equals(transactionStr)){
                    transactionType = NEVER_VALUE;
                }else if(NOT_SUPPORTED.equals(transactionStr)){
                    transactionType = NOT_SUPPORTED_VALUE;
                }else{
                    throw new DeploymentException("Invalid transaction : " + transactionStr);
                }
                
                if(transactionType != SUPPORTS_VALUE){
                    try{
                        TransactionManagerFactory tranMngFactory = factory.getTransactionManagerFactory();
                        
                        tranManager = tranMngFactory.getTransactionManager();
                    }catch(TransactionManagerFactoryException e){
                        throw new DeploymentException(e);
                    }
                }
            }
            final String transactionTimeoutStr = MetaData.getOptionalAttribute(
                element,
                TRANTIMEOUT_ATTRIBUTE
            );
            if(transactionTimeoutStr != null){
                try{
                    transactionTimeout = Integer.parseInt(transactionTimeoutStr);
                }catch(NumberFormatException e){
                    throw new DeploymentException("trantimeout is number " + transactionTimeoutStr);
                }
            }
            final String factoryStr = MetaData.getOptionalAttribute(
                element,
                FACTORY_ATTRIBUTE
            );
            if(factoryStr != null){
                final ServiceNameEditor editor = new ServiceNameEditor();
                editor.setAsText(factory.replaceProperty(factoryStr));
                factoryName = (ServiceName)editor.getValue();
            }
            
            final Iterator<Element> owElements = getChildrenByTagName(
                element,
                OVERRIDE_ELEMENT
            );
            while(owElements.hasNext()){
                Element owElement = owElements.next();
                String overrideName = getUniqueAttribute(owElement, NAME_ATTRIBUTE);
                if(overrideNames == null){
                    overrideNames = new ArrayList<String>();
                }
                overrideNames.add(overrideName);
            }
            if(overrideNames != null && overrideNames.size() != 0){
                isOverwride = false;
            }
            
            final Iterator<Element> argElements = getChildrenByTagName(
                element,
                ArgumentMetaData.ARGUMENT_TAG_NAME
            );
            while(argElements.hasNext()){
                final ArgumentMetaData argData
                     = new ArgumentMetaData(this, null);
                argData.importXML(argElements.next());
                if(inputData == null){
                    inputData = new ArrayList<ReturnValue>();
                }
                inputData.add(argData);
            }
            final String asynchAttribute = getOptionalAttribute(
                element,
                ASYNCH_ATTRIBUTE
            );
            if(asynchAttribute != null){
                isAsynch = Boolean.valueOf(asynchAttribute).booleanValue();
            }
            final String replyAttribute = getOptionalAttribute(
                element,
                REPLY_ATTRIBUTE
            );
            if(replyAttribute != null){
                isReply = Boolean.valueOf(replyAttribute).booleanValue();
            }
            final String maxAsynchWaitStr = getOptionalAttribute(
                element,
                MAX_ASYNCH_WAIT_ATTRIBUTE
            );
            if(maxAsynchWaitStr != null){
                try{
                    maxAsynchWait = Integer.parseInt(maxAsynchWaitStr);
                }catch(NumberFormatException e){
                    throw new DeploymentException("maxAsynchWait is number " + maxAsynchWaitStr);
                }
            }
            
            final Element callbackElement = getOptionalChild(
                element,
                CALLBACK_ELEMENT
            );
            if(callbackElement != null){
                callbackName = getUniqueAttribute(callbackElement, NAME_ATTRIBUTE);
                final String callbackOverrideAttribute = getOptionalAttribute(
                    callbackElement,
                    OVERRIDE_ATTRIBUTE
                );
                if(callbackOverrideAttribute != null){
                    isCallbackOverwride = Boolean.valueOf(callbackOverrideAttribute).booleanValue();
                }
                
                final Iterator<Element> callbackOwElements = getChildrenByTagName(
                    callbackElement,
                    OVERRIDE_ELEMENT
                );
                while(callbackOwElements.hasNext()){
                    Element owElement = callbackOwElements.next();
                    String overrideName = getUniqueAttribute(owElement, NAME_ATTRIBUTE);
                    if(callbackOverrideNames == null){
                        callbackOverrideNames = new ArrayList<String>();
                    }
                    callbackOverrideNames.add(overrideName);
                }
                if(callbackOverrideNames != null && callbackOverrideNames.size() != 0){
                    isCallbackOverwride = false;
                }
                
                final Iterator<Element> attributeElements = getChildrenByTagName(
                    callbackElement,
                    AttributeMetaData.ATTRIBUTE_TAG_NAME
                );
                while(attributeElements.hasNext()){
                    final AttributeMetaData attributeData
                         = new AttributeMetaData(this);
                    attributeData.importXML(attributeElements.next());
                    if(callbackAttributes == null){
                        callbackAttributes = new ArrayList<AttributeMetaData>();
                    }
                    callbackAttributes.add(attributeData);
                }
            }
        }
        
        @SuppressWarnings("unchecked")
        public StepContext invokeStep(FlowContext context) throws Exception{
            ((BeanFlowMonitorImpl)context.monitor).setCurrentStepName(stepName);
            StepContext stepContext = new StepContext();
            context.current = stepContext;
            Object input = null;
            if(inputData != null && inputData.size() != 0){
                if(inputData.size() == 1){
                    input = inputData.get(0).getValue(context);
                }else{
                    Object[] inputs = new Object[inputData.size()];
                    for(int i = 0; i < inputs.length; i++){
                        inputs[i] = inputData.get(i)
                            .getValue(context);
                    }
                    input = inputs;
                }
            }
            
            String callFlowName = factory.replaceProperty(name);
            if(overrideNames != null){
                for(int i = overrideNames.size(); --i >= 0;){
                    String overrideName = (String)overrideNames.get(i);
                    final String tmpFlowName = factory.replaceProperty(overrideName);
                    boolean containsFlow = false;
                    if(factoryName == null){
                        containsFlow = factory.containsFlow(tmpFlowName);
                    }else{
                        containsFlow = ((BeanFlowFactory)ServiceManagerFactory.getServiceObject(factoryName)).containsFlow(tmpFlowName);
                    }
                    if(containsFlow){
                        callFlowName = tmpFlowName;
                        break;
                    }
                }
            }
               
            BeanFlow flow = null;
            if(factoryName == null){
                flow = factory.createFlow(
                    callFlowName,
                    DefaultBeanFlow.this.getFlowName(),
                    isOverwride
                );
            }else{
                flow = ((BeanFlowFactory)ServiceManagerFactory.getServiceObject(factoryName)).createFlow(
                    callFlowName,
                    DefaultBeanFlow.this.getFlowName(),
                    isOverwride
                );
            }
            final TransactionInfo info = transaction.get();
            info.transactionType = transactionType;
            info.transactionTimeout = transactionTimeout;
            info.tranManager = tranManager;
            final String flowName = context.monitor.getCurrentFlowName();
            Journal journal = getJournal(CallFlowMetaData.this);
            try{
                if(journal != null){
                    journal.startJournal(
                        JOURNAL_KEY_CALLFLOW,
                        factory.getEditorFinder()
                    );
                    journal.addInfo(JOURNAL_KEY_STEP_NAME, stepName);
                }
                if(isAsynch){
                    if(callbackName == null){
                        Object asynchContext = flow.executeAsynch(input, context.monitor, isReply, maxAsynchWait);
                        StepContext oldStepContext = (StepContext)context.get(stepName);
                        List<Object> result = null;
                        if(oldStepContext != null && oldStepContext.result instanceof List<?>){
                            result = (List<Object>)oldStepContext.result;
                        }else{
                            result = new ArrayList<Object>();
                        }
                        result.add(asynchContext);
                        stepContext.result = result;
                    }else{
                        String callbackFlowName = factory.replaceProperty(callbackName);
                        if(callbackOverrideNames != null){
                            for(int i = callbackOverrideNames.size(); --i >= 0;){
                                String overrideName = callbackOverrideNames.get(i);
                                final String tmpFlowName = factory.replaceProperty(overrideName);
                                if(factory.containsFlow(tmpFlowName)){
                                    callbackFlowName = tmpFlowName;
                                    break;
                                }
                            }
                        }
                        BeanFlow callbackFlow = factory.createFlow(
                            callbackFlowName,
                            DefaultBeanFlow.this.getFlowName(),
                            isCallbackOverwride
                        );
                        AsynchCallbackContext callbackCtx = new AsynchCallbackContext(callFlowName, input);
                        if(factory.getContext() != null){
                            callbackCtx.putThreadContextAll(factory.getContext());
                        }
                        if(callbackAttributes != null){
                            Map<Object, Object> contextMap = callbackCtx.getContextMap();
                            for(int i = 0; i < callbackAttributes.size(); i++){
                                AttributeMetaData attrData = callbackAttributes.get(i);
                                attrData.setValue(contextMap, context);
                            }
                        }
                        flow.executeAsynch(input, context.monitor, new BeanFlowAsynchCallbackImpl(callbackFlow, callbackCtx), maxAsynchWait);
                    }
                }else{
                    stepContext.result = flow.execute(input, context.monitor);
                }
            }finally{
                ((BeanFlowMonitorImpl)context.monitor).setCurrentFlowName(flowName);
                ((BeanFlowMonitorImpl)context.monitor).setCurrentStepName(stepName);
                info.clear();
                if(journal != null){
                    journal.endJournal();
                }
            }
            context.put(stepName, stepContext);
            return stepContext;
        }
        
        private class BeanFlowAsynchCallbackImpl implements BeanFlowAsynchCallback{
            
            private BeanFlow flow;
            private AsynchCallbackContext context;
            
            public BeanFlowAsynchCallbackImpl(BeanFlow flow, AsynchCallbackContext context){
                this.flow = flow;
                this.context = context;
            }
            
            public void reply(Object output, Throwable th){
                if(th == null){
                    context.setOutput(output);
                }else{
                    context.setThrowable(th);
                }
                try{
                    flow.execute(context, null);
                }catch(Exception e){
                }
                flow.end();
            }
        }
    }
    
    protected class GetAsynchReplyMetaData extends MetaData implements Step, Journaling{
        private static final long serialVersionUID = -7443926264364893085L;
        protected boolean isJournal = true;
        protected String name;
        protected String stepName;
        protected long timeout = -1;
        protected boolean isCancel = true;
        
        public GetAsynchReplyMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            name = getUniqueAttribute(element, NAME_ATTRIBUTE);
            stepName = getOptionalAttribute(element, STEPNAME_ATTRIBUTE, name);
            final String journalStr = getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            final String timeoutStr = getOptionalAttribute(
                element,
                TIMEOUT_ATTRIBUTE
            );
            if(timeoutStr != null){
                try{
                    timeout = Long.parseLong(timeoutStr);
                }catch(NumberFormatException e){
                    throw new DeploymentException("timeout is number " + timeoutStr);
                }
            }
            final String cancelAttribute = getOptionalAttribute(
                element,
                CANCEL_ATTRIBUTE
            );
            if(cancelAttribute != null){
                isCancel = Boolean.valueOf(cancelAttribute).booleanValue();
            }
        }
        
        @SuppressWarnings("unchecked")
        public StepContext invokeStep(FlowContext context) throws Exception{
            ((BeanFlowMonitorImpl)context.monitor).setCurrentStepName(stepName);
            StepContext stepContext = new StepContext();
            context.current = stepContext;
            Journal journal = getJournal(GetAsynchReplyMetaData.this);
            try{
                if(journal != null){
                    journal.startJournal(
                        JOURNAL_KEY_GETASYNCHREPLY,
                        factory.getEditorFinder()
                    );
                    journal.addInfo(JOURNAL_KEY_STEP_NAME, name);
                }
                StepContext callFlowStepContext = (StepContext)context.get(stepName);
                if(callFlowStepContext != null && callFlowStepContext.result instanceof List<?>){
                    List<Object> asynchContexts = (List<Object>)callFlowStepContext.result;
                    if(asynchContexts.size() != 0){
                        Object ctx = asynchContexts.get(0);
                        if(ctx instanceof BeanFlowAsynchContext){
                            BeanFlowAsynchContext asynchContext = (BeanFlowAsynchContext)ctx;
                            stepContext.result = asynchContext.getBeanFlow().getAsynchReply(asynchContext, context.monitor, timeout, isCancel);
                            asynchContexts.remove(0);
                        }
                    }
                }
                if(journal != null){
                    journal.addInfo(
                        JOURNAL_KEY_STEP_RESULT,
                        stepContext.result
                    );
                }
            }finally{
                if(journal != null){
                    journal.endJournal();
                }
            }
            context.put(name, stepContext);
            return stepContext;
        }
    }
    
    protected class ForMetaData extends MetaData implements Step, Journaling{
        
        private static final long serialVersionUID = 7638469212499612542L;
        
        protected MetaData targetData;
        protected String varName;
        protected String indexName;
        protected int begin = 0;
        protected ExpressionMetaData beginExp;
        protected int end = -1;
        protected ExpressionMetaData endExp;
        protected List<Step> steps;
        protected boolean isJournal = true;
        protected boolean isTargetJournal = true;
        
        public ForMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            
            indexName = getOptionalAttribute(element, INDEX_ATTRIBUTE);
            
            final String beginStr
                 = getOptionalAttribute(element, BEGIN_ATTRIBUTE);
            if(beginStr != null){
                try{
                    begin = Integer.parseInt(beginStr);
                }catch(NumberFormatException e){
                    final ExpressionMetaData expData = new ExpressionMetaData();
                    expData.importString(beginStr);
                    beginExp = expData;
                }
            }
            
            final String endStr
                 = getOptionalAttribute(element, END_ATTRIBUTE);
            if(endStr != null){
                try{
                    end = Integer.parseInt(endStr);
                }catch(NumberFormatException e){
                    final ExpressionMetaData expData = new ExpressionMetaData();
                    expData.importString(endStr);
                    endExp = expData;
                }
            }
            
            String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            
            final Element targetElement
                 = getOptionalChild(element, TARGET_ELEMENT);
            if(targetElement == null){
                if(end < 0 && endExp == null){
                    throw new DeploymentException(
                        "it is necessary to specify \"end\" when you do not specify <target>."
                    );
                }
            }else{
                journalStr = MetaData.getOptionalAttribute(
                    targetElement,
                    JOURNAL_ATTRIBUTE
                );
                if(journalStr != null){
                    isTargetJournal = Boolean.valueOf(journalStr).booleanValue();
                }
                
                varName = getOptionalAttribute(element, VAR_ATTRIBUTE);
                
                final Element childElement = getUniqueChild(
                    targetElement
                );
                String tagName = childElement.getTagName();
                if(INPUT_ELEMENT.equals(tagName)){
                    targetData = new InputMetaData();
                    targetData.importXML(childElement);
                }else if(ObjectMetaData.OBJECT_TAG_NAME.equals(tagName)){
                    targetData = new ObjectMetaData(this);
                    targetData.importXML(childElement);
                }else if(ServiceRefMetaData.SERIVCE_REF_TAG_NAME.equals(tagName)){
                    targetData = new ServiceRefMetaData(this);
                    targetData.importXML(childElement);
                }else if(StaticInvokeMetaData.STATIC_INVOKE_TAG_NAME.equals(tagName)){
                    targetData = new StaticInvokeMetaData(this);
                    targetData.importXML(childElement);
                }else if(StaticFieldRefMetaData.STATIC_FIELD_REF_TAG_NAME.equals(tagName)){
                    targetData = new StaticFieldRefMetaData(this);
                    targetData.importXML(childElement);
                }else if(RESOURCE_REF_ELEMENT.equals(tagName)){
                    targetData = new ResourceRefMetaData();
                    targetData.importXML(childElement);
                }else if(STEP_REF_ELEMENT.equals(tagName)){
                    targetData = new StepRefMetaData();
                    targetData.importXML(childElement);
                }else if(VAR_ELEMENT.equals(tagName)){
                    targetData = new VarMetaData();
                    targetData.importXML(childElement);
                }else if(EXPRESSION_ELEMENT.equals(tagName)){
                    targetData = new ExpressionMetaData();
                    targetData.importXML(childElement);
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of target tag : " + tagName
                    );
                }
            }
            
            final Iterator<Element> children = getChildrenWithoutTagName(
                element,
                new String[]{TARGET_ELEMENT}
            );
            boolean isReturn = false;
            while(children.hasNext()){
                final Element currentElement = children.next();
                String tagName = currentElement.getTagName();
                if(isReturn){
                    throw new DeploymentException("Unreachable element : " + tagName);
                }
                Step stepObj = null;
                if(STEP_ELEMENT.equals(tagName)){
                    StepMetaData step = new StepMetaData(this);
                    step.importXML(currentElement);
                    stepObj = step;
                }else if(SWITCH_ELEMENT.equals(tagName)){
                    SwitchMetaData sw = new SwitchMetaData(this);
                    sw.importXML(currentElement);
                    stepObj = sw;
                }else if(IF_ELEMENT.equals(tagName)){
                    IfMetaData ifData = new IfMetaData(this);
                    ifData.importXML(currentElement);
                    stepObj = ifData;
                }else if(CALL_FLOW_ELEMENT.equals(tagName)){
                    CallFlowMetaData callFlowData = new CallFlowMetaData(this);
                    callFlowData.importXML(currentElement);
                    stepObj = callFlowData;
                }else if(REPLY_ELEMENT.equals(tagName)){
                    GetAsynchReplyMetaData replyData = new GetAsynchReplyMetaData(this);
                    replyData.importXML(currentElement);
                    stepObj = replyData;
                }else if(FOR_ELEMENT.equals(tagName)){
                    ForMetaData forData = new ForMetaData(this);
                    forData.importXML(currentElement);
                    stepObj = forData;
                }else if(WHILE_ELEMENT.equals(tagName)){
                    WhileMetaData whileData = new WhileMetaData(this);
                    whileData.importXML(currentElement);
                    stepObj = whileData;
                }else if(BREAK_ELEMENT.equals(tagName)){
                    BreakMetaData breakData = new BreakMetaData();
                    breakData.importXML(currentElement);
                    stepObj = breakData;
                    isReturn = true;
                }else if(CONTINUE_ELEMENT.equals(tagName)){
                    ContinueMetaData continueData = new ContinueMetaData();
                    continueData.importXML(currentElement);
                    stepObj = continueData;
                    isReturn = true;
                }else if(RETURN_ELEMENT.equals(tagName)){
                    ReturnMetaData returnData = new ReturnMetaData(this);
                    returnData.importXML(currentElement);
                    stepObj = returnData;
                    isReturn = true;
                }else if(THROW_ELEMENT.equals(tagName)){
                    ThrowMetaData throwData = new ThrowMetaData();
                    throwData.importXML(currentElement);
                    stepObj = throwData;
                    isReturn = true;
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of if : " + tagName
                    );
                }
                if(steps == null){
                    steps = new ArrayList<Step>();
                }
                if(stepObj != null){
                    steps.add(stepObj);
                }
            }
            if(steps == null){
                throw new DeploymentException("for body is empty.");
            }
        }
        
        @SuppressWarnings("unchecked")
        public StepContext invokeStep(FlowContext context) throws Exception{
            final Object target = targetData == null ? null : ((ReturnValue)targetData).getValue(context);
            StepContext stepContext = null;
            int beginVal = begin;
            Journal journal = getJournal(ForMetaData.this);
            if(beginExp != null){
                Object val = beginExp.getValue(context);
                if(val instanceof Number){
                    beginVal = ((Number)val).intValue();
                }else{
                    throw new IllegalArgumentException("Expression of 'begin' is not number.");
                }
            }
            int endVal = end;
            if(endExp != null){
                Object val = endExp.getValue(context);
                if(val instanceof Number){
                    endVal = ((Number)val).intValue();
                }else{
                    throw new IllegalArgumentException("Expression of 'end' is not number.");
                }
            }
            try{
                if(journal != null){
                    journal.startJournal(
                        JOURNAL_KEY_FOR,
                        factory.getEditorFinder()
                    );
                }
                if(journal != null && isTargetJournal){
                    journal.addInfo(
                        JOURNAL_KEY_STEP_TARGET,
                        target
                    );
                }
                if(target == null){
                    for(int i = beginVal; i < endVal; i++){
                        if(indexName != null){
                            context.setVar(indexName, new Integer(i));
                        }
                        for(int j = 0, jmax = steps.size(); j < jmax; j++){
                            Step step = steps.get(j);
                            stepContext = step.invokeStep(context);
                            if(stepContext == null){
                                return null;
                            }
                            if(stepContext.isContinue){
                                stepContext.isContinue = false;
                                break;
                            }else if(stepContext.isBreak){
                                stepContext.isBreak = false;
                                return stepContext;
                            }
                        }
                    }
                }else if(target.getClass().isArray()){
                    int length = Array.getLength(target);
                    if(beginVal < length){
                        for(int i = beginVal, imax = (endVal > 0 && endVal < length) ? endVal : length; i < imax; i++){
                            if(indexName != null){
                                context.setVar(indexName, new Integer(i));
                            }
                            final Object var = Array.get(target, i);
                            context.setVar(varName, var);
                            for(int j = 0, jmax = steps.size(); j < jmax; j++){
                                Step step = steps.get(j);
                                stepContext = step.invokeStep(context);
                                if(stepContext == null){
                                    return null;
                                }
                                if(stepContext.isContinue){
                                    stepContext.isContinue = false;
                                    break;
                                }else if(stepContext.isBreak){
                                    stepContext.isBreak = false;
                                    return stepContext;
                                }
                            }
                        }
                    }
                }else if(target instanceof ResultSet){
                    final ResultSet resultSet = (ResultSet)target;
                    int index = 0;
                    while(resultSet.next()){
                        if(indexName != null){
                            context.setVar(indexName, new Integer(index));
                        }
                        context.setVar(varName, resultSet);
                        if(index >= beginVal){
                            for(int j = 0, jmax = steps.size(); j < jmax; j++){
                                Step step = steps.get(j);
                                stepContext = step.invokeStep(context);
                                if(stepContext == null){
                                    return null;
                                }
                                if(stepContext.isContinue){
                                    stepContext.isContinue = false;
                                    break;
                                }else if(stepContext.isBreak){
                                    stepContext.isBreak = false;
                                    return stepContext;
                                }
                            }
                        }
                        if(endVal != -1 && index >= endVal){
                            break;
                        }
                        index++;
                    }
                }else if(target instanceof PersistentManager.Cursor){
                    final PersistentManager.Cursor cursor = (PersistentManager.Cursor)target;
                    int index = 0;
                    while(cursor.next()){
                        if(indexName != null){
                            context.setVar(indexName, new Integer(index));
                        }
                        context.setVar(varName, cursor);
                        if(index >= beginVal){
                            for(int j = 0, jmax = steps.size(); j < jmax; j++){
                                Step step = steps.get(j);
                                stepContext = step.invokeStep(context);
                                if(stepContext == null){
                                    return null;
                                }
                                if(stepContext.isContinue){
                                    stepContext.isContinue = false;
                                    break;
                                }else if(stepContext.isBreak){
                                    stepContext.isBreak = false;
                                    return stepContext;
                                }
                            }
                        }
                        if(endVal != -1 && index >= endVal){
                            break;
                        }
                        index++;
                    }
                }else if(target instanceof Enumeration){
                    final Enumeration<Object> enumeration = (Enumeration<Object>)target;
                    int index = 0;
                    while(enumeration.hasMoreElements()){
                        if(indexName != null){
                            context.setVar(indexName, new Integer(index));
                        }
                        final Object var = enumeration.nextElement();
                        context.setVar(varName, var);
                        if(index >= beginVal){
                            for(int j = 0, jmax = steps.size(); j < jmax; j++){
                                Step step = steps.get(j);
                                stepContext = step.invokeStep(context);
                                if(stepContext == null){
                                    return null;
                                }
                                if(stepContext.isContinue){
                                    stepContext.isContinue = false;
                                    break;
                                }else if(stepContext.isBreak){
                                    stepContext.isBreak = false;
                                    return stepContext;
                                }
                            }
                        }
                        if(endVal != -1 && index >= endVal){
                            break;
                        }
                        index++;
                    }
                }else if(target instanceof RandomAccess && target instanceof List){
                    final List<Object> list = (List<Object>)target;
                    int length = list.size();
                    for(int i = 0, imax = (endVal > 0 && endVal < length) ? endVal : length; i < imax; i++){
                        if(indexName != null){
                            context.setVar(indexName, new Integer(i));
                        }
                        final Object var = list.get(i);
                        context.setVar(varName, var);
                        if(i >= beginVal){
                            for(int j = 0, jmax = steps.size(); j < jmax; j++){
                                Step step = steps.get(j);
                                stepContext = step.invokeStep(context);
                                if(stepContext == null){
                                    return null;
                                }
                                if(stepContext.isContinue){
                                    stepContext.isContinue = false;
                                    break;
                                }else if(stepContext.isBreak){
                                    stepContext.isBreak = false;
                                    return stepContext;
                                }
                            }
                        }
                    }
                }else{
                    final Collection<Object> col = (Collection<Object>)target;
                    int index = 0;
                    final Iterator<Object> itr = col.iterator();
                    while(itr.hasNext()){
                        if(indexName != null){
                            context.setVar(indexName, new Integer(index));
                        }
                        final Object var = itr.next();
                        context.setVar(varName, var);
                        if(index >= beginVal){
                            for(int j = 0, jmax = steps.size(); j < jmax; j++){
                                Step step = steps.get(j);
                                stepContext = step.invokeStep(context);
                                if(stepContext == null){
                                    return null;
                                }
                                if(stepContext.isContinue){
                                    stepContext.isContinue = false;
                                    break;
                                }else if(stepContext.isBreak){
                                    stepContext.isBreak = false;
                                    return stepContext;
                                }
                            }
                        }
                        if(endVal != -1 && index >= endVal){
                            break;
                        }
                        index++;
                    }
                }
            }finally{
                if(journal != null){
                    journal.endJournal();
                }
            }
            if(stepContext == null){
                stepContext = new StepContext();
            }
            return stepContext;
        }
    }
    
    protected class WhileMetaData extends MetaData implements Step, Journaling{
        
        private static final long serialVersionUID = -2324182374245423781L;
        
        protected Test test;
        protected List<Step> steps;
        protected boolean isJournal = true;
        protected boolean isDo = false;
        
        public WhileMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            final String nullCheckAttribute = getOptionalAttribute(
                element,
                NULLCHECK_ATTRIBUTE
            );
            boolean nullCheck = false;
            if(nullCheckAttribute != null){
                nullCheck = Boolean.valueOf(nullCheckAttribute).booleanValue();
            }
            
            final String testAttribute = getOptionalAttribute(
                element,
                TEST_ATTRIBUTE
            );
            if(testAttribute != null){
                try{
                    test = new Test(factory.replaceProperty(testAttribute), nullCheck);
                }catch(Exception e){
                    throw new DeploymentException(e);
                }
            }
            final String doAttribute = getOptionalAttribute(
                element,
                DO_ATTRIBUTE
            );
            if(doAttribute != null){
                isDo = Boolean.valueOf(doAttribute).booleanValue();
            }
            final String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            String tagName = null;
            boolean isReturn = false;
            final Iterator<Element> children = getChildren(element);
            while(children.hasNext()){
                final Element currentElement = children.next();
                tagName = currentElement.getTagName();
                if(isReturn){
                    throw new DeploymentException("Unreachable element : " + tagName);
                }
                Step stepObj = null;
                if(STEP_ELEMENT.equals(tagName)){
                    StepMetaData step = new StepMetaData(this);
                    step.importXML(currentElement);
                    stepObj = step;
                }else if(SWITCH_ELEMENT.equals(tagName)){
                    SwitchMetaData sw = new SwitchMetaData(this);
                    sw.importXML(currentElement);
                    stepObj = sw;
                }else if(IF_ELEMENT.equals(tagName)){
                    IfMetaData ifData = new IfMetaData(this);
                    ifData.importXML(currentElement);
                    stepObj = ifData;
                }else if(FOR_ELEMENT.equals(tagName)){
                    ForMetaData forData = new ForMetaData(this);
                    forData.importXML(currentElement);
                    stepObj = forData;
                }else if(WHILE_ELEMENT.equals(tagName)){
                    WhileMetaData whileData = new WhileMetaData(this);
                    whileData.importXML(currentElement);
                    stepObj = whileData;
                }else if(CALL_FLOW_ELEMENT.equals(tagName)){
                    CallFlowMetaData callFlowData = new CallFlowMetaData(this);
                    callFlowData.importXML(currentElement);
                    stepObj = callFlowData;
                }else if(REPLY_ELEMENT.equals(tagName)){
                    GetAsynchReplyMetaData replyData = new GetAsynchReplyMetaData(this);
                    replyData.importXML(currentElement);
                    stepObj = replyData;
                }else if(BREAK_ELEMENT.equals(tagName)){
                    BreakMetaData breakData = new BreakMetaData();
                    breakData.importXML(currentElement);
                    stepObj = breakData;
                    isReturn = true;
                }else if(CONTINUE_ELEMENT.equals(tagName)){
                    ContinueMetaData continueData = new ContinueMetaData();
                    continueData.importXML(currentElement);
                    stepObj = continueData;
                    isReturn = true;
                }else if(RETURN_ELEMENT.equals(tagName)){
                    ReturnMetaData returnData = new ReturnMetaData(this);
                    returnData.importXML(currentElement);
                    stepObj = returnData;
                    isReturn = true;
                }else if(THROW_ELEMENT.equals(tagName)){
                    ThrowMetaData throwData = new ThrowMetaData();
                    throwData.importXML(currentElement);
                    stepObj = throwData;
                    isReturn = true;
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of if : " + tagName
                    );
                }
                if(steps == null){
                    steps = new ArrayList<Step>();
                }
                steps.add(stepObj);
            }
            if(steps == null){
                throw new DeploymentException("if body is empty.");
            }
        }
        
        public boolean isMatch(FlowContext context) throws Exception{
            return test == null ? true : test.evaluate(context);
        }
        
        public StepContext invokeStep(FlowContext context) throws Exception{
            StepContext stepContext = new StepContext();
            Journal journal = getJournal(WhileMetaData.this);
            try{
                if(journal != null){
                    journal.startJournal(
                        JOURNAL_KEY_WHILE,
                        factory.getEditorFinder()
                    );
                }
                if(isDo){
                    do{
                        Iterator<Step> itr = steps.iterator();
                        while(itr.hasNext()){
                            Step step = itr.next();
                            stepContext = step.invokeStep(context);
                            if(stepContext == null){
                                return null;
                            }else if(stepContext.isContinue || stepContext.isBreak){
                                return stepContext;
                            }
                        }
                        if(journal != null){
                            journal.addInfo(
                                JOURNAL_KEY_TEST,
                                test == null ? null : ('"' + test.toString() + '"')
                            );
                        }
                    }while(isMatch(context));
                }else{
                    while(isMatch(context)){
                        if(journal != null){
                            journal.addInfo(
                                JOURNAL_KEY_TEST,
                                test == null ? null : ('"' + test.toString() + '"')
                            );
                        }
                        Iterator<Step> itr = steps.iterator();
                        while(itr.hasNext()){
                            Step step = itr.next();
                            stepContext = step.invokeStep(context);
                            if(stepContext == null){
                                return null;
                            }else if(stepContext.isContinue || stepContext.isBreak){
                                return stepContext;
                            }
                        }
                    }
                }
            }finally{
                if(journal != null){
                    journal.endJournal();
                }
            }
            return stepContext;
        }
    }
    
    protected class SwitchMetaData extends MetaData implements Step, Journaling{
        
        private static final long serialVersionUID = -5017056531829628019L;
        
        protected List<IfMetaData> cases = new ArrayList<IfMetaData>();
        protected IfMetaData defaultData;
        protected boolean isJournal = true;
        
        public SwitchMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            final String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            final Iterator<Element> children = getChildren(element);
            while(children.hasNext()){
                final Element currentElement = children.next();
                final String tagName = currentElement.getTagName();
                if(CASE_ELEMENT.equals(tagName)){
                    IfMetaData ifData = new IfMetaData(this);
                    ifData.importXML(currentElement);
                    cases.add(ifData);
                }else if(DEFAULT_ELEMENT.equals(tagName)){
                    IfMetaData ifData = new IfMetaData(this);
                    ifData.importXML(currentElement);
                    defaultData = ifData;
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of switch : " + tagName
                    );
                }
            }
            if(cases.size() == 0){
                throw new DeploymentException(
                    "Case tag dose not exist."
                );
            }
        }
        
        public StepContext invokeStep(FlowContext context) throws Exception{
            final Iterator<IfMetaData> itr = cases.iterator();
            while(itr.hasNext()){
                IfMetaData ifData = itr.next();
                if(ifData.isMatch(context)){
                    return ifData.invokeStep(context);
                }
            }
            if(defaultData == null){
                return new StepContext();
            }else{
                return defaultData.invokeStep(context);
            }
        }
    }
    
    protected class IfMetaData extends MetaData implements Step, Journaling{
        
        private static final long serialVersionUID = -7154397109317362880L;
        
        protected Test test;
        protected List<Step> steps;
        protected boolean isJournal = true;
        
        public IfMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            String tagName = element.getTagName();
            if(!DEFAULT_ELEMENT.equals(tagName)){
                final String nullCheckAttribute = getOptionalAttribute(
                    element,
                    NULLCHECK_ATTRIBUTE
                );
                boolean nullCheck = false;
                if(nullCheckAttribute != null){
                    nullCheck = Boolean.valueOf(nullCheckAttribute).booleanValue();
                }
                final String testAttribute = getUniqueAttribute(
                    element,
                    TEST_ATTRIBUTE
                );
                try{
                    test = new Test(factory.replaceProperty(testAttribute), nullCheck);
                }catch(Exception e){
                    throw new DeploymentException("Attribute of test is illegal. test=" + testAttribute, e);
                }
            }
            final String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            boolean isReturn = false;
            final Iterator<Element> children = getChildren(element);
            while(children.hasNext()){
                final Element currentElement = children.next();
                tagName = currentElement.getTagName();
                if(isReturn){
                    throw new DeploymentException("Unreachable element : " + tagName + ", test=" + test);
                }
                Step stepObj = null;
                if(STEP_ELEMENT.equals(tagName)){
                    StepMetaData step = new StepMetaData(this);
                    step.importXML(currentElement);
                    stepObj = step;
                }else if(SWITCH_ELEMENT.equals(tagName)){
                    SwitchMetaData sw = new SwitchMetaData(this);
                    sw.importXML(currentElement);
                    stepObj = sw;
                }else if(IF_ELEMENT.equals(tagName)){
                    IfMetaData ifData = new IfMetaData(this);
                    ifData.importXML(currentElement);
                    stepObj = ifData;
                }else if(FOR_ELEMENT.equals(tagName)){
                    ForMetaData forData = new ForMetaData(this);
                    forData.importXML(currentElement);
                    stepObj = forData;
                }else if(WHILE_ELEMENT.equals(tagName)){
                    WhileMetaData whileData = new WhileMetaData(this);
                    whileData.importXML(currentElement);
                    stepObj = whileData;
                }else if(CALL_FLOW_ELEMENT.equals(tagName)){
                    CallFlowMetaData callFlowData = new CallFlowMetaData(this);
                    callFlowData.importXML(currentElement);
                    stepObj = callFlowData;
                }else if(REPLY_ELEMENT.equals(tagName)){
                    GetAsynchReplyMetaData replyData = new GetAsynchReplyMetaData(this);
                    replyData.importXML(currentElement);
                    stepObj = replyData;
                }else if(BREAK_ELEMENT.equals(tagName)){
                    BreakMetaData breakData = new BreakMetaData();
                    breakData.importXML(currentElement);
                    stepObj = breakData;
                    isReturn = true;
                }else if(CONTINUE_ELEMENT.equals(tagName)){
                    ContinueMetaData continueData = new ContinueMetaData();
                    continueData.importXML(currentElement);
                    stepObj = continueData;
                    isReturn = true;
                }else if(RETURN_ELEMENT.equals(tagName)){
                    ReturnMetaData returnData = new ReturnMetaData(this);
                    returnData.importXML(currentElement);
                    stepObj = returnData;
                    isReturn = true;
                }else if(THROW_ELEMENT.equals(tagName)){
                    ThrowMetaData throwData = new ThrowMetaData();
                    throwData.importXML(currentElement);
                    stepObj = throwData;
                    isReturn = true;
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of if : " + tagName
                    );
                }
                if(steps == null){
                    steps = new ArrayList<Step>();
                }
                steps.add(stepObj);
            }
        }
        
        public boolean isMatch(FlowContext context) throws Exception{
            return test == null ? true : test.evaluate(context);
        }
        
        public StepContext invokeStep(FlowContext context) throws Exception{
            StepContext stepContext = null;
            Journal journal = getJournal(IfMetaData.this);
            try{
                if(journal != null){
                    journal.startJournal(
                        JOURNAL_KEY_IF,
                        factory.getEditorFinder()
                    );
                }
                final boolean isMatch = isMatch(context);
                if(journal != null){
                    journal.addInfo(
                        JOURNAL_KEY_TEST,
                        test == null ? DEFAULT_ELEMENT : ('"' + test.toString() + "\"=" + isMatch) 
                    );
                }
                if(steps != null && isMatch){
                    Iterator<Step> itr = steps.iterator();
                    while(itr.hasNext()){
                        Step step = itr.next();
                        stepContext = step.invokeStep(context);
                        if(stepContext == null){
                            return null;
                        }else if(stepContext.isContinue || stepContext.isBreak){
                            return stepContext;
                        }
                    }
                }else{
                    stepContext = new StepContext();
                }
            }finally{
                if(journal != null){
                    journal.endJournal();
                }
            }
            return stepContext;
        }
    }
    
    protected class CatchMetaData extends MetaData implements Step, Journaling{
        
        private static final long serialVersionUID = 2837596969479923991L;
        
        protected Class<?> catchExceptionClass = Exception.class;
        protected List<Step> steps;
        protected String varName;
        protected boolean isJournal = true;
        
        public CatchMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            final String exceptionAttribute = MetaData.getOptionalAttribute(
                element,
                EXCEPTION_ATTRIBUTE
            );
            if(exceptionAttribute != null){
                try{
                    catchExceptionClass = Utility.convertStringToClass(
                        exceptionAttribute
                    );
                }catch(ClassNotFoundException e){
                    throw new DeploymentException(e);
                }
            }
            
            varName = getOptionalAttribute(element, VAR_ATTRIBUTE);
            final String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            
            boolean isReturn = false;
            final Iterator<Element> children = getChildren(element);
            while(children.hasNext()){
                final Element currentElement = children.next();
                final String tagName = currentElement.getTagName();
                if(isReturn){
                    throw new DeploymentException("Unreachable element : " + tagName);
                }
                Step stepObj = null;
                if(STEP_ELEMENT.equals(tagName)){
                    StepMetaData step = new StepMetaData(this);
                    step.importXML(currentElement);
                    stepObj = step;
                }else if(SWITCH_ELEMENT.equals(tagName)){
                    SwitchMetaData sw = new SwitchMetaData(this);
                    sw.importXML(currentElement);
                    stepObj = sw;
                }else if(IF_ELEMENT.equals(tagName)){
                    IfMetaData ifData = new IfMetaData(this);
                    ifData.importXML(currentElement);
                    stepObj = ifData;
                }else if(FOR_ELEMENT.equals(tagName)){
                    ForMetaData forData = new ForMetaData(this);
                    forData.importXML(currentElement);
                    stepObj = forData;
                }else if(WHILE_ELEMENT.equals(tagName)){
                    WhileMetaData whileData = new WhileMetaData(this);
                    whileData.importXML(currentElement);
                    stepObj = whileData;
                }else if(CALL_FLOW_ELEMENT.equals(tagName)){
                    CallFlowMetaData callFlowData = new CallFlowMetaData(this);
                    callFlowData.importXML(currentElement);
                    stepObj = callFlowData;
                }else if(REPLY_ELEMENT.equals(tagName)){
                    GetAsynchReplyMetaData replyData = new GetAsynchReplyMetaData(this);
                    replyData.importXML(currentElement);
                    stepObj = replyData;
                }else if(BREAK_ELEMENT.equals(tagName)){
                    BreakMetaData breakData = new BreakMetaData();
                    breakData.importXML(currentElement);
                    stepObj = breakData;
                    isReturn = true;
                }else if(CONTINUE_ELEMENT.equals(tagName)){
                    ContinueMetaData continueData = new ContinueMetaData();
                    continueData.importXML(currentElement);
                    stepObj = continueData;
                    isReturn = true;
                }else if(RETURN_ELEMENT.equals(tagName)){
                    ReturnMetaData returnData = new ReturnMetaData(this);
                    returnData.importXML(currentElement);
                    stepObj = returnData;
                    isReturn = true;
                }else if(THROW_ELEMENT.equals(tagName)){
                    ThrowMetaData throwData = new ThrowMetaData();
                    throwData.importXML(currentElement);
                    stepObj = throwData;
                    isReturn = true;
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of if : " + tagName
                    );
                }
                if(steps == null){
                    steps = new ArrayList<Step>();
                }
                steps.add(stepObj);
            }
        }
        
        public boolean isMatch(FlowContext context, Exception exception){
            if(catchExceptionClass.isAssignableFrom(exception.getClass())){
                if(varName != null){
                    context.setVar(varName, exception);
                }
                return true;
            }
            return false;
        }
        
        public StepContext invokeStep(FlowContext context) throws Exception{
            final StepContext inStepContext = context.current;
            StepContext stepContext = null;
            Journal journal = getJournal(CatchMetaData.this);
            try{
                if(journal != null){
                    journal.startJournal(
                        JOURNAL_KEY_CATCH,
                        factory.getEditorFinder()
                    );
                    Exception exception = (Exception)context.getVar(varName);
                    journal.addInfo(JOURNAL_KEY_CATCH_EXCEPTION, exception);
                }
                if(steps != null){
                    Iterator<Step> itr = steps.iterator();
                    while(itr.hasNext()){
                        Step step = itr.next();
                        stepContext = step.invokeStep(context);
                        if(stepContext == null){
                            return null;
                        }else if(stepContext.isContinue || stepContext.isBreak){
                            return stepContext;
                        }
                    }
                    context.current = inStepContext;
                }
            }finally{
                if(journal != null){
                    journal.endJournal();
                }
            }
            return inStepContext;
        }
    }
    
    protected class FinallyMetaData extends MetaData implements Step, Journaling{
        
        private static final long serialVersionUID = -81748614779496143L;
        
        protected List<Step> steps;
        protected boolean isJournal = true;
        
        public FinallyMetaData(MetaData parent){
            super(parent);
        }
        
        public boolean isJournal(){
            return isJournal;
        }
        
        public void importXML(Element element) throws DeploymentException{
            final String journalStr = MetaData.getOptionalAttribute(
                element,
                JOURNAL_ATTRIBUTE
            );
            if(journalStr != null){
                isJournal = Boolean.valueOf(journalStr).booleanValue();
            }
            boolean isReturn = false;
            final Iterator<Element> children = getChildren(element);
            while(children.hasNext()){
                final Element currentElement = children.next();
                final String tagName = currentElement.getTagName();
                if(isReturn){
                    throw new DeploymentException("Unreachable element : " + tagName);
                }
                Step stepObj = null;
                if(STEP_ELEMENT.equals(tagName)){
                    StepMetaData step = new StepMetaData(this);
                    step.importXML(currentElement);
                    stepObj = step;
                }else if(SWITCH_ELEMENT.equals(tagName)){
                    SwitchMetaData sw = new SwitchMetaData(this);
                    sw.importXML(currentElement);
                    stepObj = sw;
                }else if(IF_ELEMENT.equals(tagName)){
                    IfMetaData ifData = new IfMetaData(this);
                    ifData.importXML(currentElement);
                    stepObj = ifData;
                }else if(FOR_ELEMENT.equals(tagName)){
                    ForMetaData forData = new ForMetaData(this);
                    forData.importXML(currentElement);
                    stepObj = forData;
                }else if(WHILE_ELEMENT.equals(tagName)){
                    WhileMetaData whileData = new WhileMetaData(this);
                    whileData.importXML(currentElement);
                    stepObj = whileData;
                }else if(CALL_FLOW_ELEMENT.equals(tagName)){
                    CallFlowMetaData callFlowData = new CallFlowMetaData(this);
                    callFlowData.importXML(currentElement);
                    stepObj = callFlowData;
                }else if(REPLY_ELEMENT.equals(tagName)){
                    GetAsynchReplyMetaData replyData = new GetAsynchReplyMetaData(this);
                    replyData.importXML(currentElement);
                    stepObj = replyData;
                }else if(BREAK_ELEMENT.equals(tagName)){
                    BreakMetaData breakData = new BreakMetaData();
                    breakData.importXML(currentElement);
                    stepObj = breakData;
                    isReturn = true;
                }else if(CONTINUE_ELEMENT.equals(tagName)){
                    ContinueMetaData continueData = new ContinueMetaData();
                    continueData.importXML(currentElement);
                    stepObj = continueData;
                    isReturn = true;
                }else if(RETURN_ELEMENT.equals(tagName)){
                    ReturnMetaData returnData = new ReturnMetaData(this);
                    returnData.importXML(currentElement);
                    stepObj = returnData;
                    isReturn = true;
                }else if(THROW_ELEMENT.equals(tagName)){
                    ThrowMetaData throwData = new ThrowMetaData();
                    throwData.importXML(currentElement);
                    stepObj = throwData;
                    isReturn = true;
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of if : " + tagName
                    );
                }
                if(steps == null){
                    steps = new ArrayList<Step>();
                }
                steps.add(stepObj);
            }
            if(steps == null){
                throw new DeploymentException("if body is empty.");
            }
        }
        
        public StepContext invokeStep(FlowContext context) throws Exception{
            Journal journal = getJournal(FinallyMetaData.this);
            final StepContext inStepContext = context.current;
            StepContext stepContext = null;
            try{
                if(journal != null){
                    journal.startJournal(
                        JOURNAL_KEY_FINALLY,
                        factory.getEditorFinder()
                    );
                }
                Iterator<Step> itr = steps.iterator();
                while(itr.hasNext()){
                    Step step = itr.next();
                    stepContext = step.invokeStep(context);
                    if(stepContext == null){
                        return null;
                    }else if(stepContext.isContinue || stepContext.isBreak){
                        return stepContext;
                    }
                }
                context.current = inStepContext;
            }finally{
                if(journal != null){
                    journal.endJournal();
                }
            }
            return inStepContext;
        }
    }
    
    protected class ThrowMetaData extends MetaData implements Step{
        
        private static final long serialVersionUID = 6963691939437133793L;
        
        protected String varName;
        
        protected ReturnValue throwValue;
        
        public ThrowMetaData(){}
        
        public void importXML(Element element) throws DeploymentException{
            varName = getOptionalAttribute(element, VAR_ATTRIBUTE);
            if(varName == null){
                final Element throwElement = MetaData.getUniqueChild(element);
                final String tagName = throwElement.getTagName();
                MetaData targetData = null;
                if(ObjectMetaData.OBJECT_TAG_NAME.equals(tagName)){
                    targetData = new ObjectMetaData(this);
                    targetData.importXML(throwElement);
                }else if(STEP_REF_ELEMENT.equals(tagName)){
                    targetData = new StepRefMetaData();
                    targetData.importXML(throwElement);
                }else if(VAR_ELEMENT.equals(tagName)){
                    targetData = new VarMetaData();
                    targetData.importXML(throwElement);
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of result tag : " + tagName
                    );
                }
                throwValue = (ReturnValue)targetData;
            }
        }
        
        public StepContext invokeStep(FlowContext context) throws Exception{
            if(varName != null){
                throw (Exception)context.getVar(varName);
            }else{
                throw (Exception)throwValue.getValue(context);
            }
        }
    }
    
    protected class ReturnMetaData extends MetaData implements Step{
        
        private static final long serialVersionUID = -714587366723247275L;
        
        protected Object retValue;
        
        public ReturnMetaData(MetaData parent){
            super(parent);
        }
        
        public void importXML(Element element) throws DeploymentException{
            final Element retElement = MetaData.getOptionalChild(element);
            if(retElement == null){
                retValue = getElementContent(element);
                if(retValue != null){
                    final PropertyEditor editor
                         = factory.findEditor(String.class);
                    if(editor != null){
                        editor.setAsText(
                            factory.replaceProperty((String)retValue)
                        );
                        retValue = editor.getValue();
                    }
                }
            }else{
                final String tagName = retElement.getTagName();
                MetaData targetData = null;
                if(INPUT_ELEMENT.equals(tagName)){
                    targetData = new InputMetaData();
                    targetData.importXML(retElement);
                }else if(ObjectMetaData.OBJECT_TAG_NAME.equals(tagName)){
                    targetData = new ObjectMetaData(this);
                    targetData.importXML(retElement);
                }else if(ServiceRefMetaData.SERIVCE_REF_TAG_NAME.equals(tagName)){
                    targetData = new ServiceRefMetaData(this);
                    targetData.importXML(retElement);
                }else if(StaticInvokeMetaData.STATIC_INVOKE_TAG_NAME.equals(tagName)){
                    targetData = new StaticInvokeMetaData(this);
                    targetData.importXML(retElement);
                }else if(StaticFieldRefMetaData.STATIC_FIELD_REF_TAG_NAME.equals(tagName)){
                    targetData = new StaticFieldRefMetaData(this);
                    targetData.importXML(retElement);
                }else if(RESOURCE_REF_ELEMENT.equals(tagName)){
                    targetData = new ResourceRefMetaData();
                    targetData.importXML(retElement);
                }else if(STEP_REF_ELEMENT.equals(tagName)){
                    targetData = new StepRefMetaData();
                    targetData.importXML(retElement);
                }else if(VAR_ELEMENT.equals(tagName)){
                    targetData = new VarMetaData();
                    targetData.importXML(retElement);
                }else if(EXPRESSION_ELEMENT.equals(tagName)){
                    targetData = new ExpressionMetaData();
                    targetData.importXML(retElement);
                }else{
                    throw new DeploymentException(
                        "Invalid child tag of result tag : " + tagName
                    );
                }
                retValue = targetData;
            }
        }
        
        public StepContext invokeStep(FlowContext context) throws Exception{
            if(retValue != null){
                Object ret = (retValue instanceof ReturnValue) ? ((ReturnValue)retValue).getValue(context) : retValue;
                if(ret != null){
                    if(context.current == null){
                        context.current = new StepContext();
                    }
                    context.current.result = ret;
                }
            }
            return null;
        }
    }
    
    protected static class BreakMetaData extends MetaData implements Step{
        
        private static final long serialVersionUID = 7546910423657644942L;
        
        public BreakMetaData(){}
        
        public void importXML(Element element) throws DeploymentException{
        }
        
        public StepContext invokeStep(FlowContext context) throws Exception{
            StepContext stepContext = context.current;
            if(stepContext == null){
                stepContext = new StepContext();
            }
            stepContext.isBreak = true;
            return stepContext;
        }
    }
    
    protected static class ContinueMetaData extends MetaData implements Step{
        
        private static final long serialVersionUID = 5508331194991502650L;
        
        public ContinueMetaData(){}
        
        public void importXML(Element element) throws DeploymentException{
        }
        
        public StepContext invokeStep(FlowContext context) throws Exception{
            StepContext stepContext = context.current;
            if(stepContext == null){
                stepContext = new StepContext();
            }
            stepContext.isContinue = true;
            return stepContext;
        }
    }
    
    protected class ExpressionMetaData extends MetaData implements ReturnValue{
        
        private static final long serialVersionUID = -7154397109317362889L;
        
        protected static final String INPUT = "input";
        protected static final String THIS = "this";
        protected static final String VAR = "var";
        protected static final String TARGET = "target";
        protected static final String RESULT = "result";
        protected static final String PROP_FUNCTION_NAME = "prop";
        protected static final String STATIC_FUNCTION_NAME = "static";
        
        protected transient Expression expression;
        protected transient CompiledInterpreter compiledInterpreter;
        protected String expressionStr;
        protected boolean nullCheck;
        
        public ExpressionMetaData(){}
        
        public void importXML(Element element) throws DeploymentException{
            final String nullCheckAttribute = getOptionalAttribute(
                element,
                NULLCHECK_ATTRIBUTE
            );
            if(nullCheckAttribute != null){
                nullCheck = Boolean.valueOf(nullCheckAttribute).booleanValue();
            }
            importString(getElementContent(element));
        }
        
        public void importString(String expressionStr) throws DeploymentException{
            if(expressionStr == null || expressionStr.length() == 0){
                throw new DeploymentException(
                    "Content of expression is null."
                );
            }
            if(factory.getExpressionInterpreter() == null){
                JexlEngine jexl = new JexlEngine();
                jexl.setSilent(true);
                Map<String, Object> funcs = new HashMap<String, Object>();
                PropertyAccess propAccess = new NullNestAllowPropertyAccess(nullCheck);
                propAccess.setIgnoreNullProperty(true);
                funcs.put(PROP_FUNCTION_NAME, propAccess);
                funcs.put(STATIC_FUNCTION_NAME, StaticAccess.getInstance());
                jexl.setFunctions(funcs);
                expression = jexl.createExpression(expressionStr);
            }else{
                if(factory.getExpressionInterpreter().isCompilable()){
                    compiledInterpreter = factory.getExpressionInterpreter().compile(expressionStr);
                }
            }
            this.expressionStr = expressionStr;
        }
        
        public Object getValue(FlowContext context) throws Exception{
            if(expression != null){
                JexlContext jexlContext = new MapContext();
                jexlContext.set(INPUT, context.getInput());
                jexlContext.set(THIS, context.getThis());
                jexlContext.set(VAR, context.vars);
                for(Map.Entry<String, StepContext> entry : context.entrySet()){
                    jexlContext.set(entry.getKey(), entry.getValue());
                }
                return expression.evaluate(jexlContext);
            }else{
                Map<String, Object> variables = new HashMap<String, Object>();
                variables.put(INPUT, context.getInput());
                variables.put(THIS, context.getThis());
                variables.put(VAR, context.vars);
                for(Map.Entry<String, StepContext> entry : context.entrySet()){
                    variables.put(entry.getKey(), entry.getValue());
                }
                if(compiledInterpreter != null){
                    return compiledInterpreter.evaluate(variables);
                }else{
                    return factory.getExpressionInterpreter().evaluate(expressionStr, variables);
                }
            }
        }
        
        private void readObject(ObjectInputStream in)
         throws IOException, ClassNotFoundException{
            in.defaultReadObject();
            try{
                importString(expressionStr);
            }catch(Exception e){
                // 起こらないはず
            }
        }
    }
    
    protected class InterpreterMetaData extends MetaData implements ReturnValue{
        
        private static final long serialVersionUID = 2985010128436933702L;
        
        protected static final String INPUT = "input";
        protected static final String TARGET = "target";
        protected static final String VAR = "var";
        protected static final String RESOURCE = "resource";
        
        protected String code;
        protected CompiledInterpreter compiled;
        
        public InterpreterMetaData(MetaData parent){
            super(parent);
        }
        
        public void importXML(Element element) throws DeploymentException{
            if(factory.getInterpreter() == null){
                throw new DeploymentException(
                    "Interpreter is null."
                );
            }
            code = getElementContent(element);
            if(code == null){
                throw new DeploymentException(
                    "Content of interpreter is null."
                );
            }
            Interpreter interpreter = factory.getInterpreter();
            if(interpreter.isCompilable()){
                try{
                    compiled = interpreter.compile(code);
                }catch(EvaluateException e){
                    throw new DeploymentException(e);
                }
            }
        }
        
        public Object getValue(FlowContext context) throws Exception{
            Map<String, Object> vars = new HashMap<String, Object>();
            vars.putAll(context);
            vars.put(INPUT, context.input);
            vars.put(TARGET, context.getThis());
            vars.put(VAR, context.vars);
            vars.put(RESOURCE, context.getResourceAccess());
            if(compiled == null){
                Interpreter interpreter = factory.getInterpreter();
                return interpreter.evaluate(code, vars);
            }else{
                return compiled.evaluate(vars);
            }
        }
    }
    
    /**
     * リソース情報管理クラス。<p>
     */
    protected static class ResourceInfo implements Serializable{
        
        private static final long serialVersionUID = 3152910895585634920L;
        
        public String name;
        public String key;
        public ServiceName serviceName;
        public boolean isTranControl;
        public boolean isTranClose;
    }
    
    /**
     * Beanフローのコンテキスト情報を保持するクラス。<p>
     *
     * @author M.Takata
     */
    public static class FlowContext extends HashMap<String, StepContext>{
        
        private static final long serialVersionUID = -5942654431725540562L;
        
        public Object input;
        public BeanFlowMonitor monitor;
        public StepContext current;
        public Map<String, Object> vars;
        
        protected Map<String, ResourceInfo> resourceInfoMap;
        protected Map<String, Resource<Object>> resourceMap;
        protected ResourceAccess resourceAccess;
        
        /**
         * インスタンスを生成する。<p>
         *
         * @param in Beanフローの入力
         * @param rm リソースマネージャ
         * @param monitor モニター
         */
        public FlowContext(Object in, Map<String, ResourceInfo> resources, BeanFlowMonitor monitor){
            input = in;
            resourceInfoMap = resources;
            this.monitor = monitor;
        }
        
        /**
         * 実行中のステップの対象オブジェクトを取得する。<p>
         *
         * @return 実行中のステップの対象オブジェクト
         */
        public Object getThis(){
            return current == null ? null : current.target;
        }
        
        /**
         * Beanフローの入力オブジェクトを取得する。<p>
         *
         * @return Beanフローの入力オブジェクト
         */
        public Object getInput(){
            return input;
        }
        
        /**
         * 指定された変数名のBeanフロー内変数を取得する。<p>
         *
         * @return Beanフロー内変数
         */
        public Object getVar(String name){
            if(vars == null){
                return null;
            }
            return vars.get(name);
        }
        
        /**
         * 指定された変数名のBeanフロー内変数を設定する。<p>
         *
         * @param name Beanフロー内変数名
         * @param var Beanフロー内変数
         */
        public void setVar(String name, Object var){
            if(vars == null){
                vars = new HashMap<String, Object>();
            }
            vars.put(name, var);
        }
        
        protected ResourceAccess getResourceAccess(){
            if(resourceAccess == null){
                resourceAccess = new ResourceAccess();
            }
            return resourceAccess;
        }
        
        public Resource<Object> getResource(String name) throws ResourceException{
            if(resourceInfoMap == null || !resourceInfoMap.containsKey(name)){
                return null;
            }
            Resource<Object> resource = null;
            if(resourceMap == null){
                resourceMap = new LinkedHashMap<String, Resource<Object>>();
            }else{
                resource = resourceMap.get(name);
            }
            if(resource == null){
                ResourceInfo info = resourceInfoMap.get(name);
                ResourceFactory<Object> factory = ServiceManagerFactory.getServiceObject(info.serviceName);
                resource = factory.makeResource(info.key);
                resourceMap.put(name, resource);
            }
            return resource;
        }
        
		public void commitResources(TransactionInfo info, boolean isBegin, TransactionManager tranManager)
         throws ResourceException, SecurityException, IllegalStateException, RollbackException,
                 HeuristicMixedException, HeuristicRollbackException, SystemException{
            if(resourceMap != null){
                for(Map.Entry<String, Resource<Object>> entry : resourceMap.entrySet()){
                    ResourceInfo resInfo = resourceInfoMap.get(entry.getKey());
                    if(!resInfo.isTranControl){
                        continue;
                    }
                    Resource<Object> resource = entry.getValue();
                    if(!(resource instanceof TransactionResource<?>)){
                        continue;
                    }
                    TransactionResource<Object> tranResource = (TransactionResource<Object>)resource;
                    if(tranResource.isManaged() && isBegin){
                        continue;
                    }
                    tranResource.commit();
                }
            }
            if(tranManager != null){
                tranManager.commit();
            }
        }
        
        public void rollbackResources(TransactionInfo info, boolean isBegin, TransactionManager tranManager)
         throws ResourceException, IllegalStateException, SecurityException, SystemException{
            ResourceException re = null;
            if(resourceMap != null){
                for(Map.Entry<String, Resource<Object>> entry : resourceMap.entrySet()){
                    ResourceInfo resInfo = resourceInfoMap.get(entry.getKey());
                    if(!resInfo.isTranControl){
                        continue;
                    }
                    Resource<Object> resource = entry.getValue();
                    if(!(resource instanceof TransactionResource<?>)){
                        continue;
                    }
                    TransactionResource<?> tranResource = (TransactionResource<?>)resource;
                    if(tranResource.isManaged() && isBegin){
                        continue;
                    }
                    try{
                        tranResource.rollback();
                    }catch(ResourceException e){
                        re = e;
                    }
                }
            }
            if(tranManager != null){
                tranManager.rollback();
            }
            if(re != null){
                throw re;
            }
        }
        
        public void closeResources(){
            if(resourceMap != null){
                for(Map.Entry<String, Resource<Object>> entry : resourceMap.entrySet()){
                    ResourceInfo resInfo = resourceInfoMap.get(entry.getKey());
                    if(!resInfo.isTranClose){
                        continue;
                    }
                    Resource<Object> resource = entry.getValue();
                    if(!(resource instanceof ClosableResource<?>)){
                        continue;
                    }
                    ClosableResource<?> closeResource = (ClosableResource<?>)resource;
                    try{
                        closeResource.close();
                    }catch(ResourceException e){
                        // TODO
                    }
                }
                resourceMap = null;
            }
        }
        
        public class ResourceAccess{
            public Resource<Object> get(String name) throws ResourceException{
                return getResource(name);
            }
        }
    }
    
    /**
     * Beanフローのステップのコンテキスト情報を保持するクラス。<p>
     *
     * @author M.Takata
     */
    public static class StepContext implements Serializable{
        
        private static final long serialVersionUID = 5508331194991502651L;
        
        /**
         * ステップの対象オブジェクト。<p>
         */
        public Object target;
        
        /**
         * ステップの結果オブジェクト。<p>
         */
        public Object result;
        
        /**
         * ループさせるかどうか。<p>
         */
        public boolean isContinue = false;
        
        /**
         * ループを中断させるかどうか。<p>
         */
        public boolean isBreak = false;
        
        /**
         * ステップの対象オブジェクトを取得する。<p>
         *
         * @return ステップの対象オブジェクト
         */
        public Object getTarget(){
            return target;
        }
        
        /**
         * ステップの結果オブジェクトを取得する。<p>
         *
         * @return ステップの結果オブジェクト
         */
        public Object getResult(){
            return result;
        }
    }
    
    protected class Test implements Serializable{
        
        private static final long serialVersionUID = 5508331194991502652L;
        
        protected transient Expression expression;
        protected transient CompiledInterpreter compiledInterpreter;
        protected String condStr;
        protected boolean nullCheck = false;
        
        protected static final String INPUT = "input";
        protected static final String THIS = "this";
        protected static final String VAR = "var";
        protected static final String TARGET = "target";
        protected static final String RESULT = "result";
        protected static final String PROP_FUNCTION_NAME = "prop";
        protected static final String STATIC_FUNCTION_NAME = "static";
        
        public Test(String cond, boolean nullCheck) throws Exception{
            this.nullCheck = nullCheck;
            initCondition(cond);
        }
        
        protected void initCondition(String cond) throws Exception{
            condStr = cond;
            if(factory.getTestInterpreter() == null){
                JexlEngine jexl = new JexlEngine();
                jexl.setSilent(true);
                Map<String, Object> funcs = new HashMap<String, Object>();
                PropertyAccess propAccess = new NullNestAllowPropertyAccess(nullCheck);
                propAccess.setIgnoreNullProperty(true);
                funcs.put(PROP_FUNCTION_NAME, propAccess);
                funcs.put(STATIC_FUNCTION_NAME, StaticAccess.getInstance());
                jexl.setFunctions(funcs);
                expression = jexl.createExpression(cond);
            }else{
                if(factory.getTestInterpreter().isCompilable()){
                    compiledInterpreter = factory.getTestInterpreter().compile(condStr);
                }
            }
        }
        
        public boolean evaluate(FlowContext context) throws Exception{
            Object result = null;
            if(expression != null){
                JexlContext jexlContext = new MapContext();
                jexlContext.set(INPUT, context.getInput());
                jexlContext.set(THIS, context.getThis());
                jexlContext.set(VAR, context.vars);
                for(Map.Entry<String, StepContext> entry : context.entrySet()){
                    jexlContext.set(entry.getKey(), entry.getValue());
                }
                
                result = expression.evaluate(jexlContext);
            }else{
                Map<String, Object> variables = new HashMap<String, Object>();
                variables.put(INPUT, context.getInput());
                variables.put(THIS, context.getThis());
                variables.put(VAR, context.vars);
                for(Map.Entry<String, StepContext> entry : context.entrySet()){
                    variables.put(entry.getKey(), entry.getValue());
                }
                if(compiledInterpreter != null){
                    result = compiledInterpreter.evaluate(variables);
                }else{
                    result = factory.getTestInterpreter().evaluate(condStr, variables);
                }
            }
            if(result instanceof Boolean){
                return ((Boolean)result).booleanValue();
            }else{
                throw new IllegalArgumentException(
                    "expression is not boolean : "
                         + condStr + '=' + result
                );
            }
        }
        
        public String toString(){
            return condStr;
        }
        
        private void readObject(ObjectInputStream in)
         throws IOException, ClassNotFoundException{
            in.defaultReadObject();
            try{
                initCondition(condStr);
            }catch(Exception e){
                // 起こらないはず
            }
        }
    }
    
    protected static class TransactionInfo implements Serializable{
        
        private static final long serialVersionUID = 5508331194991502653L;
        
        public int transactionType = -1;
        public int transactionTimeout = -1;
        public TransactionManager tranManager;
        
        public void clear(){
            transactionType = -1;
            transactionTimeout = -1;
            tranManager = null;
        }
    }
    
    protected static class NullNestAllowPropertyAccess extends PropertyAccess{
        
        protected boolean nullCheck;
        
        protected NullNestAllowPropertyAccess(boolean nullCheck){
            this.nullCheck = nullCheck;
            this.setIgnoreNullProperty(!nullCheck);
        }
        
        public Object get(Object target, String prop) throws IllegalArgumentException, NoSuchPropertyException, InvocationTargetException{
            Object val = null;
            Property property = getProperty(prop);
            try{
                if(property instanceof NestedProperty){
                    NestedProperty nestedProp = (NestedProperty)property;
                    Property thisProp = nestedProp.getFirstThisProperty();
                    val = thisProp.getProperty(target);
                    if(val != null){
                        val = nestedProp.getProperty(target);
                    }
                }else{
                    val = property.getProperty(target);
                }
            }catch(NullIndexPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(NullKeyPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }catch(NullNestPropertyException e){
                if(nullCheck){
                    throw e;
                }
            }
            return val;
        }
    }
    
    protected static class StaticAccess implements Serializable{
        
        private static final long serialVersionUID = -6647258542757144863L;
        
        private static final StaticAccess instance = new StaticAccess();
        
        private StaticAccess(){};
        
        public static StaticAccess getInstance(){
            return instance;
        }
        
        public Class<?> clazz(String name) throws ClassNotFoundException{
            return Class.forName(name);
        }
        
        public Object field(String className, String fieldName) throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException{
            return Class.forName(className).getField(fieldName).get(null);
        }
    }
}
