com.yukthi.persistence.NativeQueryFactory.java Source code

Java tutorial

Introduction

Here is the source code for com.yukthi.persistence.NativeQueryFactory.java

Source

/*
 * The MIT License (MIT)
 * Copyright (c) 2016 "Yukthi Techsoft Pvt. Ltd." (http://yukthi-tech.co.in)
    
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */

package com.yukthi.persistence;

import java.io.FileInputStream;
import java.io.InputStream;
import java.io.StringWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import com.yukthi.ccg.xml.XMLBeanParser;
import com.yukthi.persistence.freemarker.TrimDirective;
import com.yukthi.utils.exceptions.InvalidArgumentException;
import com.yukthi.utils.exceptions.InvalidStateException;

import freemarker.template.Configuration;
import freemarker.template.Template;

/**
 * Factory of native queries. Loads the input query resources into a map and provides
 * factory method to fetch native queries based on name and context object.
 * 
 * @author akiran
 */
public class NativeQueryFactory {
    private static Logger logger = LogManager.getLogger(NativeQueryFactory.class);

    /**
     * Pattern for query parameters in queries
     */
    private static final Pattern QUERY_PARAM_PATTERN = Pattern.compile("\\?\\{([\\w\\.]+)\\}");

    /**
     * Map to maintain raw queries with expressions
     */
    private Map<String, String> queryMap = new HashMap<>();

    /**
     * Map to maintain query templates
     */
    private Map<String, Template> templateMap = new HashMap<>();

    /**
     * Configuration for free marker
     */
    private Configuration configuration = new Configuration();

    public NativeQueryFactory() {
        configuration.setSharedVariable("trim", new TrimDirective());
    }

    /**
     * Loads queries from specified input stream
     * @param is Input stream to be loaded
     */
    public void addResourceStream(InputStream is) {
        XMLBeanParser.parse(is, this);
    }

    /**
     * Loads queries from specified resource file. Specified resource path should be accessible in classpath.
     * @param resource Resource to be loaded
     */
    public void addResource(String resource) {
        try {
            logger.debug("Loading native query resource: {}", resource);
            InputStream is = NativeQueryFactory.class.getResourceAsStream(resource);
            addResourceStream(is);
            is.close();
        } catch (Exception ex) {
            throw new InvalidArgumentException(ex, "An error occurred while loading resource file - {}", resource);
        }
    }

    /**
     * Adds list of resources to this factory.
     * @param resources Resource files to add.
     */
    public void setResources(List<String> resources) {
        for (String resource : resources) {
            addResource(resource);
        }
    }

    /**
     * Loads queries from specified file
     * @param file File to be loaded
     */
    public void addFile(String file) {
        try {
            logger.debug("Loading native query file: {}", file);
            InputStream is = new FileInputStream(file);
            addResourceStream(is);
            is.close();
        } catch (Exception ex) {
            throw new InvalidArgumentException(ex, "An error occurred while loading file - {}", file);
        }
    }

    /**
     * Adds list of specified files to this factory.
     * @param files Files to add.
     */
    public void setFiles(List<String> files) {
        for (String file : files) {
            addFile(file);
        }
    }

    /**
     * Adder method to add raw query string with free marker template and expressions
     * @param name Name of the query
     * @param query Raw query string
     */
    public void addQuery(String name, String query) {
        queryMap.put(name, query);
    }

    /**
     * Fetches the query template with specified "name" and builds the query using specified "context".
     * If the query uses "param" directive, corresponding values will be collected into "paramValues".
     * 
     * @param name Name of the query to be fetched
     * @param paramValues Collected param values that needs to be passed to query as prepared statement params
     * @param context Context to be used to parse template into query
     * @return Built query
     */
    public String buildQuery(String name, List<Object> paramValues, Object context) {
        Template template = templateMap.get(name);

        //if template is not found build it from raw query
        if (template == null) {
            String rawQuery = queryMap.get(name);

            //if no query found with specified name
            if (rawQuery == null) {
                throw new InvalidArgumentException("No query found with specified name - {}", name);
            }

            //build the free marker template from raw query
            try {
                template = new Template(name, rawQuery, configuration);
                templateMap.put(name, template);
            } catch (Exception ex) {
                throw new InvalidStateException(ex, "An error occurred while loading query template - {}", name);
            }
        }

        try {
            //process the template into query
            StringWriter writer = new StringWriter();
            template.process(context, writer);

            writer.flush();

            //build the final query (replacing the param expressions)
            String query = writer.toString();
            StringBuffer finalQuery = new StringBuffer();
            Matcher matcher = QUERY_PARAM_PATTERN.matcher(query);
            String property = null;

            while (matcher.find()) {
                property = matcher.group(1);
                matcher.appendReplacement(finalQuery, "?");

                paramValues.add(PropertyUtils.getProperty(context, property));
            }

            matcher.appendTail(finalQuery);

            return finalQuery.toString();
        } catch (Exception ex) {
            throw new InvalidStateException(ex, "An exception occurred while building query: " + name);
        }
    }
}