org.jboss.loom.migrators.logging.LoggingMigrator.java Source code

Java tutorial

Introduction

Here is the source code for org.jboss.loom.migrators.logging.LoggingMigrator.java

Source

/**
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 .
 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and limitations under the License.
 */
package org.jboss.loom.migrators.logging;

import java.io.File;
import java.io.IOException;
import java.util.*;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.stream.XMLInputFactory;
import javax.xml.stream.XMLStreamException;
import javax.xml.stream.XMLStreamReader;
import javax.xml.transform.stream.StreamSource;
import org.apache.commons.lang.StringUtils;
import org.jboss.as.controller.client.helpers.ClientConstants;
import org.jboss.dmr.ModelNode;
import org.jboss.loom.actions.CliCommandAction;
import org.jboss.loom.actions.IMigrationAction;
import org.jboss.loom.actions.ModuleCreationAction;
import org.jboss.loom.conf.Configuration;
import org.jboss.loom.conf.Configuration.IfExists;
import org.jboss.loom.conf.GlobalConfiguration;
import org.jboss.loom.ctx.MigrationContext;
import org.jboss.loom.ctx.MigratorData;
import org.jboss.loom.ex.CliScriptException;
import org.jboss.loom.ex.LoadMigrationException;
import org.jboss.loom.ex.MigrationException;
import org.jboss.loom.migrators.AbstractMigrator;
import org.jboss.loom.migrators.logging.jaxb.*;
import org.jboss.loom.spi.IConfigFragment;
import org.jboss.loom.spi.ann.ConfigPartDescriptor;
import org.jboss.loom.utils.Utils;
import org.jboss.loom.utils.UtilsAS5;
import org.jboss.loom.utils.as7.AS7CliUtils;
import org.jboss.loom.utils.as7.CliAddScriptBuilder;
import org.jboss.loom.utils.as7.CliApiCommandBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Migrator of logging subsystem implementing IMigrator.
 * 
<appender name="CLUSTER" class="org.jboss.logging.appender.RollingFileAppender">
    <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/>
    <param name="File" value="${jboss.server.log.dir}/cluster.log"/>
    <param name="Append" value="false"/>
    <param name="MaxFileSize" value="500KB"/>
    <param name="MaxBackupIndex" value="1"/>
    
    <layout class="org.apache.log4j.PatternLayout">
        <param name="ConversionPattern" value="%d %-5p [%c] %m%n"/>
    </layout>
</appender>
<category name="org.jgroups">
    <priority value="WARN" />
    <appender-ref ref="CLUSTER"/>
</category>
<category name="org.jboss.ha">
    <priority value="INFO" />
    <appender-ref ref="CLUSTER"/>
</category>
     
 * 
 * Conf docs: https://docs.jboss.org/author/display/AS72/Logging+Configuration
 *
 * @author Roman Jakubco
 */
@ConfigPartDescriptor(name = "JBoss Logging configuration", docLink = "https://access.redhat.com/site/documentation/en-US/JBoss_Enterprise_Application_Platform/5/html-single/Administration_And_Configuration_Guide/index.html#idm2915376")
public class LoggingMigrator extends AbstractMigrator {
    private static final Logger log = LoggerFactory.getLogger(LoggingMigrator.class);

    private final static String CLI_PROP__LOG_DIR = "jboss.server.log.dir";
    private final static String AS5_PROP__LOG_TRESHOLD = "jboss.server.log.threshold";
    private final static String DEFAULT_QUEUE_LENGTH = "50";

    // Configurables
    @Override
    protected String getConfigPropertyModuleName() {
        return "logging";
    }

    private String rootLoggerTreshold = "INFO";

    private String getRootLoggerTreshold() {
        return rootLoggerTreshold;
    }

    @Override
    public int examineConfigProperty(Configuration.ModuleSpecificProperty prop) {
        if (!getConfigPropertyModuleName().equals(prop.getModuleId()))
            return 0;
        switch (prop.getPropName()) {
        case "rootLoggerTreshold":
        case AS5_PROP__LOG_TRESHOLD:
            this.rootLoggerTreshold = prop.getValue();
            return 1;
        }
        return 0;
    }

    // Sequence number for driver names.
    // TODO: Perhaps move this property to migration context.
    private int number = 1;

    public LoggingMigrator(GlobalConfiguration globalConfig) {
        super(globalConfig);
    }

    @Override
    public void loadSourceServerConfig(MigrationContext ctx) throws LoadMigrationException {
        try {
            File log4jConfFile = Utils.createPath(
                    //super.getGlobalConfig().getAS5Config().getDir(),  "server",
                    //super.getGlobalConfig().getAS5Config().getProfileName(),
                    //"conf", "jboss-log4j.xml");
                    super.getGlobalConfig().getAS5Config().getConfDir(), "jboss-log4j.xml");

            XMLInputFactory xif = XMLInputFactory.newFactory();
            xif.setProperty(XMLInputFactory.SUPPORT_DTD, false);
            XMLStreamReader xsr = xif.createXMLStreamReader(new StreamSource(log4jConfFile));

            //if( ! log4jConfFile.canRead())
            //    throw new LoadMigrationException("Cannot find/open file: " + log4jConfFile.getAbsolutePath());

            Unmarshaller unmarshaller = JAXBContext.newInstance(LoggingAS5Bean.class).createUnmarshaller();
            LoggingAS5Bean loggingAS5 = (LoggingAS5Bean) unmarshaller.unmarshal(xsr);

            MigratorData mData = new MigratorData();

            if (loggingAS5.getCategories() != null) {
                mData.getConfigFragments().addAll(loggingAS5.getCategories());
            }

            if (loggingAS5.getLoggers() != null) {
                mData.getConfigFragments().addAll(loggingAS5.getLoggers());
            }

            mData.getConfigFragments().addAll(loggingAS5.getAppenders());
            mData.getConfigFragments().add(loggingAS5.getRootLoggerAS5());

            ctx.getMigrationData().put(LoggingMigrator.class, mData);
        } catch (JAXBException | XMLStreamException e) {
            throw new LoadMigrationException(e);
        }
    }

