org.jumpmind.symmetric.db.AbstractSymmetricDialect.java Source code

Java tutorial

Introduction

Here is the source code for org.jumpmind.symmetric.db.AbstractSymmetricDialect.java

Source

/**
 * Licensed to JumpMind Inc under one or more contributor
 * license agreements.  See the NOTICE file distributed
 * with this work for additional information regarding
 * copyright ownership.  JumpMind Inc licenses this file
 * to you under the GNU General Public License, version 3.0 (GPLv3)
 * (the "License"); you may not use this file except in compliance
 * with the License.
 *
 * You should have received a copy of the GNU General Public License,
 * version 3.0 (GPLv3) along with this library; if not, see
 * <http://www.gnu.org/licenses/>.
 *
 * 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.jumpmind.symmetric.db;

import static org.apache.commons.lang.StringUtils.isNotBlank;

import java.io.IOException;
import java.sql.Types;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.jumpmind.db.model.Column;
import org.jumpmind.db.model.Database;
import org.jumpmind.db.model.Table;
import org.jumpmind.db.platform.IAlterDatabaseInterceptor;
import org.jumpmind.db.platform.IDatabasePlatform;
import org.jumpmind.db.platform.IDdlBuilder;
import org.jumpmind.db.sql.ISqlResultsListener;
import org.jumpmind.db.sql.ISqlTemplate;
import org.jumpmind.db.sql.ISqlTransaction;
import org.jumpmind.db.sql.LogSqlResultsListener;
import org.jumpmind.db.sql.SqlException;
import org.jumpmind.db.sql.SqlScript;
import org.jumpmind.db.util.BinaryEncoding;
import org.jumpmind.exception.IoException;
import org.jumpmind.symmetric.Version;
import org.jumpmind.symmetric.common.Constants;
import org.jumpmind.symmetric.common.ParameterConstants;
import org.jumpmind.symmetric.ext.IDatabaseUpgradeListener;
import org.jumpmind.symmetric.io.data.DataEventType;
import org.jumpmind.symmetric.model.Channel;
import org.jumpmind.symmetric.model.Node;
import org.jumpmind.symmetric.model.Trigger;
import org.jumpmind.symmetric.model.TriggerHistory;
import org.jumpmind.symmetric.model.TriggerRouter;
import org.jumpmind.symmetric.service.IExtensionService;
import org.jumpmind.symmetric.service.IParameterService;
import org.jumpmind.util.AppUtils;
import org.jumpmind.util.FormatUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/*
 * The abstract class for database dialects.
 */
abstract public class AbstractSymmetricDialect implements ISymmetricDialect {

    protected final Logger log = LoggerFactory.getLogger(getClass());

    public static final int MAX_SYMMETRIC_SUPPORTED_TRIGGER_SIZE = 50;

    protected IDatabasePlatform platform;

    protected AbstractTriggerTemplate triggerTemplate;

    protected IParameterService parameterService;

    protected IExtensionService extensionService;

    protected Boolean supportsGetGeneratedKeys;

    protected String databaseName;

    protected String driverVersion;

    protected String driverName;

    protected int databaseMajorVersion;

    protected int databaseMinorVersion;

    protected String databaseProductVersion;

    protected Set<String> sqlKeywords;

    protected boolean supportsTransactionViews = false;

    protected Map<String, String> sqlReplacementTokens = new HashMap<String, String>();

    public AbstractSymmetricDialect() {
    }

    public AbstractSymmetricDialect(IParameterService parameterService, IDatabasePlatform platform) {
        this.parameterService = parameterService;
        this.platform = platform;

        log.info("The DbDialect being used is {}", this.getClass().getName());

        buildSqlReplacementTokens();

        ISqlTemplate sqlTemplate = this.platform.getSqlTemplate();
        this.databaseMajorVersion = sqlTemplate.getDatabaseMajorVersion();
        this.databaseMinorVersion = sqlTemplate.getDatabaseMinorVersion();
        this.databaseName = sqlTemplate.getDatabaseProductName();
        this.databaseProductVersion = sqlTemplate.getDatabaseProductVersion();
        this.driverName = sqlTemplate.getDriverName();
        this.driverVersion = sqlTemplate.getDriverVersion();

    }

