com.mirth.connect.connectors.jdbc.JdbcConnector.java Source code

Java tutorial

Introduction

Here is the source code for com.mirth.connect.connectors.jdbc.JdbcConnector.java

Source

/*
 * Copyright (c) SymphonySoft Limited. All rights reserved.
 * http://www.symphonysoft.com
 *
 * The software in this package is published under the terms of the BSD
 * style license a copy of which has been included with this distribution in
 * the LICENSE-MULE.txt file.
 */

package com.mirth.connect.connectors.jdbc;

import java.sql.Connection;
import java.util.Map;

import javax.sql.DataSource;

import org.apache.commons.dbcp.BasicDataSource;
import org.mozilla.javascript.Context;
import org.mozilla.javascript.Script;
import org.mule.providers.AbstractServiceEnabledConnector;
import org.mule.providers.TemplateValueReplacer;
import org.mule.transaction.TransactionCoordination;
import org.mule.umo.TransactionException;
import org.mule.umo.UMOComponent;
import org.mule.umo.UMOTransaction;
import org.mule.umo.endpoint.UMOEndpoint;
import org.mule.umo.endpoint.UMOEndpointURI;
import org.mule.umo.lifecycle.InitialisationException;
import org.mule.umo.provider.UMOMessageReceiver;

import com.mirth.connect.model.CodeTemplate;
import com.mirth.connect.model.MessageObject;
import com.mirth.connect.model.CodeTemplate.CodeSnippetType;
import com.mirth.connect.server.controllers.ControllerException;
import com.mirth.connect.server.controllers.ControllerFactory;
import com.mirth.connect.server.controllers.ScriptController;
import com.mirth.connect.server.util.CompiledScriptCache;

public class JdbcConnector extends AbstractServiceEnabledConnector {
    private CompiledScriptCache compiledScriptCache = CompiledScriptCache.getInstance();
    private ScriptController scriptController = ControllerFactory.getFactory().createScriptController();
    private TemplateValueReplacer replacer = new TemplateValueReplacer();
    public static final String PROPERTY_POLLING_FREQUENCY = "pollingFrequency";
    public static final String PROPERTY_POLLING_TYPE = "pollingType";
    public static final String PROPERTY_POLLING_TIME = "pollingTime";
    public static final String POLLING_TYPE_INTERVAL = "interval";
    public static final String POLLING_TYPE_TIME = "time";
    private String pollingType = POLLING_TYPE_INTERVAL;
    private String pollingTime = "12:00 AM";
    private long pollingFrequency = 5000;
    private DataSource dataSource;
    private String driver;
    private String URL;
    private String username;
    private String password;
    private boolean processResultsInOrder = true;
    private boolean useAck;
    private Map queries;
    private boolean useScript;
    private String scriptId;
    private String ackScriptId;
    private String channelId;

    public String getChannelId() {
        return this.channelId;
    }

    public void setChannelId(String channelId) {
        this.channelId = channelId;
    }

    public String getProtocol() {
        return "jdbc";
    }

    public DataSource getDataSource() {
        return this.dataSource;
    }

    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public String getdriver() {
        return this.driver;
    }

    public void setDriver(String driver) {
        this.driver = driver;
    }

    public String getURL() {
        return URL;
    }

    public void setURL(String url) {
        this.URL = url;
    }

    public boolean isUseScript() {
        return this.useScript;
    }

    public void setUseScript(boolean useScript) {
        this.useScript = useScript;
    }