    /**
     *  TODO: customHandlers are transformed at the end into actions.
     *        Use a map and action dependencies instead.
     */
    @Override
    public void createActions(MigrationContext ctx) throws MigrationException {

        List<AppenderBean> appenders = new LinkedList();
        List<CategoryBean> categs = new LinkedList();
        List<RootLoggerAS5Bean> rootLoggers = new LinkedList();

        // Sort out the fragments.
        for (IConfigFragment fragment : ctx.getMigrationData().get(LoggingMigrator.class).getConfigFragments()) {
            if (fragment instanceof AppenderBean)
                appenders.add((AppenderBean) fragment);
            else if (fragment instanceof CategoryBean)
                categs.add((CategoryBean) fragment);
            else if (fragment instanceof RootLoggerAS5Bean)
                rootLoggers.add((RootLoggerAS5Bean) fragment);
            else
                throw new MigrationException(
                        "Config fragment unrecognized by " + this.getClass().getSimpleName() + ": " + fragment);
        }

        // For categories with appender-ref - use Action dependencies.
        Map<String, IMigrationAction> appenderNamesToActions = new HashMap();

        // Prevent duplicate categories.
        Map<CategoryBean, IMigrationAction> categoryToAction = new HashMap();

        // Appenders.
        HashMap<File, String> tempModules = new HashMap();
        for (AppenderBean appender : appenders) {
            List<? extends IMigrationAction> actions = createAppenderAction(appender, tempModules);
            for (IMigrationAction action : actions) {
                ctx.getActions().add(action);
                appenderNamesToActions.put(appender.getAppenderName(), action);
            }
        }

        // Categories
        IfExists loggerIfExists = parseIfExistsParam("logger." + IfExists.PARAM_NAME, IfExists.OVERWRITE);
        for (CategoryBean catBean : categs) {

            // Skip those which already exist.
            if (categoryToAction.containsKey(catBean)) {
                categoryToAction.get(catBean).getWarnings().add("Duplicate category found: " + catBean);
                continue;
            }

            try {
                LoggerBean categoryBean = migrateCategory(catBean);
                CliCommandAction action = createLoggerCliAction(categoryBean, loggerIfExists);

                // category/appender-ref/@ref
                for (String appenRef : catBean.getAppenderRefs()) {
                    IMigrationAction appenAction = appenderNamesToActions.get(appenRef);
                    if (null == appenAction) {
                        action.addWarning("Unknown appender referenced in category " + catBean.getCategoryName()
                                + ": " + appenRef);
                        continue;
                    }
                    action.addDependency(appenAction);
                }

                ctx.getActions().add(action);
                categoryToAction.put(catBean, action); // Prevent duplicates.
            } catch (CliScriptException ex) {
                throw new MigrationException("Migration of the Category failed: " + ex.getMessage(), ex);
            }
        }

        // Root logger
        for (RootLoggerAS5Bean rootLogger : rootLoggers) {
            List<CliCommandAction> actions = createRootLoggerCliAction(migrateRootLogger(rootLogger));

            // appender-ref/@ref
            for (String appenRef : rootLogger.getRootAppenderRefs()) {
                IMigrationAction appenAction = appenderNamesToActions.get(appenRef);
                if (null == appenAction) {
                    actions.get(0).addWarning("Unknown appender referenced in root logger: " + appenRef);
                    continue;
                }
                actions.get(0).addDependency(appenAction);
            }
            ctx.getActions().addAll(actions);
        }

    }// createActions()

    /**
     * Creates Custom-Handler CliCommandAction along with ModuleCreationAction if needed
     *
     * @param handler Custom-Handler with custom class, which must be deployed into AS7
     * @param tempModules Map containing names of the jar files and their created modules, which were already migrated
     * @return  list containing CliCommandAction for adding Custom-Handler and ModuleCreationAction for adding module if
     *          needed
     * @throws MigrationException if class cannot be found in jars in AS5 structure
     */
    private List<IMigrationAction> createCustomHandlerActions(CustomHandlerBean handler,
            HashMap<File, String> tempModules) throws MigrationException {

        File fileJar;
        try {
            fileJar = UtilsAS5.findJarFileWithClass(handler.getClassValue(),
                    getGlobalConfig().getAS5Config().getDir(), getGlobalConfig().getAS5Config().getProfileName());
        } catch (IOException ex) {
            throw new MigrationException(
                    "Failed finding jar with class " + handler.getClassValue() + ": " + ex.getMessage(), ex);
        }

        List<IMigrationAction> actions = new LinkedList();

        if (tempModules.containsKey(fileJar)) {
            // ModuleCreationAction is already set. No need for another one => just create CLI for CustomHandler
            try {
                handler.setModule(tempModules.get(fileJar));
                actions.add(createCustomHandlerCliAction(handler));
            } catch (CliScriptException ex) {
                throw new MigrationException(
                        "Failed creating a CLI command for appeneder " + handler.getName() + ": " + ex.getMessage(),
                        ex);
            }

            return actions;
        }

        // Handler jar is new => create ModuleCreationAction, new module and CLI script
        try {
            String moduleName = "logging.customHandler" + number;
            number++;
            handler.setModule(moduleName);
            tempModules.put(fileJar, moduleName);

            actions.add(createCustomHandlerCliAction(handler));

            String[] deps = new String[] { "javax.api", "org.jboss.logging", null, "org.apache.log4j" };

            ModuleCreationAction moduleAction = new ModuleCreationAction(this.getClass(), moduleName, deps, fileJar,
                    this.parseIfExistsParam("logger." + IfExists.PARAM_NAME, IfExists.OVERWRITE));
            actions.add(moduleAction);
        } catch (CliScriptException e) {
            throw new MigrationException(
                    "Migration of the appeneder " + handler.getName() + " failed (CLI command): " + e.getMessage(),
                    e);
        }

        return actions;
    }