    public boolean requiresAutoCommitFalseToSetFetchSize() {
        return false;
    }

    protected void buildSqlReplacementTokens() {
        sqlReplacementTokens.put("selectDataUsingGapsSqlHint", "");
    }

    public Map<String, String> getSqlReplacementTokens() {
        return sqlReplacementTokens;
    }

    /*
     * Provide a default implementation of this method using DDLUtils,
     * getMaxColumnNameLength()
     */
    public int getMaxTriggerNameLength() {
        int max = getPlatform().getDatabaseInfo().getMaxColumnNameLength();
        return max < MAX_SYMMETRIC_SUPPORTED_TRIGGER_SIZE && max > 0 ? max : MAX_SYMMETRIC_SUPPORTED_TRIGGER_SIZE;
    }

    public void verifyDatabaseIsCompatible() {
    }

    public void initTablesAndDatabaseObjects() {
        createOrAlterTablesIfNecessary();
        createRequiredDatabaseObjects();
        platform.resetCachedTableModel();
    }

    protected String replaceTokens(String sql, String objectName) {
        String ddl = FormatUtils.replace("functionName", objectName, sql);
        ddl = FormatUtils.replace("version", Version.versionWithUnderscores(), ddl);
        ddl = FormatUtils.replace("defaultSchema", platform.getDefaultSchema(), ddl);
        return ddl;
    }

    protected boolean installed(String sql, String objectName) {
        return platform.getSqlTemplate().queryForInt(replaceTokens(sql, objectName)) > 0;
    }

    protected void install(String sql, String objectName) {
        sql = replaceTokens(sql, objectName);
        log.info("Installing SymmetricDS database object:\n{}", sql);
        platform.getSqlTemplate().update(sql);
        log.info("Just installed {}", objectName);
    }

    protected void uninstall(String sql, String objectName) {
        sql = replaceTokens(sql, objectName);
        platform.getSqlTemplate().update(sql);
        log.info("Just uninstalled {}", objectName);
    }

    public void dropTablesAndDatabaseObjects() {
        Database modelFromDatabase = readSymmetricSchemaFromDatabase();
        platform.dropDatabase(modelFromDatabase, true);
        dropRequiredDatabaseObjects();
    }

    final public boolean doesTriggerExist(String catalogName, String schema, String tableName, String triggerName) {
        if (StringUtils.isNotBlank(triggerName)) {
            try {
                return doesTriggerExistOnPlatform(catalogName, schema, tableName, triggerName);
            } catch (Exception ex) {
                log.warn("Could not figure out if the trigger exists.  Assuming that is does not", ex);
                return false;
            }
        } else {
            return false;
        }
    }

    public abstract void dropRequiredDatabaseObjects();

    public abstract void createRequiredDatabaseObjects();

    abstract public BinaryEncoding getBinaryEncoding();

    abstract protected boolean doesTriggerExistOnPlatform(String catalogName, String schema, String tableName,
            String triggerName);

    public String getTransactionTriggerExpression(String defaultCatalog, String defaultSchema, Trigger trigger) {
        return "null";
    }

    public String createInitialLoadSqlFor(Node node, TriggerRouter trigger, Table table,
            TriggerHistory triggerHistory, Channel channel, String overrideSelectSql) {
        return triggerTemplate.createInitalLoadSql(node, trigger, table, triggerHistory, channel, overrideSelectSql)
                .trim();
    }

    public String createPurgeSqlFor(Node node, TriggerRouter triggerRouter, TriggerHistory triggerHistory) {
        String sql = null;
        if (StringUtils.isEmpty(triggerRouter.getInitialLoadDeleteStmt())) {
            sql = String.format(parameterService.getString(ParameterConstants.INITIAL_LOAD_DELETE_FIRST_SQL),
                    triggerRouter.qualifiedTargetTableName(triggerHistory));
        } else {
            sql = triggerRouter.getInitialLoadDeleteStmt();
        }
        return sql;
    }

    public String createCsvDataSql(Trigger trigger, TriggerHistory triggerHistory, Channel channel,
            String whereClause) {
        return triggerTemplate
                .createCsvDataSql(
                        trigger, triggerHistory, platform.getTableFromCache(trigger.getSourceCatalogName(),
                                trigger.getSourceSchemaName(), trigger.getSourceTableName(), false),
                        channel, whereClause)
                .trim();
    }