    public String getPassword() {
        return this.password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getUsername() {
        return this.username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public long getPollingFrequency() {
        return pollingFrequency;
    }

    public void setPollingFrequency(long pollingFrequency) {
        this.pollingFrequency = pollingFrequency;
    }

    public Map getQueries() {
        return queries;
    }

    public void setQueries(Map queries) {
        this.queries = queries;
    }

    public String getScriptId() {
        return this.scriptId;
    }

    public void setScriptId(String scriptId) {
        this.scriptId = scriptId;
    }

    public boolean isProcessResultsInOrder() {
        return processResultsInOrder;
    }

    public void setProcessResultsInOrder(boolean processResultsInOrder) {
        this.processResultsInOrder = processResultsInOrder;
    }

    public boolean isUseAck() {
        return useAck;
    }

    public void setUseAck(boolean useAck) {
        this.useAck = useAck;
    }

    public String getAckScriptId() {
        return ackScriptId;
    }

    public void setAckScriptId(String ackScriptId) {
        this.ackScriptId = ackScriptId;
    }

    public String getPollingTime() {
        return pollingTime;
    }

    public void setPollingTime(String pollingTime) {
        this.pollingTime = pollingTime;
    }

    public String getPollingType() {
        return pollingType;
    }

    public void setPollingType(String pollingType) {
        this.pollingType = pollingType;
    }

    public UMOMessageReceiver createReceiver(UMOComponent component, UMOEndpoint endpoint) throws Exception {
        String[] params = {};

        if (!useScript) {
            params = getReadAndAckStatements(endpoint.getEndpointURI(), endpoint);
        }

        long polling = pollingFrequency;
        Map props = endpoint.getProperties();

        if (props != null) {
            // Override properties on the endpoint for the specific endpoint
            String tempPolling = (String) props.get(PROPERTY_POLLING_FREQUENCY);

            if (tempPolling != null) {
                polling = Long.parseLong(tempPolling);
            }

            String pollingType = (String) props.get(PROPERTY_POLLING_TYPE);

            if (pollingType != null) {
                setPollingType(pollingType);
            }

            String pollingTime = (String) props.get(PROPERTY_POLLING_TIME);

            if (pollingTime != null) {
                setPollingTime(pollingTime);
            }
        }

        if (polling <= 0) {
            polling = 1000;
        }

        logger.debug("set polling frequency to: " + polling);
        return getServiceDescriptor().createMessageReceiver(this, component, endpoint, params);
    }

    public String[] getReadAndAckStatements(UMOEndpointURI endpointUri, UMOEndpoint endpoint) {
        String str = null;

        // find read statement
        String readStmt = null;

        if ((str = endpointUri.getParams().getProperty("sql")) != null) {
            readStmt = str;
        } else {
            readStmt = endpointUri.getAddress();
        }

        // Find ack statement
        String ackStmt = null;
        if ((str = endpointUri.getParams().getProperty("ack")) != null) {
            ackStmt = str;
            if ((str = getQuery(endpoint, ackStmt)) != null) {
                ackStmt = str;
            }
        } else {
            ackStmt = "ack";
            if ((str = getQuery(endpoint, ackStmt)) != null) {
                ackStmt = str;
            } else {
                ackStmt = null;
            }
        }

        // Translate both using queries map
        if ((str = getQuery(endpoint, readStmt)) != null) {
            readStmt = str;
        }

        if (readStmt == null) {
            throw new IllegalArgumentException("Read statement should not be NULL");
        }

        if (!readStmt.toLowerCase().startsWith("select")) {
            throw new IllegalArgumentException("Read statement should be a SELECT sql statement");
        }

        if (ackStmt != null) {
            if (!ackStmt.toLowerCase().startsWith("insert") && !ackStmt.toLowerCase().startsWith("update")
                    && !ackStmt.toLowerCase().startsWith("delete")) {
                throw new IllegalArgumentException(
                        "Ack statement should be an INSERT, UPDATE, or DELETE SQL statement");
            }
        }

        return new String[] { readStmt, ackStmt };
    }

    public String getQuery(UMOEndpoint endpoint, String stmt) {
        Object query = null;

        if ((endpoint != null) && (endpoint.getProperties() != null)) {
            Object queries = getQueries();

            if (queries instanceof Map) {
                query = ((Map) queries).get(stmt);
            }
        }

        if (query == null) {
            if (this.queries != null) {
                query = this.queries.get(stmt);
            }
        }

        return query == null ? null : query.toString();
    }

    /*
     * This method gets called when the JDBC connector is initialized. It
     * compiles the JavaScript and adds it to the cache
     */
    protected synchronized void initFromServiceDescriptor() throws InitialisationException {
        super.initFromServiceDescriptor();

        setCreateMultipleTransactedReceivers(false);

        Context context = Context.enter();

        try {
            if (scriptId != null) {
                String databaseScript = scriptController.getScript(channelId, scriptId);

                if (databaseScript != null) {
                    String generatedDatabaseScript = generateDatabaseScript(databaseScript, false);
                    logger.debug("compiling database script");
                    Script compiledDatabaseScript = context.compileString(generatedDatabaseScript, scriptId, 1,
                            null);
                    compiledScriptCache.putCompiledScript(scriptId, compiledDatabaseScript,
                            generatedDatabaseScript);
                }
            }

            if (ackScriptId != null) {
                String ackScript = scriptController.getScript(channelId, ackScriptId);

                if (ackScript != null) {
                    String generatedDatabaseScript = generateDatabaseScript(ackScript, true);
                    logger.debug("compiling database ack script");
                    Script compiledDatabaseScript = context.compileString(generatedDatabaseScript, ackScriptId, 1,
                            null);
                    compiledScriptCache.putCompiledScript(ackScriptId, compiledDatabaseScript,
                            generatedDatabaseScript);
                }
            }
        } catch (Exception e) {
            throw new InitialisationException(e, this);
        } finally {
            Context.exit();
        }
    }

    // Generates the JavaScript based on the script which the user enters
    private String generateDatabaseScript(String databaseScript, boolean ack) {
        logger.debug("generating database script");
        StringBuilder script = new StringBuilder();
        script.append("importPackage(Packages.com.mirth.connect.server.util);\n");

        // start function
        script.append("function $(string) { ");

        if (ack) {
            script.append("if (resultMap.containsKey(string)) { return resultMap.get(string) }\n else ");
        }

        script.append("if (connectorMap.containsKey(string)) { return connectorMap.get(string); }\n");
        script.append("else if (channelMap.containsKey(string)) { return channelMap.get(string); }\n");
        script.append("else if (globalChannelMap.containsKey(string)) { return globalChannelMap.get(string); }\n");
        script.append("else if (globalMap.containsKey(string)) { return globalMap.get(string); }\n");
        script.append("else { return ''; } }");
        // end function

        script.append("function $g(key, value) {");
        script.append("if (arguments.length == 1) { return globalMap.get(key); }");
        script.append("else if (arguments.length == 2) { globalMap.put(key, value); } }");

        script.append("function $gc(key, value) {");
        script.append("if (arguments.length == 1) { return globalChannelMap.get(key); }");
        script.append("else if (arguments.length == 2) { globalChannelMap.put(key, value); } }");

        script.append("function $c(key, value) {");
        script.append("if (arguments.length == 1) { return channelMap.get(key); }");
        script.append("else if (arguments.length == 2) { channelMap.put(key, value); } }");

        script.append("function $co(key, value) {");
        script.append("if (arguments.length == 1) { return connectorMap.get(key); }");
        script.append("else if (arguments.length == 2) { connectorMap.put(key, value); } }");

        script.append("function $r(key, value) {");
        script.append("if (arguments.length == 1) {return responseMap.get(key); }");
        script.append("else if (arguments.length == 2) { responseMap.put(key, value); } }");

        try {
            for (CodeTemplate template : ControllerFactory.getFactory().createCodeTemplateController()
                    .getCodeTemplate(null)) {
                if (template.getType() == CodeSnippetType.FUNCTION) {
                    script.append(template.getCode());
                }
            }
        } catch (ControllerException e) {
            logger.error("Could load code templates.", e);
        }

        script.append("function doDatabaseScript() {");
        script.append(databaseScript + "\n");
        script.append("}\n");
        script.append("doDatabaseScript();\n");
        return script.toString();
    }

    public Connection getConnection(MessageObject messageObject) throws Exception {
        UMOTransaction tx = TransactionCoordination.getInstance().getTransaction();

        if (tx != null) {
            if (tx.hasResource(URL)) {
                logger.debug("Retrieving connection from current transaction");
                return (Connection) tx.getResource(URL);
            }
        }

        Connection connection = null;

        /*
         * The database source is NULL because the username, password, or URL
         * have replaceable values
         */
        if (dataSource == null) {
            if (messageObject != null) {
                username = replacer.replaceValues(username, messageObject);
                password = replacer.replaceValues(password, messageObject);
                URL = replacer.replaceValues(URL, messageObject);
            } else {
                username = replacer.replaceValues(username, channelId);
                password = replacer.replaceValues(password, channelId);
                URL = replacer.replaceValues(URL, channelId);
            }

            setupDataSource(URL, driver, username, password);
        }

        logger.debug("Retrieving new connection from datasource: " + URL + " using driver: " + driver);
        connection = dataSource.getConnection();

        if (tx != null) {
            logger.debug("Binding connection to current transaction");
            try {
                tx.bindResource(URL, connection);
            } catch (TransactionException e) {
                throw new RuntimeException("Could not bind connection to current transaction", e);
            }
        }

        return connection;
    }

    private void setupDataSource(String address, String driver, String username, String password) {
        BasicDataSource basicDataSource = new BasicDataSource();
        basicDataSource.setDriverClassName(driver);
        basicDataSource.setUsername(username);
        basicDataSource.setPassword(password);
        basicDataSource.setUrl(address);
        dataSource = basicDataSource;
    }

    private void shutdownDataSource() throws Exception {
        BasicDataSource bds = (BasicDataSource) dataSource;
        bds.close();
    }

    @Override
    public void doConnect() throws Exception {
        super.doConnect();

        // if we don't plan on replacing the values, setup the datasource
        if (!TemplateValueReplacer.hasReplaceableValues(URL)
                && !TemplateValueReplacer.hasReplaceableValues(username)
                && !TemplateValueReplacer.hasReplaceableValues(password)) {
            setupDataSource(URL, driver, username, password);
        }
    }

    @Override
    public void doDisconnect() throws Exception {
        super.doDisconnect();
        shutdownDataSource();
    }

}