    /**
     *  Processes AppenderBean. Adds actions to context!
     *  TODO: Refactor to return the action.
     */
    private List<? extends IMigrationAction> createAppenderAction(AppenderBean appenderBean,
            HashMap<File, String> tempModules) throws MigrationException {

        // Selection of classes which are stored in log4j or jboss logging jars.
        String cls = appenderBean.getAppenderClass();
        if (!(cls.startsWith("org.apache.log4j") || cls.startsWith("org.jboss.logging.appender"))) {
            // Selection of classes which are created by the user
            // In situation that the user creates own class with same name as classes in log4j or jboss logging => CustomHandler
            // Module for these handlers must be set with creation of ModuleCreationAction
            CustomHandlerBean handler = createCustomHandler(appenderBean, true);
            return createCustomHandlerActions(handler, tempModules);
        }

        try {
            String appenderType = StringUtils.substringAfterLast(cls, ".");
            CliCommandAction action;

            switch (appenderType) {
            case "DailyRollingFileAppender": {
                PerRotFileHandlerBean handler = createPerRotFileHandler(appenderBean);
                action = createPerRotHandlerCliAction(handler);
            }
                break;
            case "RollingFileAppender": {
                SizeRotFileHandlerBean handler = createSizeRotFileHandler(appenderBean);
                action = createSizeRotHandlerCliAction(handler);
            }
                break;
            case "ConsoleAppender": {
                ConsoleHandlerBean handler = createConsoleHandler(appenderBean);
                action = createConsoleHandlerCliAction(handler);
            }
                break;
            case "AsyncAppender": {
                AsyncHandlerBean handler = createAsyncHandler(appenderBean);
                action = createAsyncHandleCliAction(handler);
            }
                break;

            //  If the class don't correspond to any type of AS7 handler => CustomHandler
            default: {
                // Module of these handler will be set in the method. Module log4j.
                CustomHandlerBean handler = createCustomHandler(appenderBean, false);
                action = createCustomHandlerCliAction(handler);
            }
            }

            return Collections.singletonList(action);
        } catch (CliScriptException e) {
            throw new MigrationException(
                    "Migration of the appender " + appenderBean.getAppenderName() + " failed: " + e.getMessage(),
                    e);
        }

    }// processAppenderBean()

    /**
     * Migrates a Category from AS5 into Logger in AS7
     *
     * @param category object representing category from AS5
     * @return created object of Logger in AS7
     */
    private static LoggerBean migrateCategory(CategoryBean category) {
        LoggerBean logger = new LoggerBean();

        logger.setLoggerCategory(category.getCategoryName());
        logger.setLoggerLevelName(category.getCategoryValue());
        logger.setHandlers(category.getAppenderRefs());

        return logger;
    }

    /**
     * Migrates a root-logger from AS5 into a root-logger in AS7
     *
     * @param loggerAS5 object representing root-logger from AS5
     * @return created object of root-logger from AS7
     */
    private RootLoggerAS7Bean migrateRootLogger(RootLoggerAS5Bean loggerAS5) {
        RootLoggerAS7Bean rootLoggerAS7 = new RootLoggerAS7Bean();
        // Defined as reference to sys prop in AS 5: <priority value="${jboss.server.log.threshold}"/>
        if (loggerAS5.getRootPriorityValue().equals("${" + AS5_PROP__LOG_TRESHOLD + "}")) {
            rootLoggerAS7.setRootLoggerLevel(this.getRootLoggerTreshold());
        } else {
            rootLoggerAS7.setRootLoggerLevel(loggerAS5.getRootPriorityValue());
        }

        rootLoggerAS7.setRootLoggerHandlers(loggerAS5.getRootAppenderRefs());

        return rootLoggerAS7;
    }

    /**
     * Migrates a Periodic-Rotating-File-Appender to a Handler in AS7
     *
     * @param appender object representing Periodic-Rotating-File-Appender
     * @param ctx      migration context
     * @return migrated Periodic-Rotating-File-Handler object
     */
    static PerRotFileHandlerBean createPerRotFileHandler(AppenderBean appender) {

        PerRotFileHandlerBean handler = new PerRotFileHandlerBean();
        handler.setName(appender.getAppenderName());
        if (appender.getParameters() != null) {
            for (ParameterBean parameter : appender.getParameters()) {
                if (parameter.getParamName().equalsIgnoreCase("Append")) {
                    handler.setAppend(parameter.getParamValue());
                    continue;
                }

                if (parameter.getParamName().equals("File")) {
                    String value = parameter.getParamValue();
                    handler.setRelativeTo(CLI_PROP__LOG_DIR);
                    handler.setPath(new File(value).getName());
                }

                if (parameter.getParamName().equalsIgnoreCase("DatePattern")) {
                    // TODO: Basic for now. Don't know what to do with apostrophes
                    handler.setSuffix(parameter.getParamValue());
                    continue;
                }

                if (parameter.getParamName().equalsIgnoreCase("Threshold")) {
                    handler.setLevel(parameter.getParamValue());
                }
            }
        }

        handler.setFormatter(appender.getLayoutParamValue());

        return handler;
    }

    /**
     * Migrates a Size-Rotating-File-Appender to a Handler in AS7
     *
     * @param appender object representing Size-Rotating-File-Appender
     * @param ctx      migration context
     * @return migrated Size-Rotating-File-Handler object
     */
    static SizeRotFileHandlerBean createSizeRotFileHandler(AppenderBean appender) {

        SizeRotFileHandlerBean handler = new SizeRotFileHandlerBean();
        handler.setName(appender.getAppenderName());
        if (appender.getParameters() != null) {
            for (ParameterBean parameter : appender.getParameters()) {
                if (parameter.getParamName().equalsIgnoreCase("Append")) {
                    handler.setAppend(parameter.getParamValue());
                    continue;
                }

                if (parameter.getParamName().equals("File")) {
                    String value = parameter.getParamValue();

                    //TODO: Problem with bad parse? same thing in DailyRotating
                    handler.setRelativeTo(CLI_PROP__LOG_DIR);
                    handler.setPath(new File(value).getName());
                    continue;
                }

                if (parameter.getParamName().equalsIgnoreCase("MaxFileSize")) {
                    handler.setRotateSize(parameter.getParamValue());
                    continue;
                }

                if (parameter.getParamName().equalsIgnoreCase("MaxBackupIndex")) {
                    handler.setMaxBackupIndex(parameter.getParamValue());
                    continue;
                }

                if (parameter.getParamName().equalsIgnoreCase("Threshold")) {
                    handler.setLevel(parameter.getParamValue());
                }
            }
        }

        handler.setFormatter(appender.getLayoutParamValue());

        return handler;
    }