    public String createCsvPrimaryKeySql(Trigger trigger, TriggerHistory triggerHistory, Channel channel,
            String whereClause) {
        return triggerTemplate
                .createCsvPrimaryKeySql(
                        trigger, triggerHistory, platform.getTableFromCache(trigger.getSourceCatalogName(),
                                trigger.getSourceSchemaName(), trigger.getSourceTableName(), false),
                        channel, whereClause)
                .trim();
    }

    public Set<String> getSqlKeywords() {
        if (sqlKeywords == null) {
            this.sqlKeywords = this.platform.getSqlTemplate().getSqlKeywords();
        }
        return sqlKeywords;
    }

    protected String getDropTriggerSql(StringBuilder sqlBuffer, String catalogName, String schemaName,
            String triggerName, String tableName) {
        schemaName = schemaName == null ? "" : (schemaName + ".");
        return "drop trigger " + schemaName + triggerName;
    }

    public void removeTrigger(StringBuilder sqlBuffer, String catalogName, String schemaName, String triggerName,
            String tableName) {
        String sql = getDropTriggerSql(sqlBuffer, catalogName, schemaName, triggerName, tableName);
        logSql(sql, sqlBuffer);
        if (parameterService.is(ParameterConstants.AUTO_SYNC_TRIGGERS)) {
            try {
                this.platform.getSqlTemplate().update(sql);
            } catch (Exception e) {
                log.warn("Tried to remove trigger using: {} and failed because: {}", sql, e.getMessage());
            }
        }
    }

    final protected void logSql(String sql, StringBuilder sqlBuffer) {
        if (sqlBuffer != null && StringUtils.isNotBlank(sql)) {
            sqlBuffer.append(sql);
            sqlBuffer.append(System.getProperty("line.separator"));
            sqlBuffer.append(System.getProperty("line.separator"));
        }
    }

    /*
     * Create the configured trigger. The catalog will be changed to the source
     * schema if the source schema is configured.
     */
    public void createTrigger(final StringBuilder sqlBuffer, final DataEventType dml, final Trigger trigger,
            final TriggerHistory hist, final Channel channel, final String tablePrefix, final Table table) {
        log.info("Creating {} trigger for {}", hist.getTriggerNameForDmlType(dml),
                table.getFullyQualifiedTableName());

        String previousCatalog = null;
        String sourceCatalogName = table.getCatalog();
        String defaultCatalog = platform.getDefaultCatalog();
        String defaultSchema = platform.getDefaultSchema();

        String triggerSql = triggerTemplate.createTriggerDDL(dml, trigger, hist, channel, tablePrefix, table,
                defaultCatalog, defaultSchema);

        String postTriggerDml = createPostTriggerDDL(dml, trigger, hist, channel, tablePrefix, table);

        if (parameterService.is(ParameterConstants.AUTO_SYNC_TRIGGERS)) {
            ISqlTransaction transaction = null;
            try {
                transaction = this.platform.getSqlTemplate()
                        .startSqlTransaction(platform.getDatabaseInfo().isRequiresAutoCommitForDdl());
                previousCatalog = switchCatalogForTriggerInstall(sourceCatalogName, transaction);

                try {
                    log.debug("Running: {}", triggerSql);
                    transaction.execute(triggerSql);
                } catch (SqlException ex) {
                    log.info("Failed to create trigger: {}", triggerSql);
                    throw ex;
                }

                if (StringUtils.isNotBlank(postTriggerDml)) {
                    try {
                        transaction.execute(postTriggerDml);
                    } catch (SqlException ex) {
                        log.info("Failed to create post trigger: {}", postTriggerDml);
                        throw ex;
                    }
                }
                transaction.commit();
            } catch (SqlException ex) {
                transaction.rollback();
                throw ex;
            } finally {
                try {
                    if (sourceCatalogName != null && !sourceCatalogName.equalsIgnoreCase(previousCatalog)) {
                        switchCatalogForTriggerInstall(previousCatalog, transaction);
                    }
                } finally {
                    transaction.close();
                }

            }
        }

        logSql(triggerSql, sqlBuffer);
        logSql(postTriggerDml, sqlBuffer);

    }

