Java tutorial
/*************************************************************** * This file is part of the [fleXive](R) framework. * * Copyright (c) 1999-2014 * UCS - unique computing solutions gmbh (http://www.ucs.at) * All rights reserved * * The [fleXive](R) project is free software; you can redistribute * it and/or modify it under the terms of the GNU Lesser General Public * License version 2.1 or higher as published by the Free Software Foundation. * * The GNU Lesser General Public License can be found at * http://www.gnu.org/licenses/lgpl.html. * A copy is found in the textfile LGPL.txt and important notices to the * license from the author are found in LICENSE.txt distributed with * these libraries. * * This library 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 General Public License for more details. * * For further information about UCS - unique computing solutions gmbh, * please see the company website: http://www.ucs.at * * For further information about [fleXive](R), please see the * project website: http://www.flexive.org * * * This copyright notice MUST APPEAR in all copies of the file! ***************************************************************/ package com.flexive.ejb.beans.configuration; import com.flexive.core.Database; import com.flexive.core.DatabaseConst; import com.flexive.core.flatstorage.FxFlatStorageInfo; import com.flexive.core.flatstorage.FxFlatStorageManager; import com.flexive.core.storage.ContentStorage; import com.flexive.core.storage.StorageManager; import com.flexive.ejb.beans.EJBUtils; import com.flexive.shared.FxContext; import com.flexive.shared.FxLanguage; import com.flexive.shared.FxSharedUtils; import com.flexive.shared.configuration.DivisionData; import com.flexive.shared.configuration.ParameterScope; import com.flexive.shared.configuration.SystemParameters; import com.flexive.shared.exceptions.FxApplicationException; import com.flexive.shared.exceptions.FxDbException; import com.flexive.shared.exceptions.FxInvalidParameterException; import com.flexive.shared.exceptions.FxNoAccessException; import com.flexive.shared.impex.FxDivisionExportInfo; import com.flexive.shared.interfaces.DivisionConfigurationEngine; import com.flexive.shared.interfaces.DivisionConfigurationEngineLocal; import com.flexive.shared.structure.TypeStorageMode; import com.flexive.shared.value.FxString; import com.google.common.collect.Sets; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import javax.annotation.Resource; import javax.ejb.*; import java.io.*; import java.sql.*; import java.util.*; import java.util.zip.ZipFile; import static com.flexive.core.DatabaseConst.TBL_CONFIG_DIVISION; import static com.flexive.core.DatabaseConst.TBL_RESOURCES; /** * Division configuration implementation. * * @author Daniel Lichtenberger (daniel.lichtenberger@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at) */ @TransactionManagement(TransactionManagementType.CONTAINER) @TransactionAttribute(TransactionAttributeType.REQUIRED) @Stateless(name = "DivisionConfigurationEngine", mappedName = "DivisionConfigurationEngine") public class DivisionConfigurationEngineBean extends GenericConfigurationImpl implements DivisionConfigurationEngine, DivisionConfigurationEngineLocal { private static final Log LOG = LogFactory.getLog(DivisionConfigurationEngineBean.class); /** * Division config cache root. */ private static final String CACHE_ROOT = "/divisionConfig"; @Resource javax.ejb.SessionContext ctx; @EJB DatabasePatchUtilsLocal patchUtils; @Override protected ParameterScope getDefaultScope() { return ParameterScope.DIVISION; } /** * {@inheritDoc} */ @Override protected Connection getConnection() throws SQLException { return Database.getDbConnection(); } /** * {@inheritDoc} */ @Override protected PreparedStatement getInsertStatement(Connection conn, String path, String key, String value, String className) throws SQLException, FxNoAccessException { if (!FxContext.getUserTicket().isGlobalSupervisor()) { throw new FxNoAccessException("ex.configuration.update.perm.division"); } String sql = "INSERT INTO " + TBL_CONFIG_DIVISION + " (cpath, ckey, cvalue, className) VALUES (?, ?, ?, ?)"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, path); stmt.setString(2, key); StorageManager.setBigString(stmt, 3, value); stmt.setString(4, className); return stmt; } /** * {@inheritDoc} */ @Override protected PreparedStatement getSelectStatement(Connection conn, String path, String key) throws SQLException { final String sql = "SELECT cvalue FROM " + TBL_CONFIG_DIVISION + " WHERE cpath=? AND ckey=?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, path); stmt.setString(2, key); return stmt; } /** * {@inheritDoc} */ @Override protected PreparedStatement getSelectStatement(Connection conn, String path) throws SQLException { final String sql = "SELECT ckey, cvalue FROM " + TBL_CONFIG_DIVISION + " WHERE cpath=?"; PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, path); return stmt; } /** * {@inheritDoc} */ @Override protected PreparedStatement getSelectStatement(Connection conn) throws SQLException { final String sql = "SELECT cpath, ckey, cvalue, className FROM " + TBL_CONFIG_DIVISION; return conn.prepareStatement(sql); } /** * {@inheritDoc} */ @Override protected PreparedStatement getUpdateStatement(Connection conn, String path, String key, String value, String className) throws SQLException, FxNoAccessException { if (!FxContext.getUserTicket().isGlobalSupervisor()) { throw new FxNoAccessException("ex.configuration.update.perm.division"); } final String sql = "UPDATE " + TBL_CONFIG_DIVISION + " SET cvalue=?, className=? WHERE cpath=? AND ckey=?"; PreparedStatement stmt = conn.prepareStatement(sql); StorageManager.setBigString(stmt, 1, value); stmt.setString(2, className); stmt.setString(3, path); stmt.setString(4, key); return stmt; } /** * {@inheritDoc} */ @Override protected PreparedStatement getDeleteStatement(Connection conn, String path, String key) throws SQLException, FxNoAccessException { if (!FxContext.getUserTicket().isGlobalSupervisor()) { throw new FxNoAccessException("ex.configuration.delete.perm.division"); } final String sql = "DELETE FROM " + TBL_CONFIG_DIVISION + " WHERE cpath=? " + (key != null ? " AND ckey=?" : ""); PreparedStatement stmt = conn.prepareStatement(sql); stmt.setString(1, path); if (key != null) { stmt.setString(2, key); } return stmt; } /** * {@inheritDoc} */ @Override protected String getCachePath(String path) { return CACHE_ROOT + path; } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.REQUIRED) public void installBinary(long binaryId, String resourceName) throws FxApplicationException { Connection con = null; try { ContentStorage storage = StorageManager.getContentStorage(TypeStorageMode.Hierarchical); String binaryName = resourceName; String subdir = ""; if (binaryName.indexOf('/') > 0) { binaryName = binaryName.substring(binaryName.lastIndexOf('/') + 1); subdir = resourceName.substring(0, resourceName.lastIndexOf('/') + 1); } ClassLoader cl = Thread.currentThread().getContextClassLoader(); con = getConnection(); long length = 0; String[] files = FxSharedUtils .loadFromInputStream( cl.getResourceAsStream("fxresources/binaries/" + subdir + "resourceindex.flexive"), -1) .replaceAll("\r", "").split("\n"); for (String file : files) { if (file.startsWith(binaryName + "|")) { length = Long.parseLong(file.split("\\|")[1]); break; } } if (length == 0) throw new FxApplicationException("ex.scripting.load.resource.failed", resourceName); storage.storeBinary(con, binaryId, 1, 1, binaryName, length, cl.getResourceAsStream("fxresources/binaries/" + resourceName)); } catch (SQLException e) { throw new FxDbException(LOG, e, "ex.db.sqlError", e.getMessage()); } finally { try { if (con != null) con.close(); } catch (SQLException e) { //ignore } } } private static class SQLPatchScript { long from; long to; String script; SQLPatchScript(long from, long to, String script) { this.from = from; this.to = to; this.script = script; } } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void patchDatabase() throws FxApplicationException { final long oldVersion = get(SystemParameters.DB_VERSION); final long patchedVersion = performPatching(); if (patchedVersion != oldVersion) { modifyDatabaseVersion(patchedVersion); } } private long performPatching() throws FxApplicationException { FxContext.get().runAsSystem(); try { long dbVersion = get(SystemParameters.DB_VERSION); long currentVersion = dbVersion; if (dbVersion == -1) { put(SystemParameters.DB_VERSION, FxSharedUtils.getDBVersion()); return dbVersion; //no need to patch } else if (dbVersion == FxSharedUtils.getDBVersion()) { //nothing to do return dbVersion; } else if (dbVersion > FxSharedUtils.getDBVersion()) { //the database is more current than the EAR! LOG.warn("This [fleXive] build is intended for database schema version #" + FxSharedUtils.getDBVersion() + " but the database reports a higher schema version! (database schema version: " + dbVersion + ")"); return dbVersion; } //lets see if we have a patch we can apply try { final String dbVendor = FxContext.get().getDivisionData().getDbVendor(); final String dir = "resources/patch-" + dbVendor + "/"; String idxFile = dir + "resourceindex.flexive"; ClassLoader cl = Thread.currentThread().getContextClassLoader(); final InputStream scriptIndex = cl.getResourceAsStream(idxFile); if (scriptIndex == null) { LOG.info("No patches available for " + dbVendor); return dbVersion; } String[] files = FxSharedUtils.loadFromInputStream(scriptIndex, -1).replaceAll("\r", "") .split("\n"); Connection con = null; Statement stmt = null; try { con = Database.getNonTXDataSource().getConnection(); stmt = con.createStatement(); List<SQLPatchScript> scripts = new ArrayList<SQLPatchScript>(50); for (String file : files) { String[] f = file.split("\\|"); int size = Integer.parseInt(f[1]); String[] data = f[0].split("_"); if (data.length != 3) { LOG.warn("Expected " + f[0] + " to have format xxx_yyy_zzz.sql"); continue; } if (!"patch".equals(data[0])) { LOG.info("Expected a patch file, but got: " + data[0]); continue; } if (!data[2].endsWith(".sql")) { LOG.info("Expected an sql file, but got: " + data[2]); continue; } long fromVersion = Long.parseLong(data[1]); long toVersion = Long.parseLong(data[2].substring(0, data[2].indexOf('.'))); String code = FxSharedUtils.loadFromInputStream(cl.getResourceAsStream(dir + f[0]), size); scripts.add(new SQLPatchScript(fromVersion, toVersion, code)); // LOG.info("Patch available from version " + fromVersion + " to " + toVersion); } // stmt.executeUpdate(code); boolean patching = true; long maxVersion = currentVersion; while (patching) { patching = false; for (SQLPatchScript ps : scripts) { if (ps.from == currentVersion) { LOG.info("Patching database schema from version [" + ps.from + "] to [" + ps.to + "] ... "); new SQLScriptExecutor(ps.script, stmt).execute(); if (ps.to == 2863) { patchUtils.migrateContentDataGroups(); // remove ISGROUP after the migration was committed try { stmt.execute("ALTER TABLE " + DatabaseConst.TBL_CONTENT_DATA + " DROP COLUMN ISGROUP;"); } catch (SQLException e) { // not critical, column will be ignored in future versions LOG.warn("Failed to remove " + DatabaseConst.TBL_CONTENT_DATA + ".ISGROUP (ignored)", e); } } FxFlatStorageManager.getInstance().patchDatabase(con, ps.from, ps.to); currentVersion = ps.to; patching = true; if (ps.to > maxVersion) ps.to = maxVersion; break; } } } if (currentVersion < maxVersion) { LOG.warn("Failed to patch to maximum available database schema version (" + maxVersion + "). Current database schema version: " + currentVersion); } return currentVersion; } finally { Database.closeObjects(DivisionConfigurationEngineBean.class, con, stmt); } } catch (IOException e) { LOG.fatal(e); return currentVersion; } catch (SQLException e) { LOG.fatal(e); return currentVersion; } } finally { FxContext.get().stopRunAsSystem(); } } private void modifyDatabaseVersion(long currentVersion) throws FxApplicationException { FxContext.get().runAsSystem(); try { put(SystemParameters.DB_VERSION, currentVersion); } finally { FxContext.get().stopRunAsSystem(); } } /** * Helper class to execute an SQL script which contains of multiple statements and comments */ static class SQLScriptExecutor { /** * Statement delimiter */ public final static char DELIMITER = ';'; private Statement stmt; private String script; private List<String> lines; /** * Ctor * * @param script the script to parse and execute * @param stat an open and valid statements * @throws SQLException on errors * @throws IOException on errors */ public SQLScriptExecutor(String script, Statement stat) throws SQLException, IOException { this.stmt = stat; this.script = script; this.lines = new ArrayList<String>(20); parse(); } /** * Parse the script * * @throws IOException on errors * @throws SQLException on errors */ protected void parse() throws IOException, SQLException { BufferedReader reader = new BufferedReader(new StringReader(script)); String currLine; StringBuilder sb = new StringBuilder(500); boolean eos; while ((currLine = reader.readLine()) != null) { if (isComment(currLine)) continue; eos = currLine.indexOf(DELIMITER) != -1; sb.append(currLine).append(' '); if (eos) { lines.add(sb.toString()); sb.setLength(0); } } } /** * Is the passed line a comment? * Only single line comments starting with "#" or "--" are supported! * * @param line line to examine * @return is comment */ private boolean isComment(String line) { return (line != null) && (line.length() > 0) && (line.trim().charAt(0) == '#' || line.trim().startsWith("--")); } /** * Execute the script * * @throws SQLException on errors */ public void execute() throws SQLException { for (String line : lines) { try { stmt.execute(line); } catch (SQLException e) { LOG.error("Failed to execute [" + line + "]!"); throw e; } } } } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.SUPPORTS) public String getDatabaseInfo() { final DivisionData divisionData = FxContext.get().getDivisionData(); return "Division #" + divisionData.getId() + " - " + divisionData.getDbVendor() + " " + divisionData.getDbVersion(); } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.SUPPORTS) public String getDatabaseDriverInfo() { return FxContext.get().getDivisionData().getDbDriverVersion(); } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.SUPPORTS) public boolean isFlatStorageEnabled() { return FxFlatStorageManager.getInstance().isEnabled(); } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.REQUIRED) public List<FxFlatStorageInfo> getFlatStorageInfos() throws FxApplicationException { try { return FxFlatStorageManager.getInstance().getFlatStorageInfos(); } catch (SQLException e) { throw new FxDbException(e, "ex.db.sqlError", e.getMessage()); } } /** * {@inheritDoc} */ @Override public void createFlatStorage(String name, String description, int stringColumns, int textColumns, int bigIntColumns, int doubleColumns, int selectColumns) throws FxApplicationException { createFlatStorage(name, FxFlatStorageInfo.Type.TypeNormal, description, stringColumns, textColumns, bigIntColumns, doubleColumns, selectColumns); createFlatStorage(name, FxFlatStorageInfo.Type.TypeGroups, description, stringColumns, textColumns, bigIntColumns, doubleColumns, selectColumns); } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void createFlatStorage(String name, FxFlatStorageInfo.Type storageType, String description, int stringColumns, int textColumns, int bigIntColumns, int doubleColumns, int selectColumns) throws FxApplicationException { try { Connection con = null; try { con = Database.getNonTXDataSource().getConnection(); FxFlatStorageManager.getInstance().createFlatStorage(con, name, storageType, description, stringColumns, textColumns, bigIntColumns, doubleColumns, selectColumns); } catch (FxApplicationException e) { EJBUtils.rollback(ctx); throw e; } finally { Database.closeObjects(DivisionConfigurationEngineBean.class, con, null); } } catch (SQLException e) { EJBUtils.rollback(ctx); throw new FxDbException(e, "ex.db.sqlError", e.getMessage()); } } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void removeFlatStorage(String name) throws FxApplicationException { try { try { FxFlatStorageManager.getInstance().removeFlatStorage(name); } catch (FxApplicationException e) { EJBUtils.rollback(ctx); throw e; } } catch (SQLException e) { throw new FxDbException(e, "ex.db.sqlError", e.getMessage()); } } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW) public void exportDivision(String localFileName) throws FxApplicationException { if (!FxContext.getUserTicket().isGlobalSupervisor()) throw new FxNoAccessException("ex.export.noAccess"); if (StringUtils.isEmpty(localFileName)) throw new FxInvalidParameterException("localFileName", "ex.export.noFileProvided"); File zip = new File(localFileName); boolean createError = false; try { if (zip.exists() || !zip.createNewFile()) createError = true; } catch (IOException e) { LOG.info(e); createError = true; } if (createError) throw new FxInvalidParameterException("localFileName", "ex.export.fileCreateError", localFileName); FileOutputStream fos = null; Connection con = null; try { con = Database.getDbConnection(); fos = new FileOutputStream(zip); StorageManager.getStorageImpl().exportDivision(con, fos); } catch (Exception e) { throw new FxApplicationException(e, "ex.export.failed", localFileName, e.getMessage()); } finally { Database.closeObjects(DivisionConfigurationEngine.class, con, null); try { if (fos != null) fos.close(); } catch (IOException e) { LOG.error(e); } } } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.SUPPORTS) public FxDivisionExportInfo getDivisionExportInfo(String localFileName) throws FxApplicationException { if (!FxContext.getUserTicket().isGlobalSupervisor()) throw new FxNoAccessException("ex.import.noAccess"); if (StringUtils.isEmpty(localFileName)) throw new FxInvalidParameterException("localFileName", "ex.import.noFileProvided"); File data = new File(localFileName); if (!data.exists() || !data.isFile()) throw new FxInvalidParameterException("localFileName", "ex.import.noFile", localFileName); ZipFile zip; try { zip = new ZipFile(data); } catch (IOException e) { throw new FxInvalidParameterException("localFileName", "ex.import.noZIP", localFileName); } return StorageManager.getStorageImpl().getDivisionExportInfo(zip); } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.NEVER) public void importDivision(String localFileName) throws FxApplicationException { if (!FxContext.getUserTicket().isGlobalSupervisor()) throw new FxNoAccessException("ex.import.noAccess"); if (StringUtils.isEmpty(localFileName)) throw new FxInvalidParameterException("localFileName", "ex.import.noFileProvided"); File data = new File(localFileName); if (!data.exists() || !data.isFile()) throw new FxInvalidParameterException("localFileName", "ex.import.noFile", localFileName); ZipFile zip; try { zip = new ZipFile(data); } catch (IOException e) { throw new FxInvalidParameterException("localFileName", "ex.import.noZIP", localFileName); } Connection con = null; try { con = Database.getDbConnection(); StorageManager.getStorageImpl().importDivision(con, zip); } catch (Exception e) { throw new FxApplicationException(e, "ex.import.failed", localFileName, e.getMessage()); } finally { Database.closeObjects(DivisionConfigurationEngine.class, con, null); } } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.REQUIRED) public void setResourceValue(String key, FxString value) throws FxApplicationException { if (StringUtils.isBlank(key)) return; key = key.trim(); if (key.length() > 250) throw new FxApplicationException("ex.configuration.resource.key.tooLong", key); if (!StringUtils.isAsciiPrintable(key)) throw new FxApplicationException("ex.configuration.resource.key.nonAscii", key); Connection con = null; PreparedStatement ps = null; try { con = Database.getDbConnection(); ps = con.prepareStatement("DELETE FROM " + TBL_RESOURCES + " WHERE RKEY=?"); ps.setString(1, key); ps.executeUpdate(); if (value != null && !value.isEmpty()) { ps.close(); ps = con.prepareStatement("INSERT INTO " + TBL_RESOURCES + " (RKEY,LANG,RVAL)VALUES(?,?,?)"); ps.setString(1, key); for (long lang : value.getTranslatedLanguages()) { ps.setLong(2, lang); ps.setString(3, value.getTranslation(lang)); ps.addBatch(); } ps.executeBatch(); } } catch (SQLException e) { throw new FxApplicationException(e, "ex.db.sqlError", e.getMessage()); } finally { Database.closeObjects(DivisionConfigurationEngine.class, con, ps); } } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.REQUIRED) public void removeResourceValues(String keyPrefix) throws FxApplicationException { if (StringUtils.isBlank(keyPrefix)) return; keyPrefix = keyPrefix.trim(); if (keyPrefix.length() > 250) throw new FxApplicationException("ex.configuration.resource.key.tooLong", keyPrefix); if (!StringUtils.isAsciiPrintable(keyPrefix)) throw new FxApplicationException("ex.configuration.resource.key.nonAscii", keyPrefix); Connection con = null; PreparedStatement ps = null; try { con = Database.getDbConnection(); ps = con.prepareStatement("DELETE FROM " + TBL_RESOURCES + " WHERE RKEY LIKE ?"); ps.setString(1, keyPrefix + "%"); ps.executeUpdate(); } catch (SQLException e) { throw new FxApplicationException(e, "ex.db.sqlError", e.getMessage()); } finally { Database.closeObjects(DivisionConfigurationEngine.class, con, ps); } } /** * Build an FxString from translations * * @param firstLang the first returned language * @param defaultLanguage default language to set, if applicable * @param trans translations * @return FxString */ private FxString buildFxString(long firstLang, long defaultLanguage, Map<Long, String> trans) { if (trans.size() == 0) return null; if (trans.size() == 1 && firstLang == FxLanguage.SYSTEM_ID) return new FxString(false, trans.get(firstLang)); FxString value = new FxString(trans); if (value.translationExists(defaultLanguage)) value.setDefaultLanguage(defaultLanguage); else value.setDefaultLanguage(firstLang); return value; } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.SUPPORTS) public FxString getResourceValue(String key, long defaultLanguage) throws FxApplicationException { if (StringUtils.isBlank(key)) { return null; } key = key.trim(); Connection con = null; PreparedStatement ps = null; try { con = Database.getDbConnection(); ps = con.prepareStatement("SELECT LANG,RVAL FROM " + TBL_RESOURCES + " WHERE RKEY=?"); ps.setString(1, key); ResultSet rs = ps.executeQuery(); long firstLang = -1; Map<Long, String> trans = new HashMap<Long, String>(10); while (rs != null && rs.next()) { if (firstLang == -1) firstLang = rs.getLong(1); trans.put(rs.getLong(1), rs.getString(2)); } return buildFxString(firstLang, defaultLanguage, trans); } catch (SQLException e) { throw new FxApplicationException(e, "ex.db.sqlError", e.getMessage()); } finally { Database.closeObjects(DivisionConfigurationEngine.class, con, ps); } } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.SUPPORTS) public Map<String, FxString> getResourceValues(String keyPrefix, long defaultLanguage) throws FxApplicationException { return getResourceValuesLike(keyPrefix + "%", defaultLanguage); } /** * {@inheritDoc} */ @Override @TransactionAttribute(TransactionAttributeType.SUPPORTS) public Map<String, FxString> getResourceValuesContains(String keyMatch, long defaultLanguage) throws FxApplicationException { return getResourceValuesLike("%" + keyMatch + "%", defaultLanguage); } /** * Get all resource FxString values that match a given key pattern. * * @param keyPattern requested keypattern including wildcards ('%') * @param defaultLanguage default language to set in the returned FxString, if not available the first found * translation is the default language * @return an ordered (in regards to keys) map containing all found keys and FxString values * @throws FxApplicationException on errors * @since 3.1.6 */ private Map<String, FxString> getResourceValuesLike(String keyPattern, long defaultLanguage) throws FxApplicationException { Map<String, FxString> ret = new LinkedHashMap<String, FxString>(10, 5.0f); if (StringUtils.isBlank(keyPattern) || "%".equals(keyPattern) || "%%".equals(keyPattern)) { return ret; } keyPattern = keyPattern.trim(); Connection con = null; PreparedStatement ps = null; try { con = Database.getDbConnection(); ps = con.prepareStatement( "SELECT RKEY,LANG,RVAL FROM " + TBL_RESOURCES + " WHERE RKEY LIKE ? ORDER BY RKEY ASC"); ps.setString(1, keyPattern); ResultSet rs = ps.executeQuery(); String currKey = null; String key; Map<Long, String> trans = new HashMap<Long, String>(10); long firstLang = -1; while (rs != null && rs.next()) { key = rs.getString(1); if (!key.equals(currKey)) { final FxString data = buildFxString(firstLang, defaultLanguage, trans); if (data != null) ret.put(currKey, data); currKey = key; trans.clear(); firstLang = -1; } if (firstLang == -1) firstLang = rs.getLong(2); trans.put(rs.getLong(2), rs.getString(3)); } if (trans.size() > 0) { final FxString data = buildFxString(firstLang, defaultLanguage, trans); if (data != null) ret.put(currKey, data); } return ret; } catch (SQLException e) { throw new FxApplicationException(e, "ex.db.sqlError", e.getMessage()); } finally { Database.closeObjects(DivisionConfigurationEngine.class, con, ps); } } @Override @TransactionAttribute(TransactionAttributeType.SUPPORTS) public Set<String> getResourceKeysMatching(String keyMatch) throws FxApplicationException { FxSharedUtils.checkParameterNull(keyMatch, "keyPrefix"); final Set<String> keys = Sets.newHashSet(); Connection con = null; PreparedStatement ps = null; try { con = Database.getDbConnection(); ps = con.prepareStatement("SELECT RKEY FROM " + TBL_RESOURCES + " WHERE RKEY LIKE ?"); String queryValue = keyMatch.trim(); if (queryValue.indexOf('%') == -1) { queryValue += "%"; } ps.setString(1, queryValue); final ResultSet rs = ps.executeQuery(); while (rs.next()) { keys.add(rs.getString(1)); } return keys; } catch (SQLException e) { throw new FxApplicationException(e, "ex.db.sqlError", e.getMessage()); } finally { Database.closeObjects(DivisionConfigurationEngineBean.class, con, ps); } } @Override @TransactionAttribute(TransactionAttributeType.SUPPORTS) public Set<String> getResourceKeysMatching(String keyMatch, String valueMatch, long searchLanguage) throws FxApplicationException { FxSharedUtils.checkParameterNull(keyMatch, "keyMatch"); FxSharedUtils.checkParameterNull(valueMatch, "valueMatch"); final Set<String> keys = Sets.newHashSet(); Connection con = null; PreparedStatement ps = null; try { con = Database.getDbConnection(); ps = con.prepareStatement( "SELECT RKEY FROM " + TBL_RESOURCES + " WHERE RKEY LIKE ? AND UPPER(RVAL) LIKE ? AND LANG=?"); String keyQuery = keyMatch.trim(); if (keyQuery.indexOf('%') == -1) { keyQuery += "%"; } String valueQuery = valueMatch.trim(); if (valueQuery.indexOf('%') == -1) { valueQuery += "%"; } ps.setString(1, keyQuery); ps.setString(2, valueQuery.toUpperCase()); ps.setLong(3, searchLanguage); final ResultSet rs = ps.executeQuery(); while (rs.next()) { keys.add(rs.getString(1)); } return keys; } catch (SQLException e) { throw new FxApplicationException(e, "ex.db.sqlError", e.getMessage()); } finally { Database.closeObjects(DivisionConfigurationEngineBean.class, con, ps); } } }