    /**
     * Migrates a Async-Appender to a Handler in AS7
     *
     * @param appender object representing Async-Appender
     * @return migrated Async-Handler object
     */
    static AsyncHandlerBean createAsyncHandler(AppenderBean appender) {

        AsyncHandlerBean handler = new AsyncHandlerBean();
        handler.setName(appender.getAppenderName());
        if (appender.getParameters() != null) {
            // TODO: Problem with queue-length in Async
            for (ParameterBean parameter : appender.getParameters()) {
                if (parameter.getParamName().equalsIgnoreCase("BufferSize")) {
                    handler.setQueueLength(parameter.getParamValue());
                    continue;
                }

                if (parameter.getParamName().equalsIgnoreCase("Blocking")) {
                    handler.setOverflowAction(parameter.getParamValue());
                }
            }
        }

        Set<String> appendersRef = new HashSet();

        for (String ref : appender.getAppenderRefs()) {
            appendersRef.add(ref);
        }

        handler.setSubhandlers(appendersRef);
        handler.setFormatter(appender.getLayoutParamValue());

        return handler;
    }

    /**
     * Migrates a Console-Appender to a Handler in AS7
     *
     * @param appender object representing Console-Appender
     * @return migrated Console-Handler object
     */
    static ConsoleHandlerBean createConsoleHandler(AppenderBean appender) {

        ConsoleHandlerBean handler = new ConsoleHandlerBean();
        handler.setName(appender.getAppenderName());
        if (appender.getParameters() != null) {
            for (ParameterBean parameter : appender.getParameters()) {
                if (parameter.getParamName().equalsIgnoreCase("Target")) {
                    handler.setTarget(parameter.getParamValue());
                    continue;
                }

                if (parameter.getParamName().equalsIgnoreCase("Threshold")) {
                    handler.setLevel(parameter.getParamValue());
                }
            }
        }

        handler.setFormatter(appender.getLayoutParamValue());

        return handler;
    }

    /**
     * Migrates a Custom-Appender to a Handler in AS7
     *
     * @param appender object representing Custom-Appender
     * @param custom  true if appender class is created by user, false if it is declared in log4j or jboss logging
     * @return migrated Custom-Handler object
     */
    static CustomHandlerBean createCustomHandler(AppenderBean appender, boolean custom) {

        CustomHandlerBean handler = new CustomHandlerBean();
        handler.setName(appender.getAppenderName());
        handler.setClassValue(appender.getAppenderClass());

        if (!custom) {
            // Only possibility is class in log4j=> log4j module in AS7
            handler.setModule("org.apache.log4j");
        }

        Set<PropertyBean> properties = new HashSet();
        if (appender.getParameters() != null) {
            for (ParameterBean parameter : appender.getParameters()) {
                if (parameter.getParamName().equalsIgnoreCase("Threshold")) {
                    handler.setLevel(parameter.getParamValue());
                    continue;
                }

                PropertyBean property = new PropertyBean();
                property.setName(parameter.getParamName());
                property.setValue(parameter.getParamValue());
                properties.add(property);
            }
        }

        handler.setProperties(properties);
        handler.setFormatter(appender.getLayoutParamValue());

        return handler;
    }

    /**
     * Not implemented yet. Not sure if it is necessary..
     * Migrates File-Appender to Handler in AS7
     *
     * @return migrated File-Handler object
     */
    static FileHandlerBean createFileHandler(AppenderBean appender) throws CliScriptException {
        return null;
    }

    /**
     * Creates CliCommandAction for adding a Logger
     *
     * @param logger object representing Logger
     * @return created CliCommandAction for adding the Logger
     * @throws CliScriptException if required attributes for a creation of the CLI command of the logger are missing or
     *                            are empty (loggerCategory)
     */
    static CliCommandAction createLoggerCliAction(LoggerBean logger, IfExists ifExists) throws CliScriptException {
        String errMsg = " in logger(Category in AS5) must be set.";
        Utils.throwIfBlank(logger.getLoggerCategory(), errMsg, "Logger name");

        // First, check if it exists. If so, delete first.
        switch (ifExists) {
        case OVERWRITE:
        case MERGE:
            /* TODO: Actually, this is handled by the CliCommandAction itself - setIfExists().
             *       Check why it doesn't work.
            // TODO: MIGR-61 Merge resources instead of skipping or replacing
            try {
            //log.debug("Removing resource if exists: " + loggerCmd);
            //AS7CliUtils.removeResourceIfExists( loggerCmd, ctx.getAS7Client() );
            if( AS7CliUtils.exists( loggerCmd, ctx.getAS7Client() ) ){
                new CliCommandAction( LoggingMigrator.class, AS7CliUtils.formatCommand( loggerCmd ), loggerCmd );
            }
            } catch( CliBatchException | IOException ex ) {
            throw new CliScriptException("Failed removing resource '"+AS7CliUtils.formatCommand( loggerCmd )+"': " + ex.getMessage(), ex );
            }/**/
        }

        return new CliCommandAction(LoggingMigrator.class, createLoggerScript(logger),
                createLoggerCommand(logger, ifExists)).setIfExists(ifExists);
    }

    private static ModelNode createLoggerCommand(LoggerBean logger, IfExists ifExists) {
        // ModelNode
        ModelNode loggerCmd = new ModelNode();
        loggerCmd.get(ClientConstants.OP_ADDR).add("subsystem", "logging");
        loggerCmd.get(ClientConstants.OP_ADDR).add("logger", logger.getLoggerCategory());

        // ADD
        loggerCmd.get(ClientConstants.OP).set(ClientConstants.ADD);

        if (logger.getHandlers() != null) {
            ModelNode handlersNode = new ModelNode();
            for (String handler : logger.getHandlers()) {
                ModelNode handlerNode = new ModelNode();
                handlerNode.set(handler);
                handlersNode.add(handlerNode);
            }
            loggerCmd.get("handlers").set(handlersNode);
        }

        CliApiCommandBuilder builder = new CliApiCommandBuilder(loggerCmd);
        builder.addPropertyIfSet("level", logger.getLoggerLevelName());
        builder.addPropertyIfSet("use-parent-handlers", logger.getUseParentHandlers());

        return builder.getCommand();
    }