    /*
     * Provide the option switch a connection's schema for trigger installation.
     */
    protected String switchCatalogForTriggerInstall(String catalog, ISqlTransaction transaction) {
        return null;
    }

    protected String createPostTriggerDDL(DataEventType dml, Trigger trigger, TriggerHistory hist, Channel channel,
            String tablePrefix, Table table) {
        return triggerTemplate.createPostTriggerDDL(dml, trigger, hist, channel, tablePrefix, table,
                platform.getDefaultCatalog(), platform.getDefaultSchema());
    }

    public String getCreateSymmetricDDL() {
        Database database = readSymmetricSchemaFromXml();
        prefixConfigDatabase(database);
        IDdlBuilder builder = platform.getDdlBuilder();
        return builder.createTables(database, true);
    }

    protected void prefixConfigDatabase(Database targetTables) {
        platform.prefixDatabase(parameterService.getTablePrefix(), targetTables);
    }

    public Table getTable(TriggerHistory triggerHistory, boolean useCache) {
        if (triggerHistory != null) {
            return platform.getTableFromCache(triggerHistory.getSourceCatalogName(),
                    triggerHistory.getSourceSchemaName(), triggerHistory.getSourceTableName(), !useCache);
        } else {
            return null;
        }
    }

    /*
     * @return true if SQL was executed.
     */
    public boolean createOrAlterTablesIfNecessary(String... tableNames) {
        try {
            log.info("Checking if SymmetricDS tables need created or altered");

            Database modelFromXml = readSymmetricSchemaFromXml();
            Database modelFromDatabase = readSymmetricSchemaFromDatabase();

            if (tableNames != null && tableNames.length > 0) {
                tableNames = platform.alterCaseToMatchDatabaseDefaultCase(tableNames);
                modelFromXml.removeAllTablesExcept(tableNames);
                modelFromDatabase.removeAllTablesExcept(tableNames);
            }

            IDdlBuilder builder = platform.getDdlBuilder();

            List<IAlterDatabaseInterceptor> alterDatabaseInterceptors = extensionService
                    .getExtensionPointList(IAlterDatabaseInterceptor.class);
            IAlterDatabaseInterceptor[] interceptors = alterDatabaseInterceptors
                    .toArray(new IAlterDatabaseInterceptor[alterDatabaseInterceptors.size()]);
            if (builder.isAlterDatabase(modelFromDatabase, modelFromXml, interceptors)) {
                String delimiter = platform.getDatabaseInfo().getSqlCommandDelimiter();

                ISqlResultsListener resultsListener = new LogSqlResultsListener(log);
                List<IDatabaseUpgradeListener> databaseUpgradeListeners = extensionService
                        .getExtensionPointList(IDatabaseUpgradeListener.class);

                String alterSql = builder.alterDatabase(modelFromDatabase, modelFromXml, interceptors);
                if (isNotBlank(alterSql)) {
                    log.info("There are SymmetricDS tables that needed altered");

                    for (IDatabaseUpgradeListener listener : databaseUpgradeListeners) {
                        String sql = listener.beforeUpgrade(this, this.parameterService.getTablePrefix(),
                                modelFromDatabase, modelFromXml);
                        SqlScript script = new SqlScript(sql, getPlatform().getSqlTemplate(), true, false, false,
                                delimiter, null);
                        script.setListener(resultsListener);
                        script.execute(platform.getDatabaseInfo().isRequiresAutoCommitForDdl());
                    }

                    log.debug("Alter SQL generated: {}", alterSql);

                    SqlScript script = new SqlScript(alterSql, getPlatform().getSqlTemplate(), true, false, false,
                            delimiter, null);
                    script.setListener(resultsListener);
                    script.execute(platform.getDatabaseInfo().isRequiresAutoCommitForDdl());

                    for (IDatabaseUpgradeListener listener : databaseUpgradeListeners) {
                        String sql = listener.afterUpgrade(this, this.parameterService.getTablePrefix(),
                                modelFromXml);
                        script = new SqlScript(sql, getPlatform().getSqlTemplate(), true, false, false, delimiter,
                                null);
                        script.setListener(resultsListener);
                        script.execute(platform.getDatabaseInfo().isRequiresAutoCommitForDdl());
                    }

                    log.info("Done with auto update of SymmetricDS tables");
                    return true;
                } else {
                    return false;
                }
            } else {
                return false;
            }
        } catch (RuntimeException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public Database readSymmetricSchemaFromXml() {
        try {
            Database database = merge(readDatabaseFromXml("/symmetric-schema.xml"),
                    readDatabaseFromXml("/console-schema.xml"));
            prefixConfigDatabase(database);

            String extraTablesXml = parameterService.getString(ParameterConstants.AUTO_CONFIGURE_EXTRA_TABLES);
            if (StringUtils.isNotBlank(extraTablesXml)) {
                try {
                    database = merge(database, readDatabaseFromXml(extraTablesXml));
                } catch (Exception ex) {
                    log.error("", ex);
                }
            }

            return database;
        } catch (RuntimeException ex) {
            throw ex;
        } catch (Exception ex) {
            throw new RuntimeException(ex);
        }
    }

    public Database readSymmetricSchemaFromDatabase() {
        return platform.readFromDatabase(readSymmetricSchemaFromXml().getTables());
    }

    protected Database readDatabaseFromXml(String resourceName) throws IOException {
        try {
            return platform.readDatabaseFromXml(resourceName, true);
        } catch (IoException ex) {
            return new Database();
        }
    }

    protected Database merge(Database... databases) {
        Database database = new Database();
        if (databases != null) {
            for (Database db : databases) {
                Table[] tables = db.getTables();
                for (Table table : tables) {
                    database.addTable(table);
                }
            }
        }
        return database;
    }

    public IDatabasePlatform getPlatform() {
        return this.platform;
    }

    public String getName() {
        return databaseName;
    }

    public String getVersion() {
        return databaseMajorVersion + "." + databaseMinorVersion;
    }

    public int getMajorVersion() {
        return databaseMajorVersion;
    }

    public int getMinorVersion() {
        return databaseMinorVersion;
    }

    public String getProductVersion() {
        return databaseProductVersion;
    }

    public boolean supportsTransactionViews() {
        return supportsTransactionViews;
    }

    public long insertWithGeneratedKey(String sql, SequenceIdentifier sequenceId) {
        return insertWithGeneratedKey(sql, sequenceId, null, null);
    }

    public long insertWithGeneratedKey(final String sql, final SequenceIdentifier identifier, Object... args) {
        return platform.getSqlTemplate().insertWithGeneratedKey(sql, getSequenceKeyName(identifier),
                getSequenceKeyName(identifier), args, null);
    }

    public String getSequenceName(SequenceIdentifier identifier) {
        switch (identifier) {
        case REQUEST:
            return "sym_extract_r_st_request_id";
        case DATA:
            return "sym_data_data_id";
        case TRIGGER_HIST:
            return "sym_trigger_his_ger_hist_id";
        }
        return null;
    }

    public String getSequenceKeyName(SequenceIdentifier identifier) {
        switch (identifier) {
        case REQUEST:
            return "request_id";
        case DATA:
            return "data_id";
        case TRIGGER_HIST:
            return "trigger_hist_id";
        }
        return null;
    }

    @Deprecated
    public Column[] orderColumns(String[] columnNames, Table table) {
        Column[] unorderedColumns = table.getColumns();
        Column[] orderedColumns = new Column[columnNames.length];
        for (int i = 0; i < columnNames.length; i++) {
            String name = columnNames[i];
            for (Column column : unorderedColumns) {
                if (column.getName().equalsIgnoreCase(name)) {
                    orderedColumns[i] = column;
                    break;
                }
            }
        }
        return orderedColumns;
    }

    public void disableSyncTriggers(ISqlTransaction transaction) {
        disableSyncTriggers(transaction, null);
    }

    public boolean supportsTransactionId() {
        return false;
    }

    public boolean isBlobSyncSupported() {
        return true;
    }

    public boolean isClobSyncSupported() {
        return true;
    }

    public boolean isTransactionIdOverrideSupported() {
        return true;
    }

    public String getEngineName() {
        return parameterService.getString(ParameterConstants.ENGINE_NAME);
    }

    public boolean supportsOpenCursorsAcrossCommit() {
        return true;
    }

    public String getInitialLoadTableAlias() {
        return "t";
    }

    public String preProcessTriggerSqlClause(String sqlClause) {
        return sqlClause;
    }

    public void truncateTable(String tableName) {
        String quote = platform.getDdlBuilder().isDelimitedIdentifierModeOn()
                ? platform.getDatabaseInfo().getDelimiterToken()
                : "";
        boolean success = false;
        int tryCount = 5;
        while (!success && tryCount > 0) {
            try {
                Table table = platform.getTableFromCache(tableName, false);
                if (table != null) {
                    platform.getSqlTemplate()
                            .update(String.format("truncate table %s%s%s", quote, table.getName(), quote));
                    success = true;
                } else {
                    throw new RuntimeException(String.format("Could not find %s to trunate", tableName));
                }
            } catch (SqlException ex) {
                log.warn("", ex);
                AppUtils.sleep(5000);
                tryCount--;
            }
        }
    }

    public boolean areDatabaseTransactionsPendingSince(long time) {
        throw new UnsupportedOperationException();
    }

    public long getDatabaseTime() {
        try {
            String sql = "select current_timestamp from " + this.parameterService.getTablePrefix()
                    + "_node_identity";
            sql = FormatUtils.replaceTokens(sql, platform.getSqlScriptReplacementTokens(), false);
            Date dateTime = this.platform.getSqlTemplate().queryForObject(sql, java.util.Date.class);
            if (dateTime != null) {
                return dateTime.getTime();
            } else {
                return System.currentTimeMillis();
            }
        } catch (Exception ex) {
            log.error("", ex);
            return System.currentTimeMillis();
        }
    }

    public String getSourceNodeExpression() {
        return null;
    }

    final public String getDataHasChangedCondition(Trigger trigger) {
        if (parameterService.is(ParameterConstants.TRIGGER_UPDATE_CAPTURE_CHANGED_DATA_ONLY)) {
            return getDbSpecificDataHasChangedCondition(trigger);
        } else {
            return Constants.ALWAYS_TRUE_CONDITION;
        }
    }

    protected String getDbSpecificDataHasChangedCondition(Trigger trigger) {
        return Constants.ALWAYS_TRUE_CONDITION;
    }

    public boolean needsToSelectLobData() {
        return false;
    }

    public boolean canGapsOccurInCapturedDataIds() {
        return true;
    }

    public String massageDataExtractionSql(String sql, Channel channel) {
        String textColumnExpression = parameterService
                .getString(ParameterConstants.DATA_EXTRACTOR_TEXT_COLUMN_EXPRESSION);
        if (isNotBlank(textColumnExpression)) {
            sql = sql.replace("d.old_data", textColumnExpression.replace("$(columnName)", "d.old_data"));
            sql = sql.replace("d.row_data", textColumnExpression.replace("$(columnName)", "d.row_data"));
            sql = sql.replace("d.pk_data", textColumnExpression.replace("$(columnName)", "d.pk_data"));
        }
        return sql;
    }

    public String getDriverName() {
        return driverName;
    }

    public String getDriverVersion() {
        return driverVersion;
    }

    public String massageForLob(String sql, Channel channel) {
        return sql;
    }

    public boolean escapesTemplatesForDatabaseInserts() {
        return false;
    }

    public String getMasterCollation() {
        return parameterService.getString(ParameterConstants.DB_MASTER_COLLATION, "");
    }

    public boolean supportsBatchUpdates() {
        return true;
    }

    public void cleanupTriggers() {
    }

    public AbstractTriggerTemplate getTriggerTemplate() {
        return triggerTemplate;
    }

    protected void close(ISqlTransaction transaction) {
        if (transaction != null) {
            transaction.close();
        }
    }

    public String getTablePrefix() {
        return parameterService.getTablePrefix();
    }

    public String getTemplateNumberPrecisionSpec() {
        return null;
    }

    public int getSqlTypeForIds() {
        return Types.NUMERIC;
    }

    @Override
    public IParameterService getParameterService() {
        return parameterService;
    }

    public void setExtensionService(IExtensionService extensionService) {
        this.extensionService = extensionService;
    }
}