com.jaspersoft.jasperserver.war.action.DataSourceAction.java Source code

Java tutorial

Introduction

Here is the source code for com.jaspersoft.jasperserver.war.action.DataSourceAction.java

Source

/*
 * Copyright (C) 2005 - 2014 TIBCO Software Inc. All rights reserved.
 * http://www.jaspersoft.com.
 *
 * Unless you have purchased  a commercial license agreement from Jaspersoft,
 * the following license terms  apply:
 *
 * This program is free software: you can redistribute it and/or  modify
 * it under the terms of the GNU Affero General Public License  as
 * published by the Free Software Foundation, either version 3 of  the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero  General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public  License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

package com.jaspersoft.jasperserver.war.action;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.jaspersoft.jasperserver.api.JSDuplicateResourceException;
import com.jaspersoft.jasperserver.api.JSException;
import com.jaspersoft.jasperserver.api.engine.common.service.EngineService;
import com.jaspersoft.jasperserver.api.common.service.JdbcDriverService;
import com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.AwsDataSourceService;
import com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.CustomReportDataSourceServiceFactory;
import com.jaspersoft.jasperserver.api.engine.jasperreports.service.impl.TibcoDriverManagerImpl;
import com.jaspersoft.jasperserver.api.engine.jasperreports.util.*;
import com.jaspersoft.jasperserver.api.metadata.common.domain.Resource;
import com.jaspersoft.jasperserver.api.metadata.common.domain.ResourceLookup;
import com.jaspersoft.jasperserver.api.metadata.common.domain.ResourceReference;
import com.jaspersoft.jasperserver.api.metadata.common.service.RepositoryService;
import com.jaspersoft.jasperserver.api.metadata.common.service.ResourceFactory;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.AwsReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.BeanReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.CustomReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.JdbcReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.JndiJdbcReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.ReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.VirtualReportDataSource;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.domain.client.VirtualReportDataSourceImpl;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.service.CustomReportDataSourceService;
import com.jaspersoft.jasperserver.api.metadata.jasperreports.service.ReportDataSourceService;
import com.jaspersoft.jasperserver.api.metadata.user.domain.TenantQualified;
import com.jaspersoft.jasperserver.core.util.JSONUtil;
import com.jaspersoft.jasperserver.war.common.ConfigurationBean;
import com.jaspersoft.jasperserver.war.common.JasperServerConstImpl;
import com.jaspersoft.jasperserver.war.common.JdkTimeZonesList;
import com.jaspersoft.jasperserver.war.dto.BaseDTO;
import com.jaspersoft.jasperserver.war.dto.ReportDataSourceWrapper;
import com.jaspersoft.jasperserver.war.dto.StringOption;
import org.apache.commons.lang.exception.ExceptionUtils;
import org.json.JSONObject;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.MessageSource;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.security.context.SecurityContextHolder;
import org.springframework.validation.Errors;
import org.springframework.validation.FieldError;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.webflow.action.FormAction;
import org.springframework.webflow.execution.Event;
import org.springframework.webflow.execution.RequestContext;
import org.springframework.webflow.execution.ScopeType;

import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.net.ConnectException;
import java.net.SocketTimeoutException;
import java.sql.Connection;
import java.sql.Driver;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.*;

import static org.apache.commons.lang.StringUtils.isBlank;
import static org.apache.commons.lang.StringUtils.isEmpty;

public class DataSourceAction extends FormAction implements ApplicationContextAware {
    private static final String ATTRIBUTE_RESOURCE_ID_NOT_SUPPORTED_SYMBOLS = "resourceIdNotSupportedSymbols";
    private static final String RESOURCE_DATA_SOURCE_CONNECTION_STATE_FAILED = "resource.dataSource.connectionState.failed";
    private static final String RESOURCE_DATA_SOURCE_CONNECTION_STATE_PASSED = "resource.dataSource.connectionState.passed";

    private RepositoryService repository;
    private JasperServerConstImpl constants = new JasperServerConstImpl();
    private JdkTimeZonesList timezones;
    private ResourceFactory dataSourceMappings;
    private List<String> awsRegions;
    private AwsDataSourceRecovery awsDataSourceRecovery;
    private AwsProperties awsProperties;

    private AwsEc2MetadataClient awsEc2MetadataClient;
    private InstanceProductTypeResolver instanceProductTypeResolver;

    public static final String AJAX_RESPONSE_MODEL = "ajaxResponseModel";

    public static final String FORM_OBJECT_KEY = "dataResource";
    public static final String JDBC_DRIVERS_JSON_KEY = "jdbcDriversJSON";
    public static final String PASSWORD_SUBSTITUTION_KEY = "passwordSubstitution";
    public static final String VALIDATION_PATTERNS = "validationPatterns";
    public static final String DYNAMIC_URL_PART_PATTERN = "dynamicUrlPartPattern";
    public static final String DATASOURCEURI_PARAM = "resource";
    public static final String PARENT_FOLDER_ATTR = "parentFolder";
    public static final String PARENT_FOLDER_URI = "ParentFolderUri";
    public static final String DATASOURCE_JDBC = "jdbc";
    public static final String DATASOURCE_JNDI = "jndi";
    public static final String DATASOURCE_BEAN = "bean";
    public static final String DATASOURCE_VIRTUAL = "virtual";
    public static final String DATASOURCE_AWS = "aws";
    public static final String TYPE = "type";

    public static final String SUB_DATASOURCES_JSON_KEY = "selectedSubDs";
    public static final String SUB_DATASOURCE_ID_KEY = "dsId";
    public static final String SUB_DATASOURCE_URI_KEY = "dsUri";
    public static final String SUB_DATASOURCE_NAME_KEY = "dsName";
    public static final String DEPENDENT_RESOURCES_JSON_KEY = "dependentResources";

    public static final int MAX_DEPENDENT_RESOURCES = 20;

    protected MessageSource messages;
    private ConfigurationBean configuration;
    private CustomReportDataSourceServiceFactory customDataSourceFactory;
    private EngineService engine;
    private String queryLanguageFlowAttribute;
    private String UPLOAD_DRIVER_PREFIX = "file_";
    private String passwordSubstitution = null;

    private ObjectMapper jsonMapper = new ObjectMapper();
    private JdbcDriverService jdbcDriverService;
    private Map<String, Map<String, Object>> jdbcConnectionMap;
    private String dynamicUrlPartPattern;
    private Map<String, String> validationPatternsMap;

    ApplicationContext ctx;

    public DataSourceAction() {
        setFormObjectClass(ReportDataSourceWrapper.class); //custom form backing object class
        setFormObjectName(FORM_OBJECT_KEY);
        setFormObjectScope(ScopeType.FLOW); //this is a multi-page wizard!
    }

    /* (non-Javadoc)
    * @see org.springframework.context.ApplicationContextAware#setApplicationContext(org.springframework.context.ApplicationContext)
    */
    public void setApplicationContext(ApplicationContext arg0) throws BeansException {
        ctx = arg0;
    }

    public Event initAction(RequestContext context) throws Exception {
        ReportDataSourceWrapper formObject = (ReportDataSourceWrapper) getFormObject(context);
        Object parentFlowObject = null;
        if (formObject != null) {
            parentFlowObject = formObject.getParentFlowObject();
        }
        if (formObject.isNewMode()) {
            context.getFlowScope().put(TYPE, context.getExternalContext().getRequestParameterMap().get(TYPE));
        } else {
            context.getFlowScope().put(TYPE, getTypeByFormObjectByType(formObject.getType()));
        }

        String typeFromRequest = context.getExternalContext().getRequestParameterMap().get(TYPE);
        boolean forceNewMode = formObject.isSubflowMode();
        if (formObject.isNewMode() || typeFromRequest != null
                && !typeFromRequest.equals(getTypeByFormObjectByType(formObject.getType()))) {
            formObject = (ReportDataSourceWrapper) createFormObject(context);
            if (forceNewMode) {
                formObject.setParentFlowObject(parentFlowObject);
                formObject.setMode(BaseDTO.MODE_SUB_FLOW_NEW);
            }
        }

        ReportDataSource ds = formObject.getReportDataSource();

        Locale displayLocale = LocaleContextHolder.getLocale();
        String selectedTimezone = null;
        if (ds instanceof JdbcReportDataSource)
            selectedTimezone = ((JdbcReportDataSource) ds).getTimezone();
        if (ds instanceof JndiJdbcReportDataSource)
            selectedTimezone = ((JndiJdbcReportDataSource) ds).getTimezone();

        List timezoneList = timezones.getTimeZones(displayLocale);
        timezoneList = new ArrayList(timezoneList);
        if (selectedTimezone != null && selectedTimezone.length() > 0) {
            TimeZone zone = TimeZone.getTimeZone(selectedTimezone);
            StringOption option = new StringOption(selectedTimezone, zone.getDisplayName(displayLocale));
            if (!timezoneList.contains(option))
                timezoneList.add(0, option);
        }
        context.getFlowScope().put("timezones", timezoneList);
        context.getFlowScope().put("selectedTimezone", selectedTimezone);
        getFormObjectAccessor(context).setCurrentFormObject(formObject, ScopeType.FLOW);
        context.getFlowScope().put(FORM_OBJECT_KEY, formObject);
        context.getFlowScope().put(ATTRIBUTE_RESOURCE_ID_NOT_SUPPORTED_SYMBOLS,
                configuration.getResourceIdNotSupportedSymbols());
        context.getFlowScope().put("awsRegions", getAwsRegions());
        context.getFlowScope().put("isEc2Instance", awsEc2MetadataClient.isEc2Instance());
        context.getFlowScope().put("suppressEc2CredentialsWarnings",
                awsProperties.isSuppressEc2CredentialsWarnings());

        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
        if (principal != null && principal instanceof TenantQualified) {
            if (((TenantQualified) principal).getTenantId() != null) {
                context.getFlowScope().put("tenantId", ((TenantQualified) principal).getTenantId());
            }
        }

        String requestParentFolder = context.getExternalContext().getRequestParameterMap().get(PARENT_FOLDER_URI);
        if (formObject.getReportDataSource() != null) {
            if (isBlank(requestParentFolder) || !repository.repositoryPathExists(null, requestParentFolder)) {
                requestParentFolder = null;
            }
            formObject.getReportDataSource().setParentFolder(requestParentFolder);
        }

        if (formObject.isSubflowMode() && formObject.getAllDatasources() == null) {
            //   context.getFlowScope().put(FORM_OBJECT_KEY, formObject);
            context.getFlowScope().put("constants", constants);
            //return success();
        }

        if (ds instanceof CustomReportDataSource) {
            CustomReportDataSource cds = (CustomReportDataSource) ds;
            // look up definition & use it to init defaults & set prop defs
            CustomDataSourceDefinition customDef = customDataSourceFactory.getDefinition(cds);
            if (customDef != null) {
                customDef.setDefaultValues(cds);
                formObject.setCustomProperties(customDef.getEditablePropertyDefinitions());
                formObject.setCustomDatasourceLabel(customDef.getLabelName());
                context.getFlowScope().put(TYPE, customDef.getName());
            }
        }

        passwordSubstitution = messages.getMessage("input.password.substitution", null,
                LocaleContextHolder.getLocale());

        context.getFlowScope().put(JDBC_DRIVERS_JSON_KEY, JSONUtil.toJSON(getAvailableJdbcDrivers()));
        context.getFlowScope().put(DYNAMIC_URL_PART_PATTERN, JSONUtil.toJSON(this.dynamicUrlPartPattern));
        context.getFlowScope().put(VALIDATION_PATTERNS, JSONUtil.toJSON(this.validationPatternsMap));
        context.getFlowScope().put(PASSWORD_SUBSTITUTION_KEY, passwordSubstitution);

        return success();
    }

    public Map<String, Map<String, Object>> getAvailableJdbcDrivers() {
        Map<String, Map<String, Object>> availableDrivers = new LinkedHashMap<String, Map<String, Object>>();

        for (Map.Entry<String, Map<String, Object>> entry : jdbcConnectionMap.entrySet()) {
            Map<String, Object> driverData = new HashMap<String, Object>();
            driverData.putAll(entry.getValue());
            driverData.put("available",
                    jdbcDriverService.isRegistered(String.valueOf(entry.getValue().get("jdbcDriverClass"))));
            availableDrivers.put(entry.getKey(), driverData);
        }

        for (String registeredDriverClassName : jdbcDriverService.getRegisteredDriverClassNames()) {
            boolean defined = false;
            for (Map.Entry<String, Map<String, Object>> entry : jdbcConnectionMap.entrySet()) {
                if (registeredDriverClassName.equals(entry.getValue().get("jdbcDriverClass"))) {
                    defined = true;
                    break;
                }
            }
            if (!defined) {
                availableDrivers.put(registeredDriverClassName, null);
            }
        }

        return availableDrivers;
    }

    public String getTypeByFormObjectByType(String formObjectType) {
        if (JasperServerConstImpl.getJNDIDatasourceType().equals(formObjectType)) {
            return DATASOURCE_JNDI;
        } else if (JasperServerConstImpl.getJDBCDatasourceType().equals(formObjectType)) {
            return DATASOURCE_JDBC;
        } else if (JasperServerConstImpl.getBeanDatasourceType().equals(formObjectType)) {
            return DATASOURCE_BEAN;
        }
        if (JasperServerConstImpl.getVirtualDatasourceType().equals(formObjectType)) {
            return DATASOURCE_VIRTUAL;
        }
        if (JasperServerConstImpl.getAwsDatasourceType().equals(formObjectType)) {
            return DATASOURCE_AWS;
        }
        return formObjectType;
    }

    public Event prepareChooseType(RequestContext context) throws Exception {
        Map typeMap = new HashMap();

        // add custom types
        Iterator cdsi = customDataSourceFactory.getDefinitions().iterator();
        while (cdsi.hasNext()) {
            CustomDataSourceDefinition cds = (CustomDataSourceDefinition) cdsi.next();
            typeMap.put(cds.getName(),
                    messages.getMessage(cds.getLabelName(), null, LocaleContextHolder.getLocale()));
        }

        context.getRequestScope().put("allTypes", typeMap);
        return success();
    }

    /**
     * Bind sub-datasources for virtual datasource
     *
     * @param context
     * @return
     * @throws Exception
     */
    public Event bindSubDatasources(RequestContext context) throws Exception {
        ReportDataSourceWrapper formObject = (ReportDataSourceWrapper) getFormObject(context);
        VirtualReportDataSource vds = (VirtualReportDataSource) formObject.getReportDataSource();
        String subDsJSON = (String) formObject.getNamedProperties().get(SUB_DATASOURCES_JSON_KEY);
        vds.getDataSourceUriMap().clear();
        if (isEmpty(subDsJSON)) {
            return success();
        }
        JsonNode subDsList = jsonMapper.readTree(subDsJSON);
        for (Iterator<JsonNode> i = subDsList.iterator(); i.hasNext();) {
            JsonNode subDs = i.next();
            String id = subDs.get(SUB_DATASOURCE_ID_KEY).asText();
            String uri = subDs.get(SUB_DATASOURCE_URI_KEY).asText();
            vds.getDataSourceUriMap().put(id, new ResourceReference(uri));
        }
        return success();
    }

    public Event saveDatasource(RequestContext context) throws Exception {
        ReportDataSourceWrapper wrapper = (ReportDataSourceWrapper) getFormObject(context);
        if (wrapper.isStandAloneMode()) {
            if (wrapper.getType() != null) {

                ReportDataSource ds = wrapper.getReportDataSource();

                if ((ds instanceof JdbcReportDataSource)
                        && (((JdbcReportDataSource) ds).getDriverClass()).equalsIgnoreCase("VirtualDS")) {
                    VirtualReportDataSourceImpl fds = new VirtualReportDataSourceImpl();
                    fds.setCreationDate(ds.getCreationDate());
                    fds.setDescription(ds.getDescription());
                    fds.setLabel(ds.getLabel());
                    fds.setName(ds.getName());
                    fds.setParentFolder(ds.getParentFolder());
                    //   fds.setTimezone();
                    fds.setUpdateDate(ds.getUpdateDate());

                    try {
                        String[] paths = ds.getDescription().split("\\|");
                        String schemaList = ((JdbcReportDataSource) ds).getConnectionUrl();
                        String[] schemas = null;
                        if ((schemaList != null) && !schemaList.equals("")) {
                            schemas = schemaList.split("\\|");
                        }
                        Map<String, ResourceReference> uriMap = new HashMap<String, ResourceReference>();
                        for (int i = 0; i < paths.length; i++) {
                            String schemaName = null;
                            String path = paths[i].trim();
                            if ((schemas != null) && (i < schemas.length))
                                schemaName = schemas[i].trim();
                            else
                                schemaName = path.substring(path.lastIndexOf("/") + 1);
                            uriMap.put(schemaName, new ResourceReference(path));
                        }
                        fds.setDataSourceUriMap(uriMap);
                        ds = fds;
                    } catch (Exception ex) {
                        ex.printStackTrace();
                    }
                }
                if (ds.getName() != null) {
                    try {

                        // On edit datasource we set the passwordSubstitution to the passwords form fields
                        // If we get the substitution from UI then set the password from original datasource (if it exists)
                        if (ds instanceof JdbcReportDataSource
                                && ((JdbcReportDataSource) ds).getPassword().equals(passwordSubstitution)) {
                            JdbcReportDataSource existingDs = (JdbcReportDataSource) repository.getResource(null,
                                    ds.getURIString());
                            if (existingDs != null) {
                                ((JdbcReportDataSource) ds).setPassword(existingDs.getPassword());
                            }
                        }
                        if (ds instanceof AwsReportDataSource
                                && ((AwsReportDataSource) ds).getAWSSecretKey().equals(passwordSubstitution)) {
                            AwsReportDataSource existingDs = (AwsReportDataSource) repository.getResource(null,
                                    ds.getURIString());
                            if (existingDs != null) {
                                ((AwsReportDataSource) ds).setAWSSecretKey(existingDs.getAWSSecretKey());
                            }
                        }
                        if (ds instanceof CustomReportDataSource) {
                            Object password = ((CustomReportDataSource) ds).getPropertyMap().get("password");
                            if (password != null && password.equals(passwordSubstitution)) {
                                CustomReportDataSource existingDs = (CustomReportDataSource) repository
                                        .getResource(null, ds.getURIString());
                                if (existingDs != null) {
                                    ((CustomReportDataSource) ds).getPropertyMap().put("password",
                                            existingDs.getPropertyMap().get("password"));
                                }
                            }
                        }

                        repository.saveResource(null, ds);
                        if (wrapper.isEditMode() && ds instanceof AwsReportDataSource) {
                            try {
                                awsDataSourceRecovery.createAwsDSSecurityGroup((AwsReportDataSource) ds);
                            } catch (Exception ex) {
                                logger.error("Exception creating of Aws Security Group", ex);
                            }
                        }
                    } catch (JSDuplicateResourceException e) {
                        getFormErrors(context).rejectValue("reportDataSource.name",
                                "ReportDataSourceValidator.error.duplicate");
                        return error();
                    }
                }
            }
            if (!wrapper.isEditMode()) {
                context.getExternalContext().getSessionMap().put("repositorySystemConfirm",
                        messages.getMessage("resource.file.fileAdded",
                                new String[] { wrapper.getReportDataSource().getName(),
                                        wrapper.getReportDataSource().getParentFolder() },
                                LocaleContextHolder.getLocale()));
            }
            return yes();
        }
        return success();
    }

    public Event testCustomDataSource(RequestContext context) throws Exception {
        String connectionStatusMessageKey = RESOURCE_DATA_SOURCE_CONNECTION_STATE_FAILED;
        if (customDataSourceFactory != null) {
            try {
                ReportDataSourceWrapper wrapper = (ReportDataSourceWrapper) getFormObject(context);
                CustomReportDataSource ds = (CustomReportDataSource) wrapper.getReportDataSource();

                // On edit datasource we set the passwordSubstitution to the passwords form fields
                // If we get the substitution from UI then set the password from original datasource (if it exists)
                if (ds.getPropertyMap().get("password") != null
                        && ds.getPropertyMap().get("password").equals(passwordSubstitution)) {
                    CustomReportDataSource existingDs = (CustomReportDataSource) repository.getResource(null,
                            ds.getURIString());
                    if (existingDs != null) {
                        ds.getPropertyMap().put("password", existingDs.getPropertyMap().get("password"));
                    }
                }

                ReportDataSourceService service = customDataSourceFactory.createService(ds);
                if (setTestAvailable(context, service)
                        && ((CustomReportDataSourceService) service).testConnection()) {
                    connectionStatusMessageKey = RESOURCE_DATA_SOURCE_CONNECTION_STATE_PASSED;
                }
            } catch (Exception e) {
                logger.error("exception testing custom data source", e);
            }
        }
        context.getRequestScope().put(AJAX_RESPONSE_MODEL,
                messages.getMessage(connectionStatusMessageKey, null, LocaleContextHolder.getLocale()));
        return success();
    }

    public Event testJdbcDataSource(RequestContext context) throws Exception {
        TestJdbcConnectionResponseBuilder response = new TestJdbcConnectionResponseBuilder().failed();
        ReportDataSourceWrapper wrapper = (ReportDataSourceWrapper) getFormObject(context);
        JdbcReportDataSource ds = (JdbcReportDataSource) wrapper.getReportDataSource();

        Connection conn = null;
        try {
            jdbcDriverService.register(ds.getDriverClass());

            // On edit datasource we set the passwordSubstitution to the passwords form fields
            // If we get the substitution from UI then set the password from original datasource (if it exists)
            if (ds.getPassword().equals(passwordSubstitution)) {
                JdbcReportDataSource existingDs = (JdbcReportDataSource) repository.getResource(null,
                        ds.getURIString());
                if (existingDs != null) {
                    ds.setPassword(existingDs.getPassword());
                }
            }
            Properties properties = new Properties();
            properties.put("user", ds.getUsername());
            properties.put("password", ds.getPassword());
            Driver driver = DriverManager.getDriver(ds.getConnectionUrl());
            conn = driver.connect(ds.getConnectionUrl(), properties);
            if (conn != null) {
                response.passed();
            }
        } catch (Exception e) {
            logger.error("exception testing jdbc data source", e);
            response.failed(e);
        } finally {
            if (conn != null)
                conn.close();
        }

        context.getRequestScope().put(AJAX_RESPONSE_MODEL, response.buildJson());
        return success();
    }

    public void setInstanceProductTypeResolver(InstanceProductTypeResolver instanceProductTypeResolver) {
        this.instanceProductTypeResolver = instanceProductTypeResolver;
    }

    private class TestJdbcConnectionResponseBuilder {
        public static final String PASSED = "PASSED";
        public static final String FAILED = "FAILED";

        private Map<String, String> response = new HashMap<String, String>();

        public TestJdbcConnectionResponseBuilder passed() {
            response.put("status", PASSED);
            response.put("message", messages.getMessage(RESOURCE_DATA_SOURCE_CONNECTION_STATE_PASSED, null,
                    LocaleContextHolder.getLocale()));
            return this;
        }

        public TestJdbcConnectionResponseBuilder failed() {
            response.put("status", FAILED);
            response.put("message", messages.getMessage(RESOURCE_DATA_SOURCE_CONNECTION_STATE_FAILED, null,
                    LocaleContextHolder.getLocale()));
            return this;
        }

        public TestJdbcConnectionResponseBuilder failed(Exception e) {
            this.failed();
            if (e != null) {
                response.put("message", e.getMessage());

                StringWriter result = new StringWriter();
                PrintWriter trace = new PrintWriter(result);
                e.printStackTrace(trace);

                response.put("details", result.toString());
            }
            return this;
        }

        public String buildJson() throws Exception {
            ObjectMapper mapper = new ObjectMapper();

            StringWriter result = new StringWriter();
            mapper.writeValue(result, response);
            return result.toString();
        }
    }

    public Event testAwsDataSource(RequestContext context) throws Exception {
        TestJdbcConnectionResponseBuilder response = new TestJdbcConnectionResponseBuilder().failed();
        ReportDataSourceWrapper wrapper = (ReportDataSourceWrapper) getFormObject(context);
        AwsReportDataSource ds = (AwsReportDataSource) wrapper.getReportDataSource();
        Connection conn = null;
        try {
            jdbcDriverService.register(ds.getDriverClass());

            // On edit datasource we set the passwordSubstitution to the passwords form fields
            // If we get the substitution from UI then set the password from original datasource (if it exists)
            if (ds.getPassword().equals(passwordSubstitution)) {
                AwsReportDataSource existingDs = (AwsReportDataSource) repository.getResource(null,
                        ds.getURIString());
                if (existingDs != null) {
                    ds.setPassword(existingDs.getPassword());
                }
            }
            if (ds.getAWSSecretKey().equals(passwordSubstitution)) {
                AwsReportDataSource existingDs = (AwsReportDataSource) repository.getResource(null,
                        ds.getURIString());
                if (existingDs != null) {
                    ds.setAWSSecretKey(existingDs.getAWSSecretKey());
                }
            }

            awsDataSourceRecovery.createAwsDSSecurityGroup(ds);
            conn = DriverManager.getConnection(ds.getConnectionUrl(), ds.getUsername(), ds.getPassword());
            if (conn != null) {
                response.passed();
            }
        } catch (Exception e) {
            logger.error("exception testing AWS data source", e);

            Throwable throwable = ExceptionUtils.getRootCause(e);
            if (throwable instanceof ConnectException || throwable instanceof SocketTimeoutException
                    || (e instanceof SQLException
                            && ((SQLException) e).getSQLState().startsWith(AwsDataSourceService.SQL_STATE_CLASS))) {
                response.failed(new JSException(messages.getMessage("aws.exception.datasource.recovery.timeout",
                        null, LocaleContextHolder.getLocale()), e));
            } else {
                response.failed(e);
            }
        } finally {
            if (conn != null)
                conn.close();
        }
        context.getRequestScope().put(AJAX_RESPONSE_MODEL, response.buildJson());
        return success();
    }

    public Event testJndiDataSource(RequestContext context) throws Exception {
        String connectionStatusMessageKey = RESOURCE_DATA_SOURCE_CONNECTION_STATE_FAILED;
        ReportDataSourceWrapper wrapper = (ReportDataSourceWrapper) getFormObject(context);
        JndiJdbcReportDataSource ds = (JndiJdbcReportDataSource) wrapper.getReportDataSource();
        Connection conn = null;
        try {
            Context ctx = new InitialContext();
            DataSource dataSource = (DataSource) ctx.lookup("java:comp/env/" + ds.getJndiName());
            conn = TibcoDriverManagerImpl.getInstance().unlockConnection(dataSource);
            if (conn != null) {
                connectionStatusMessageKey = RESOURCE_DATA_SOURCE_CONNECTION_STATE_PASSED;
            }
        } catch (Exception e) {
            logger.error("exception testing jndi data source", e);
        } finally {
            if (conn != null)
                conn.close();
        }

        context.getRequestScope().put(AJAX_RESPONSE_MODEL,
                messages.getMessage(connectionStatusMessageKey, null, LocaleContextHolder.getLocale()));
        return success();
    }

    public Event testBeanDataSource(RequestContext context) throws Exception {
        String connectionStatusMessageKey = RESOURCE_DATA_SOURCE_CONNECTION_STATE_FAILED;
        ReportDataSourceWrapper wrapper = (ReportDataSourceWrapper) getFormObject(context);
        BeanReportDataSource ds = (BeanReportDataSource) wrapper.getReportDataSource();

        try {
            Object bean = ctx.getBean(ds.getBeanName());

            if (bean == null) {
                // Return connection failed message - it's default behavior
            } else if (ds.getBeanMethod() == null) {
                // The bean had better be a ReportDataSourceService
                if (bean instanceof ReportDataSourceService) {
                    connectionStatusMessageKey = RESOURCE_DATA_SOURCE_CONNECTION_STATE_PASSED;
                }
            } else {
                // The method on this bean returns a ReportDataSourceService
                Method serviceMethod;
                try {
                    serviceMethod = bean.getClass().getMethod(ds.getBeanMethod(), null);
                    Object obj = serviceMethod.invoke(bean, null);
                    if (obj != null) {
                        connectionStatusMessageKey = RESOURCE_DATA_SOURCE_CONNECTION_STATE_PASSED;
                    }
                } catch (SecurityException e) {
                    logger.error("exception testing bean data source", e);
                }
            }
        } catch (Exception e) {
            logger.error("exception testing bean data source", e);
        }
        context.getRequestScope().put(AJAX_RESPONSE_MODEL,
                messages.getMessage(connectionStatusMessageKey, null, LocaleContextHolder.getLocale()));
        return success();
    }

    public Event validateDataSource(RequestContext context) throws Exception {
        Errors errors = getFormErrors(context);

        ReportDataSourceWrapper wrapper = (ReportDataSourceWrapper) getFormObject(context);

        getValidator().validate(wrapper, errors);

        List fieldErrors = errors.getFieldErrors();
        if (fieldErrors != null && !fieldErrors.isEmpty()) {
            FieldError error = (FieldError) fieldErrors.get(0);
            String field = error.getField();

            if ("source".equals(field) || "selectedUri".equals(field)) {
                return result("chooseSource");
            } else if ("type".equals(field)) {
                return result("chooseType");
            } else if (JasperServerConstImpl.getJDBCDatasourceType().equals(wrapper.getType())) {
                return result("jdbcPropsForm");
            } else if (JasperServerConstImpl.getJNDIDatasourceType().equals(wrapper.getType())) {
                return result("jndiPropsForm");
            } else if (JasperServerConstImpl.getBeanDatasourceType().equals(wrapper.getType())) {
                return result("beanPropsForm");
            } else if (JasperServerConstImpl.getVirtualDatasourceType().equals(wrapper.getType())) {
                return result("virtualPropsForm");
            } else {
                return result("customPropsForm");
            }
        }

        return success();
    }

    /**
     * Init sub-datasources JSON in formObject from VDS object.
     * Used when opening existing VDS for edit
     *
     * @param formObject
     */
    public void initSubDatasources(ReportDataSourceWrapper formObject) {
        ArrayNode subDsList = jsonMapper.createArrayNode();
        VirtualReportDataSource vds = (VirtualReportDataSource) formObject.getReportDataSource();
        for (Map.Entry<String, ResourceReference> dsMapping : vds.getDataSourceUriMap().entrySet()) {
            ObjectNode subDsNode = subDsList.addObject();
            subDsNode.put(SUB_DATASOURCE_ID_KEY, dsMapping.getKey());
            subDsNode.put(SUB_DATASOURCE_URI_KEY, dsMapping.getValue().getReferenceURI());
            Resource res = repository.getResource(null, dsMapping.getValue().getReferenceURI());
            subDsNode.put(SUB_DATASOURCE_NAME_KEY, res.getName());
        }
        String subDsJson = JSONUtil.toJSON(subDsList);
        formObject.getNamedProperties().put(SUB_DATASOURCES_JSON_KEY, subDsJson);
    }

    public void initDependentResourcesList(ReportDataSourceWrapper formObject) {
        if (!formObject.isEditMode()) {
            return;
        }
        List<ResourceLookup> depRes = repository.getDependentResources(null,
                formObject.getReportDataSource().getURIString(), null, 0, MAX_DEPENDENT_RESOURCES);
        String depResJson = JSONUtil.toJSON(depRes);
        formObject.getNamedProperties().put(DEPENDENT_RESOURCES_JSON_KEY, depResJson);
    }

    public Object createFormObject(RequestContext context)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        ReportDataSourceWrapper formObject = new ReportDataSourceWrapper();
        String resourceUri = context.getRequestParameters().get(DATASOURCEURI_PARAM);

        if (resourceUri != null && resourceUri.trim().length() != 0) {
            Resource resource = repository.getResource(null, resourceUri);

            if (resource == null) {
                throw new JSException("jsexception.could.not.find.resource.with.uri", new Object[] { resourceUri });
            }

            formObject.setMode(BaseDTO.MODE_STAND_ALONE_EDIT);
            ReportDataSource dataSource = (ReportDataSource) resource;
            formObject.setType(getDataSourceMappings().getIdForClass(dataSource.getClass()));
            if (JasperServerConstImpl.getJNDIDatasourceType()
                    .equals(getDataSourceMappings().getIdForClass(dataSource.getClass()))) {
                context.getFlowScope().put(TYPE, DATASOURCE_JNDI);
            } else if (JasperServerConstImpl.getBeanDatasourceType()
                    .equals(getDataSourceMappings().getIdForClass(dataSource.getClass()))) {
                context.getFlowScope().put(TYPE, DATASOURCE_BEAN);
            }
            formObject.setReportDataSource(dataSource);
            if (JasperServerConstImpl.getVirtualDatasourceType().equals(formObject.getType())) {
                initDependentResourcesList(formObject);
                initSubDatasources(formObject);
            }
        }
        if (formObject.getReportDataSource() == null) {
            String parentFolder = (String) context.getFlowScope().get(PARENT_FOLDER_ATTR);
            String type = context.getExternalContext().getRequestParameterMap().get(TYPE);

            if (parentFolder == null) {
                parentFolder = context.getRequestParameters().get(PARENT_FOLDER_URI);
                context.getFlowScope().put(PARENT_FOLDER_ATTR, parentFolder);
            }

            formObject.setMode(BaseDTO.MODE_STAND_ALONE_NEW);
            //   set default options for datasource type
            formObject.setSource(JasperServerConstImpl.getFieldChoiceLocal());

            ReportDataSource source;

            //If JRS are in EC2 Instance than AWS data source will be setup as default(else JDBC is default).
            if ((type == null && instanceProductTypeResolver.isEC2()) || DATASOURCE_AWS.equals(type)) {
                source = newReportDataSource(JasperServerConstImpl.getAwsDatasourceType());
                context.getFlowScope().put(TYPE, DATASOURCE_AWS);
                formObject.setType(JasperServerConstImpl.getAwsDatasourceType());
                if (awsEc2MetadataClient.isEc2Instance()) {
                    ((AwsReportDataSource) source).setAWSRegion(awsEc2MetadataClient.getEc2InstanceRegion());
                }
            } else if (type == null) {
                source = newReportDataSource(JasperServerConstImpl.getJDBCDatasourceType());
                formObject.setType(JasperServerConstImpl.getJDBCDatasourceType());
            } else if (type.equals(DATASOURCE_JDBC)) {
                source = newReportDataSource(JasperServerConstImpl.getJDBCDatasourceType());
                formObject.setType(JasperServerConstImpl.getJNDIDatasourceType());
            } else if (type.equals(DATASOURCE_JNDI)) {
                source = newReportDataSource(JasperServerConstImpl.getJNDIDatasourceType());
                formObject.setType(JasperServerConstImpl.getJNDIDatasourceType());
            } else if (type.equals(DATASOURCE_BEAN)) {
                source = newReportDataSource(JasperServerConstImpl.getBeanDatasourceType());
                formObject.setType(JasperServerConstImpl.getBeanDatasourceType());
            } else if (type.equals(DATASOURCE_VIRTUAL)) {
                source = newReportDataSource(JasperServerConstImpl.getVirtualDatasourceType());
                formObject.setType(JasperServerConstImpl.getVirtualDatasourceType());
            } else {
                source = newReportDataSource(type);
                formObject.setType(type);
            }
            source.setParentFolder(parentFolder);
            source.setVersion(Resource.VERSION_NEW);
            formObject.setReportDataSource(source);
        }

        if (formObject.getReportDataSource() instanceof CustomReportDataSource) {
            evaluateTestAvailability(context, (CustomReportDataSource) formObject.getReportDataSource());
        }

        return formObject;
    }

    private void evaluateTestAvailability(RequestContext context, CustomReportDataSource reportDataSource)
            throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        if (logger.isDebugEnabled()) {
            logger.debug("Evaluating service class: " + reportDataSource.getServiceClass());
        }
        try {
            Class<?> serviceClass = Class.forName(reportDataSource.getServiceClass());
            // Instance created to evaluate if it implements the test interface
            Object newServiceClass = serviceClass.newInstance();
            setTestAvailable(context, newServiceClass);
            newServiceClass = null;
        } catch (ClassNotFoundException cnf) {
            setTestAvailable(context, cnf);
        }
    }

    private boolean setTestAvailable(RequestContext context, Object instance) {
        boolean isAvailable;
        context.getRequestScope().put("testAvailable",
                isAvailable = (instance instanceof CustomReportDataSourceService));
        return isAvailable;
    }

    private ReportDataSource newReportDataSource(String dsType) {
        if (customDataSourceFactory != null) {
            CustomDataSourceDefinition cdsd = customDataSourceFactory.getDefinitionByName(dsType);
            if (cdsd != null) {
                return customDataSourceFactory.createDataSource(dsType);
            }
        }

        return (ReportDataSource) getDataSourceMappings().newResource(null, dsType);
    }

    private ReportDataSource newReportDataSource(String dsType, ReportDataSource oldDS) {
        ReportDataSource newDS = newReportDataSource(dsType);
        newDS.setParentFolder(oldDS.getParentFolder());
        newDS.setName(oldDS.getName());
        newDS.setLabel(oldDS.getLabel());
        newDS.setDescription(oldDS.getDescription());
        newDS.setVersion(oldDS.getVersion());

        if (oldDS instanceof JdbcReportDataSource) {
            JdbcReportDataSource oldJdbc = (JdbcReportDataSource) oldDS;
            JdbcReportDataSource newJdbc = (JdbcReportDataSource) newDS;
            newJdbc.setDriverClass(oldJdbc.getDriverClass());
            newJdbc.setConnectionUrl(oldJdbc.getConnectionUrl());
            newJdbc.setPassword(oldJdbc.getPassword());
            newJdbc.setTimezone(oldJdbc.getTimezone());
            newJdbc.setUsername(oldJdbc.getUsername());
        } else if (oldDS instanceof JndiJdbcReportDataSource) {
            JndiJdbcReportDataSource oldJndi = (JndiJdbcReportDataSource) oldDS;
            JndiJdbcReportDataSource newJndi = (JndiJdbcReportDataSource) newDS;
            newJndi.setJndiName(oldJndi.getJndiName());
            newJndi.setTimezone(oldJndi.getTimezone());
        } else if (oldDS instanceof VirtualReportDataSource) {
            VirtualReportDataSource oldVirtual = (VirtualReportDataSource) oldDS;
            VirtualReportDataSource newVirtual = (VirtualReportDataSource) newDS;
            newVirtual
                    .setDataSourceUriMap(new HashMap<String, ResourceReference>(oldVirtual.getDataSourceUriMap()));
            newVirtual.setTimezone(oldVirtual.getTimezone());
        } else if (oldDS instanceof BeanReportDataSource) {
            BeanReportDataSource oldBean = (BeanReportDataSource) oldDS;
            BeanReportDataSource newBean = (BeanReportDataSource) newDS;
            newBean.setBeanMethod(oldBean.getBeanMethod());
            newBean.setBeanName(oldBean.getBeanName());
        }
        return newDS;
    }

    public Event uploadJDBCDrivers(RequestContext context) throws Exception {
        String driverClassName = context.getRequestParameters().get("className");
        if (driverClassName == null || driverClassName.isEmpty()) {
            throw new Exception("Class name is empty.");
        }

        Map<String, byte[]> driverFilesData = new HashMap<String, byte[]>();

        int i = 0;
        MultipartFile multipartFile = context.getRequestParameters().getMultipartFile(UPLOAD_DRIVER_PREFIX + i);
        while (multipartFile != null) {
            driverFilesData.put(multipartFile.getOriginalFilename(), multipartFile.getBytes());

            i++;
            multipartFile = context.getRequestParameters().getMultipartFile(UPLOAD_DRIVER_PREFIX + i);
        }

        String errorMessage = "";
        try {
            jdbcDriverService.setDriver(driverClassName, driverFilesData);
        } catch (NoClassDefFoundError e) {
            errorMessage = messages.getMessage("resource.dataSource.jdbc.classNotFound",
                    new String[] { e.getMessage() }, LocaleContextHolder.getLocale());
            logger.error(errorMessage);
        } catch (ClassNotFoundException e) {
            errorMessage = messages.getMessage("resource.dataSource.jdbc.classNotFound",
                    new String[] { e.getMessage() }, LocaleContextHolder.getLocale());
            logger.error(errorMessage);
        } catch (Exception e) {
            errorMessage = e.getMessage();
            logger.error(errorMessage);
        }

        Map<String, Map<String, Object>> availableDrivers = getAvailableJdbcDrivers();

        JSONObject json = new JSONObject();
        json.put("result", isEmpty(errorMessage));
        json.put("errorMessage", errorMessage);
        json.put(JDBC_DRIVERS_JSON_KEY, JSONUtil.toJSON(availableDrivers));
        context.getRequestScope().put(AJAX_RESPONSE_MODEL, json.toString());
        context.getFlowScope().put(JDBC_DRIVERS_JSON_KEY, JSONUtil.toJSON(availableDrivers));

        return success();
    }

    public void setMessages(MessageSource messages) {
        this.messages = messages;
    }

    /**
    * @return Returns the dataSourceMappings.
    */
    public ResourceFactory getDataSourceMappings() {
        return dataSourceMappings;
    }

    /**
     * @param dataSourceMappings The dataSourceMappings to set.
     */
    public void setDataSourceMappings(ResourceFactory dataSourceMappings) {
        this.dataSourceMappings = dataSourceMappings;
    }

    public JdkTimeZonesList getTimezones() {
        return timezones;
    }

    public void setTimezones(JdkTimeZonesList timezones) {
        this.timezones = timezones;
    }

    public List<String> getAwsRegions() {
        return awsRegions;
    }

    public void setAwsRegions(List<String> awsRegions) {
        this.awsRegions = awsRegions;
    }

    public RepositoryService getRepository() {
        return repository;
    }

    public void setRepository(RepositoryService repository) {
        this.repository = repository;
    }

    public void setConfiguration(ConfigurationBean configuration) {
        this.configuration = configuration;
    }

    public CustomReportDataSourceServiceFactory getCustomDataSourceFactory() {
        return customDataSourceFactory;
    }

    public void setCustomDataSourceFactory(CustomReportDataSourceServiceFactory customDataSourceFactory) {
        this.customDataSourceFactory = customDataSourceFactory;
    }

    public String getQueryLanguageFlowAttribute() {
        return queryLanguageFlowAttribute;
    }

    public void setQueryLanguageFlowAttribute(String queryLanguageFlowAttribute) {
        this.queryLanguageFlowAttribute = queryLanguageFlowAttribute;
    }

    protected String getQueryLanguage(RequestContext context) {
        return context.getFlowScope().getString(getQueryLanguageFlowAttribute());
    }

    public EngineService getEngine() {
        return engine;
    }

    public void setEngine(EngineService engine) {
        this.engine = engine;
    }

    public void setJdbcDriverService(JdbcDriverService jdbcDriverService) {
        this.jdbcDriverService = jdbcDriverService;
    }

    public void setAwsDataSourceRecovery(AwsDataSourceRecovery awsDataSourceRecovery) {
        this.awsDataSourceRecovery = awsDataSourceRecovery;
    }

    public void setAwsProperties(AwsProperties awsProperties) {
        this.awsProperties = awsProperties;
    }

    public void setAwsEc2MetadataClient(AwsEc2MetadataClient awsEc2MetadataClient) {
        this.awsEc2MetadataClient = awsEc2MetadataClient;
    }

    public void setJdbcConnectionMap(Map<String, Map<String, Object>> jdbcConnectionMap) {
        this.jdbcConnectionMap = jdbcConnectionMap;
    }

    public void setDynamicUrlPartPattern(String dynamicUrlPartPattern) {
        this.dynamicUrlPartPattern = dynamicUrlPartPattern;
    }

    public void setValidationPatternsMap(Map<String, String> validationPatternsMap) {
        this.validationPatternsMap = validationPatternsMap;
    }
}