    /**
     * Creates CliCommandAction for adding a Periodic-Rotating-File-Handler
     *
     * @param handler object representing Periodic-Rotating-File-Handler
     * @return  created CliCommandAction for adding the Periodic-Rotating-File-Handler
     * @throws CliScriptException if required attributes for a creation of CLI command of the handler are missing or
     *                            are empty (name, relativeTo, path, suffix)
     */
    static CliCommandAction createPerRotHandlerCliAction(PerRotFileHandlerBean handler) throws CliScriptException {
        String errMsg = " in periodic-rotating-file-handler (~Appender in source server) must be set.";
        Utils.throwIfBlank(handler.getName(), errMsg, "Name");
        Utils.throwIfBlank(handler.getRelativeTo(), errMsg, "Relative-to");
        Utils.throwIfBlank(handler.getPath(), errMsg, "Path");
        Utils.throwIfBlank(handler.getSuffix(), errMsg, "Suffix");

        ModelNode handlerCmd = new ModelNode();
        handlerCmd.get(ClientConstants.OP).set(ClientConstants.ADD);
        handlerCmd.get(ClientConstants.OP_ADDR).add("subsystem", "logging");
        handlerCmd.get(ClientConstants.OP_ADDR).add("periodic-rotating-file-handler", handler.getName());

        ModelNode temp = new ModelNode();
        temp.get("relative-to").set(handler.getRelativeTo());
        temp.get("path").set(handler.getPath());

        handlerCmd.get("file").set(temp);

        CliApiCommandBuilder builder = new CliApiCommandBuilder(handlerCmd);
        builder.addPropertyIfSet("suffix", handler.getSuffix());
        builder.addPropertyIfSet("level", handler.getLevel());
        builder.addPropertyIfSet("formatter", handler.getFormatter());
        builder.addPropertyIfSet("autoflush", handler.getAutoflush());
        builder.addPropertyIfSet("append", handler.getAppend());
        // TODO: AS7CliUtils.copyProperties(handler, builder, "level filter formatter ...");

        return new CliCommandAction(LoggingMigrator.class, createPerHandlerScript(handler), builder.getCommand());
    }

    /**
     * Creates CliCommandAction for adding a Size-Rotating-File-Handler
     *
     * @param handler object representing Size-Rotating-File-Handler
     * @return  created CliCommandAction for adding Size-Rotating-File-Handler
     * @throws CliScriptException if required attributes for a creation of the CLI command of the handler are missing or
     *                            are empty (name, relativeTo, path)
     */
    static CliCommandAction createSizeRotHandlerCliAction(SizeRotFileHandlerBean handler)
            throws CliScriptException {
        String errMsg = " in size-rotating-file-handler (~Appender in source server) must be set.";
        Utils.throwIfBlank(handler.getName(), errMsg, "Name");
        Utils.throwIfBlank(handler.getRelativeTo(), errMsg, "Relative-to");
        Utils.throwIfBlank(handler.getPath(), errMsg, "Path");

        ModelNode handlerCmd = new ModelNode();
        handlerCmd.get(ClientConstants.OP).set(ClientConstants.ADD);
        handlerCmd.get(ClientConstants.OP_ADDR).add("subsystem", "logging");
        handlerCmd.get(ClientConstants.OP_ADDR).add("size-rotating-file-handler", handler.getName());

        ModelNode temp = new ModelNode();
        temp.get("relative-to").set(handler.getRelativeTo());
        temp.get("path").set(handler.getPath());

        handlerCmd.get("file").set(temp);

        CliApiCommandBuilder builder = new CliApiCommandBuilder(handlerCmd);
        builder.addPropertyIfSet("level", handler.getLevel());
        builder.addPropertyIfSet("filter", handler.getFilter());
        builder.addPropertyIfSet("formatter", handler.getFormatter());
        builder.addPropertyIfSet("autoflush", handler.getAutoflush());
        builder.addPropertyIfSet("append", handler.getAppend());
        String size = handler.getRotateSize();
        if (size.endsWith("KB"))
            size = StringUtils.replace(size, "KB", "K");
        if (size.endsWith("MB"))
            size = StringUtils.replace(size, "MB", "M");
        if (size.endsWith("GB"))
            size = StringUtils.replace(size, "GB", "G");
        builder.addPropertyIfSet("rotate-size", size);
        builder.addPropertyIfSet("max-backup-index", handler.getMaxBackupIndex());
        // TODO: AS7CliUtils.copyProperties(handler, builder, "level filter formatter ...");

        return new CliCommandAction(LoggingMigrator.class, createSizeHandlerScript(handler), builder.getCommand());
    }

    /**
     * Creates CliCommandAction for adding a Async-Handler
     *
     * @param handler object representing Async-Handler
     * @return  created CliCommandAction for adding the Async-Handler
     * @throws CliScriptException if required attributes for a creation of the CLI command of the handler are missing
     *                            or are empty (name, queueLength)
     * 
     * Example from EAP 5 production:
     * 
    <!-- Buffer events and log them asynchronously -->
    <appender name="ASYNC" class="org.apache.log4j.AsyncAppender">
      <errorHandler class="org.jboss.logging.util.OnlyOnceErrorHandler"/>
      <appender-ref ref="FILE"/>
      <!--
      <appender-ref ref="CONSOLE"/>
      <appender-ref ref="SMTP"/>
      -->
    </appender>
     */
    static CliCommandAction createAsyncHandleCliAction(AsyncHandlerBean handler)
            throws CliScriptException, MigrationException {
        String errMsg = " in async-handler (AsyncAppender) must be set.";
        Utils.throwIfBlank(handler.getName(), errMsg, "Name");
        //Utils.throwIfBlank(handler.getQueueLength(), errMsg, "Queue length"); // It doesn't have to, in AS 5.

        ModelNode handlerCmd = new ModelNode();
        handlerCmd.get(ClientConstants.OP).set(ClientConstants.ADD);
        handlerCmd.get(ClientConstants.OP_ADDR).add("subsystem", "logging");
        handlerCmd.get(ClientConstants.OP_ADDR).add("async-handler", handler.getName());

        if (handler.getSubhandlers() != null) {
            ModelNode handlersNode = new ModelNode();
            for (String subHandler : handler.getSubhandlers()) {
                ModelNode handlerNode = new ModelNode();

                handlerNode.set(subHandler);
                handlersNode.add(handlerNode);
            }
            handlerCmd.get("handlers").set(handlersNode);
        }

        CliApiCommandBuilder builder = new CliApiCommandBuilder(handlerCmd);

        builder.addPropertyIfSet("queue-length", handler.getQueueLength(), DEFAULT_QUEUE_LENGTH);
        builder.addPropertyIfSet("level", handler.getLevel());
        builder.addPropertyIfSet("filter", handler.getFilter());
        builder.addPropertyIfSet("formatter", handler.getFormatter());
        builder.addPropertyIfSet("overflow-action", handler.getOverflowAction());
        // TODO:
        //builder.setPropsFromObject(handler, "level filter formatter overflow-action");

        return new CliCommandAction(LoggingMigrator.class, createAsyncHandlerScript(handler), builder.getCommand());
    }

    /**
     * Creates CliCommandAction for adding a Console-Handler
     *
     * @param handler object representing Console-Handler
     * @return  created CliCommandAction for adding the Console-Handler
     * @throws CliScriptException if required attributes for a creation of the CLI command of the handler are missing or
     *                            are empty (name)
     */
    static CliCommandAction createConsoleHandlerCliAction(ConsoleHandlerBean handler) throws CliScriptException {
        String errMsg = " in console-handler (~Appender in source server) must be set.";
        Utils.throwIfBlank(handler.getName(), errMsg, "Name");

        ModelNode handlerCmd = new ModelNode();
        handlerCmd.get(ClientConstants.OP).set(ClientConstants.ADD);
        handlerCmd.get(ClientConstants.OP_ADDR).add("subsystem", "logging");
        handlerCmd.get(ClientConstants.OP_ADDR).add("console-handler", handler.getName());

        CliApiCommandBuilder builder = new CliApiCommandBuilder(handlerCmd);
        builder.addPropertyIfSet("level", handler.getLevel());
        builder.addPropertyIfSet("filter", handler.getFilter());
        builder.addPropertyIfSet("formatter", handler.getFormatter());
        builder.addPropertyIfSet("autoflush", handler.getAutoflush());
        builder.addPropertyIfSet("target", handler.getTarget());

        // TODO: AS7CliUtils.copyProperties(handler, builder, "level filter formatter autoflush target");

        return new CliCommandAction(LoggingMigrator.class, createConsoleHandlerScript(handler),
                builder.getCommand());
    }

    /**
     * Creates CliCommandAction for adding a CustomHandler
     *
     * @param handler object representing Custom-Handler
     * @return  created CliCommandAction for adding the Custom-Handler
     * @throws CliScriptException if required attributes for a creation of the CLI command of the handler are missing or
     *                            are empty(name, module, classValue)
     */
    static CliCommandAction createCustomHandlerCliAction(CustomHandlerBean handler) throws CliScriptException {
        String errMsg = " in custom-handler must be set.";
        Utils.throwIfBlank(handler.getName(), errMsg, "Name");
        Utils.throwIfBlank(handler.getModule(), errMsg, "Module");
        Utils.throwIfBlank(handler.getClassValue(), errMsg, "Class-value");

        return new CliCommandAction(LoggingMigrator.class, createCustomHandlerScript(handler),
                createCustomHandlerModelNode(handler));
    }

    static ModelNode createCustomHandlerModelNode(CustomHandlerBean handler) throws CliScriptException {

        ModelNode handlerCmd = new ModelNode();
        handlerCmd.get(ClientConstants.OP).set(ClientConstants.ADD);
        handlerCmd.get(ClientConstants.OP_ADDR).add("subsystem", "logging");
        handlerCmd.get(ClientConstants.OP_ADDR).add("custom-handler", handler.getName());

        if (handler.getProperties() != null) {
            ModelNode propertyNode = new ModelNode();
            for (PropertyBean property : handler.getProperties()) {
                propertyNode.get(property.getName()).set(property.getValue());
            }
            handlerCmd.get("properties").set(propertyNode);
        }

        CliApiCommandBuilder builder = new CliApiCommandBuilder(handlerCmd);
        builder.addPropertyIfSet("level", handler.getLevel());
        builder.addPropertyIfSet("filter", handler.getFilter());
        builder.addPropertyIfSet("formatter", handler.getFormatter());
        builder.addPropertyIfSet("class", handler.getClassValue());
        builder.addPropertyIfSet("module", handler.getModule());
        // TODO: AS7CliUtils.copyProperties(handler, builder, "level filter formatter class module");

        return builder.getCommand();
    }

    /**
     * Creates a list of CliCommandActions for modifying a root-logger
     *
     * @param root object representing root-logger
     * @return list of created CliCommandActions
     * @throws CliScriptException
     */
    List<CliCommandAction> createRootLoggerCliAction(RootLoggerAS7Bean root) throws CliScriptException {
        List<CliCommandAction> actions = new ArrayList();
        if (root.getRootLoggerLevel() != null) {
            String level = "/subsystem=logging/root-logger=ROOT:write-attribute(name=level, value="
                    + root.getRootLoggerLevel() + ")";

            ModelNode levelNode = new ModelNode();
            levelNode.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
            levelNode.get(ClientConstants.OP_ADDR).add("subsystem", "logging");
            levelNode.get(ClientConstants.OP_ADDR).add("root-logger", "ROOT");
            levelNode.get("name").set("level");
            levelNode.get("value").set(root.getRootLoggerLevel());

            actions.add(new CliCommandAction(this.getClass(), level, levelNode));
        }

        if ((root.getRootLoggerHandlers() != null) || !(root.getRootLoggerHandlers().isEmpty())) {
            StringBuilder handlerTemp = new StringBuilder();
            for (String handler : root.getRootLoggerHandlers()) {
                handlerTemp.append('"').append(handler).append('"');
            }

            String temp = handlerTemp.toString();
            String handlers = "/subsystem=logging/root-logger=ROOT:write-attribute(name=handlers, value=["
                    + StringUtils.substringBeforeLast(temp, ",") + "])";

            ModelNode handlersNode = new ModelNode();
            handlersNode.get(ClientConstants.OP).set(ClientConstants.WRITE_ATTRIBUTE_OPERATION);
            handlersNode.get(ClientConstants.OP_ADDR).add("subsystem", "logging");
            handlersNode.get(ClientConstants.OP_ADDR).add("root-logger", "ROOT");
            handlersNode.get("name").set("handlers");

            ModelNode list = new ModelNode();
            for (String test : root.getRootLoggerHandlers()) {
                list.add(test);
            }
            handlersNode.get("value").set(list);

            actions.add(new CliCommandAction(this.getClass(), handlers, handlersNode));

        }
        return actions;
    }

    /*  ============= Script stuff - TODO: Get rid of it. Generate from ModelNode. ============== */

    /**
     * Creates a CLI script for adding a Logger
     *
     * @param logger object of Logger
     * @return string containing created CLI script
     * @throws CliScriptException if required attributes are missing
     * @deprecated  Generate this out of ModelNode.
     */
    static String createLoggerScript(LoggerBean logger) throws CliScriptException {
        String errMsg = " in logger (Category in source server) must be set.";
        Utils.throwIfBlank(logger.getLoggerCategory(), errMsg, "Logger name");

        StringBuilder resultScript = new StringBuilder(
                "/subsystem=logging/logger=" + logger.getLoggerCategory() + ":add(");

        CliAddScriptBuilder builder = new CliAddScriptBuilder();
        builder.addProperty("level", logger.getLoggerLevelName());
        builder.addProperty("use-parent-handlers", logger.getUseParentHandlers());
        resultScript.append(builder.formatAndClearProps());

        if (logger.getHandlers() != null) {
            /*StringBuilder handlersBuilder = new StringBuilder();
            for (String handler : logger.getHandlers()) {
            handlersBuilder.append(",\"").append(handler).append("\"");
            }
            String handlers = handlersBuilder.toString();
            if( ! handlers.isEmpty() ) {
            handlers = handlers.replaceFirst(",", "");
            resultScript.append(", handlers=[").append(handlers).append("]");
            }
             */
            String handlersStr = AS7CliUtils.joinQuoted(logger.getHandlers());
            if (!handlersStr.isEmpty())
                resultScript.append(", handlers=[").append(handlersStr).append("]");
        }

        resultScript.append(")");
        return resultScript.toString();
    }

    /**
     * Creates a CLI script for adding a Periodic-Rotating-File-Handler
     *
     * @param periodic object of Periodic-Rotating-File-Handler
     * @return string containing created CLI script
     * @throws CliScriptException if required attributes are missing
     * @deprecated  Generate this out of ModelNode.
     */
    static String createPerHandlerScript(PerRotFileHandlerBean periodic) throws CliScriptException {
        String errMsg = " in periodic-rotating-file-handler (~Appender in source server) must be set.";
        Utils.throwIfBlank(periodic.getName(), errMsg, "Name");
        Utils.throwIfBlank(periodic.getRelativeTo(), errMsg, "Relative-to");
        Utils.throwIfBlank(periodic.getPath(), errMsg, "Path");
        Utils.throwIfBlank(periodic.getSuffix(), errMsg, "Suffix");

        StringBuilder resultScript = new StringBuilder("/subsystem=logging/periodic-rotating-file-handler=");
        resultScript.append(periodic.getName()).append(":add(");
        resultScript.append("file={\"relative-to\"=>\"").append(periodic.getRelativeTo()).append("\"");
        resultScript.append(", \"path\"=>\"").append(periodic.getPath()).append("\"}");
        resultScript.append(", suffix=").append(periodic.getSuffix()).append(", ");

        CliAddScriptBuilder builder = new CliAddScriptBuilder();
        builder.addProperty("level", periodic.getLevel());
        builder.addProperty("formatter", periodic.getFormatter());
        builder.addProperty("autoflush", periodic.getAutoflush());
        builder.addProperty("append", periodic.getAppend());
        // TODO: AS7CliUtils.copyProperties(handler, builder, "level formatter autoflush append");

        resultScript.append(builder.formatAndClearProps()).append(")");

        return resultScript.toString();
    }

    /**
     * Creates a CLI script for adding a Size-Rotating-File-Handler
     *
     * @param sizeHandler object of Size-Rotating-File-Handler
     * @return string containing created CLI script
     * @throws CliScriptException if required attributes are missing
     * @deprecated  Generate this out of ModelNode.
     */
    static String createSizeHandlerScript(SizeRotFileHandlerBean sizeHandler) throws CliScriptException {
        String errMsg = " in size-rotating-file-handler (~Appender in source server) must be set.";
        Utils.throwIfBlank(sizeHandler.getName(), errMsg, "Name");
        Utils.throwIfBlank(sizeHandler.getRelativeTo(), errMsg, "Relative-to");
        Utils.throwIfBlank(sizeHandler.getPath(), errMsg, "Path");

        CliAddScriptBuilder builder = new CliAddScriptBuilder();
        StringBuilder resultScript = new StringBuilder("/subsystem=logging/size-rotating-file-handler=");

        resultScript.append(sizeHandler.getName()).append(":add(");
        resultScript.append("file={\"relative-to\"=>\"").append(sizeHandler.getRelativeTo()).append("\"");
        resultScript.append(", \"path\"=>\"").append(sizeHandler.getPath()).append("\"},");

        builder.addProperty("level", sizeHandler.getLevel());
        builder.addProperty("filter", sizeHandler.getFilter());
        builder.addProperty("formatter", sizeHandler.getFormatter());
        builder.addProperty("autoflush", sizeHandler.getAutoflush());
        builder.addProperty("append", sizeHandler.getAppend());
        builder.addProperty("rotate-size", sizeHandler.getRotateSize());
        builder.addProperty("max-backup-index", sizeHandler.getMaxBackupIndex());
        // TODO: AS7CliUtils.copyProperties(handler, builder, "level filter formatter autoflush ...");

        resultScript.append(builder.formatAndClearProps()).append(")");

        return resultScript.toString();
    }

    /**
     * Creates a CLI script for adding a Async-Handler
     *
     * @param asyncHandler object of Async-Handler
     * @return string containing created CLI script
     * @throws CliScriptException if required attributes are missing
     * @deprecated  Generate this out of ModelNode.
     */
    static String createAsyncHandlerScript(AsyncHandlerBean asyncHandler) throws CliScriptException {
        String errMsg = " in async-handler (AsyncAppender in AS5) must be set.";
        Utils.throwIfBlank(asyncHandler.getName(), errMsg, "Name");
        //Utils.throwIfBlank(asyncHandler.getQueueLength(), errMsg, "Queue length"); // It doesn't have to, in AS 5.

        StringBuilder resultScript = new StringBuilder("/subsystem=logging/async-handler=");
        resultScript.append(asyncHandler.getName()).append(":add(");
        //resultScript.append("queue-length=").append( String.defaultIfNull( asyncHandler.getQueueLength(), 100) );

        CliAddScriptBuilder builder = new CliAddScriptBuilder();
        builder.addProperty("queue-length",
                StringUtils.defaultIfBlank(asyncHandler.getQueueLength(), DEFAULT_QUEUE_LENGTH));
        builder.addProperty("level", asyncHandler.getLevel());
        builder.addProperty("filter", asyncHandler.getFilter());
        builder.addProperty("formatter", asyncHandler.getFormatter());
        builder.addProperty("overflow-action", asyncHandler.getOverflowAction());
        // TODO: AS7CliUtils.copyProperties(handler, builder, "level filter formatter ...");

        resultScript.append(builder.formatAndClearProps());

        if (asyncHandler.getSubhandlers() != null) {
            StringBuilder handlersBuilder = new StringBuilder();
            for (String subHandler : asyncHandler.getSubhandlers()) {
                handlersBuilder.append(", \"").append(subHandler).append("\"");
            }

            String handlers = handlersBuilder.toString().replaceFirst(", ", "");
            if (!handlers.isEmpty()) {
                resultScript.append(", subhandlers=[").append(handlers).append("]");
            }
        }

        resultScript.append(")");

        return resultScript.toString();
    }

    /**
     * Creates a CLI script for adding a Console-Handler
     *
     * @param consoleHandler object of Console-Handler
     * @return string containing created CLI script
     * @throws CliScriptException if required attributes are missing
     * @deprecated  Generate this out of ModelNode.
     */
    static String createConsoleHandlerScript(ConsoleHandlerBean consoleHandler) throws CliScriptException {
        String errMsg = " in console-handler (~Appender in source server) must be set.";
        Utils.throwIfBlank(consoleHandler.getName(), errMsg, "Name");

        StringBuilder resultScript = new StringBuilder("/subsystem=logging/console-handler=");
        resultScript.append(consoleHandler.getName()).append(":add(");

        CliAddScriptBuilder builder = new CliAddScriptBuilder();
        builder.addProperty("level", consoleHandler.getLevel());
        builder.addProperty("filter", consoleHandler.getFilter());
        builder.addProperty("formatter", consoleHandler.getFormatter());
        builder.addProperty("autoflush", consoleHandler.getAutoflush());
        builder.addProperty("target", consoleHandler.getTarget());
        // TODO: AS7CliUtils.copyProperties(handler, builder, "level filter formatter autoflush target");

        resultScript.append(builder.formatAndClearProps()).append(")");

        return resultScript.toString();
    }

    /**
     * Creates a CLI script for adding a Custom-Handler
     *
     * @return string containing created CLI script
     * @throws CliScriptException if required attributes are missing
     * @deprecated  Generate this out of ModelNode.
     */
    static String createCustomHandlerScript(CustomHandlerBean customHandler) throws CliScriptException {
        String errMsg = " in custom-handler must be set.";
        Utils.throwIfBlank(customHandler.getName(), errMsg, "Name");
        Utils.throwIfBlank(customHandler.getModule(), errMsg, "Module");
        Utils.throwIfBlank(customHandler.getClassValue(), errMsg, "Class-value");

        StringBuilder resultScript = new StringBuilder("/subsystem=logging/custom-handler=");
        resultScript.append(customHandler.getName()).append(":add(");

        CliAddScriptBuilder builder = new CliAddScriptBuilder();
        builder.addProperty("level", customHandler.getLevel());
        builder.addProperty("filter", customHandler.getFilter());
        builder.addProperty("formatter", customHandler.getFormatter());
        builder.addProperty("class", customHandler.getClassValue());
        builder.addProperty("module", customHandler.getModule());
        // TODO: AS7CliUtils.copyProperties(handler, builder, "level filter formatter class module");

        resultScript.append(builder.formatAndClearProps());

        if (customHandler.getProperties() != null && !customHandler.getProperties().isEmpty()) {
            StringBuilder propertiesBuilder = new StringBuilder();
            for (PropertyBean property : customHandler.getProperties()) {
                propertiesBuilder.append(", \"").append(property.getName()).append("\"=>");
                propertiesBuilder.append('"').append(property.getValue()).append('"');
            }
            String properties = propertiesBuilder.toString();
            properties = properties.replaceFirst(", ", "");
            resultScript.append(", properties={").append(properties).append('}');
        }

        resultScript.append(")");

        return resultScript.toString();
    }

    /**
     *  Creates a CLI script for changing a root-logger. The Root-logger can be only modified with the CLI not added.
     *  So this method creates a script for changing the level, filter and handlers. Required attribute is level.
     *  (Changes possible in future)
     *
     * @param rootLogger object representing root-logger in AS7
     * @return  String containing scripts
     * @throws CliScriptException if attribute level is missing.
     */
    static String createRootLoggerScript(RootLoggerAS7Bean rootLogger) throws CliScriptException {
        String errMsg = " in root-logger must be set.";
        Utils.throwIfBlank(rootLogger.getRootLoggerLevel(), errMsg, "Level");

        StringBuilder resultScript = new StringBuilder();

        // TODO: Is filter attribute even existing in CLI and JBoss conf? Because it is impossible to set filter via CLI
        if (rootLogger.getRootLogFilValue() != null) {
            resultScript.append("/subsystem=logging/root-logger=ROOT:write-attribute(");
            resultScript.append("name=filter, value=").append(rootLogger.getRootLogFilValue());
            resultScript.append(")\n");
        }

        resultScript.append("/subsystem=logging/root-logger=ROOT:write-attribute(");
        resultScript.append("name=level, value=").append(rootLogger.getRootLoggerLevel());
        resultScript.append(")\n");

        for (String handler : rootLogger.getRootLoggerHandlers()) {
            resultScript.append("/subsystem=logging/root-logger=ROOT:root-logger-assign-handler(name=");
            resultScript.append(handler).append(")\n");
        }

        return resultScript.toString();
    }

}// class