com.flexive.ejb.beans.LanguageBean.java Source code

Java tutorial

Introduction

Here is the source code for com.flexive.ejb.beans.LanguageBean.java

Source

/***************************************************************
 *  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;

import com.flexive.core.Database;
import com.flexive.core.storage.StorageManager;
import com.flexive.core.structure.StructureLoader;
import com.flexive.shared.CacheAdmin;
import com.flexive.shared.FxContext;
import com.flexive.shared.FxLanguage;
import com.flexive.shared.cache.FxCacheException;
import com.flexive.shared.content.FxPermissionUtils;
import com.flexive.shared.exceptions.*;
import com.flexive.shared.interfaces.LanguageEngine;
import com.flexive.shared.interfaces.LanguageEngineLocal;
import com.flexive.shared.mbeans.FxCacheMBean;
import com.flexive.shared.security.Role;
import com.flexive.shared.value.FxString;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import javax.ejb.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static com.flexive.core.DatabaseConst.*;

/**
 * [fleXive] language engine interface.
 * This engine should not be used to load languages as they are available from the environment!
 * Its purpose is to enable/disable, initially load and manage (position, etc.) languages.
 *
 * @author Markus Plesser (markus.plesser@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
 * @version $Rev$
 */

@Stateless(name = "LanguageEngine", mappedName = "LanguageEngine")
@TransactionAttribute(TransactionAttributeType.SUPPORTS)
@TransactionManagement(TransactionManagementType.CONTAINER)
public class LanguageBean implements LanguageEngine, LanguageEngineLocal {

    private static final Log LOG = LogFactory.getLog(LanguageBean.class);
    private final static String CACHE_KEY_ALL_LANG_IDS = "@ID@";

    /**
     * All tables that have references to languages and the referencing column
     */
    private final static String[][] LANG_USAGE = { { TBL_ACCOUNTS, "LANG" }, { TBL_ACLS + ML, "LANG" },
            { TBL_WORKFLOW_STEPDEFINITION + ML, "LANG" }, { TBL_STRUCT_TYPES + ML, "LANG" },
            { TBL_STRUCT_GROUPS + ML, "LANG" }, { TBL_STRUCT_DATATYPES + ML, "LANG" },
            { TBL_STRUCT_SELECTLIST + ML, "LANG" }, { TBL_STRUCT_SELECTLIST_ITEM + ML, "LANG" },
            { TBL_STRUCT_PROPERTIES + ML, "LANG" }, { TBL_STRUCT_ASSIGNMENTS, "DEFLANG" },
            { TBL_STRUCT_ASSIGNMENTS + ML, "LANG" }, { TBL_CONTENT, "MAINLANG" }, { TBL_CONTENT_DATA, "LANG" },
            { TBL_CONTENT_DATA_FT, "LANG" } };

    /**
     * {@inheritDoc}
     */
    @Override
    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public FxLanguage load(long languageId) throws FxApplicationException {
        try {
            FxLanguage lang = (FxLanguage) CacheAdmin.getInstance().get(CacheAdmin.LANGUAGES_ID, languageId);
            if (lang == null) {
                loadAll(true, true);
                lang = (FxLanguage) CacheAdmin.getInstance().get(CacheAdmin.LANGUAGES_ID, languageId);
            }
            if (lang == null) {
                //check unavailable
                for (FxLanguage l : loadAll(false, false)) {
                    if (l.getId() == languageId)
                        return l;
                }
                throw new FxInvalidLanguageException("ex.language.invalid", languageId);
            }
            return lang;
        } catch (FxCacheException e) {
            throw new FxLoadException(LOG, e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public FxLanguage load(String languageIsoCode) throws FxApplicationException {
        try {
            if (StringUtils.isBlank(languageIsoCode) || languageIsoCode.length() != 2)
                throw new FxInvalidLanguageException("ex.language.invalid", languageIsoCode);
            FxLanguage lang = (FxLanguage) CacheAdmin.getInstance().get(CacheAdmin.LANGUAGES_ISO, languageIsoCode);
            if (lang == null) {
                //check unavailable
                String check = languageIsoCode.toLowerCase();
                for (FxLanguage l : loadAll(false, false)) {
                    if (l.getIso2digit().equals(check))
                        return l;
                }
                throw new FxInvalidLanguageException("ex.language.invalid", languageIsoCode);
            }
            return lang;
        } catch (FxCacheException e) {
            throw new FxLoadException(LOG, e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @SuppressWarnings({ "unchecked" })
    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public List<FxLanguage> loadAvailable() throws FxApplicationException {
        try {
            List<FxLanguage> available = (List<FxLanguage>) CacheAdmin.getInstance().get(CacheAdmin.LANGUAGES_ALL,
                    CACHE_KEY_ALL_LANG_IDS);
            if (available == null) {
                loadAll(true, true);
                available = (List<FxLanguage>) CacheAdmin.getInstance().get(CacheAdmin.LANGUAGES_ALL,
                        CACHE_KEY_ALL_LANG_IDS);
                if (available == null)
                    throw new FxInvalidLanguageException("ex.language.loadFailed");
            }
            return available;
        } catch (FxCacheException e) {
            throw new FxLoadException(LOG, e);
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public List<FxLanguage> loadDisabled() throws FxApplicationException {
        return loadAll(false, false);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public List<FxLanguage> loadAvailable(boolean excludeSystemLanguage) throws FxApplicationException {
        List<FxLanguage> tmp = loadAvailable();
        ArrayList<FxLanguage> result = new ArrayList<FxLanguage>();
        for (FxLanguage lang : tmp) {
            if (excludeSystemLanguage && lang.getId() == 0)
                continue;
            result.add(lang);
        }
        return result;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @TransactionAttribute(TransactionAttributeType.SUPPORTS)
    public boolean isValid(long languageId) {
        // Does the language exist at all? Check via constructor
        try {
            load(languageId);
        } catch (FxApplicationException exc) {
            return false;
        }
        return true;
    }

    /**
     * Initial load function.
     *
     * @param used      load used or unused languages?
     * @param add2cache put loaded languages into cache?
     * @return list containing requested languages
     */
    private synchronized List<FxLanguage> loadAll(boolean used, boolean add2cache) {
        String sql = "SELECT l.LANG_CODE, l.ISO_CODE, t.LANG, t.DESCRIPTION FROM " + TBL_LANG + " l, " + TBL_LANG
                + ML + " t " + "WHERE t.LANG_CODE=l.LANG_CODE AND l.INUSE="
                + StorageManager.getBooleanExpression(used) + " ORDER BY l.DISPPOS ASC, l.LANG_CODE ASC";
        Connection con = null;
        Statement stmt = null;
        List<FxLanguage> alLang = new ArrayList<FxLanguage>(140);
        try {
            con = Database.getDbConnection();
            stmt = con.createStatement();
            ResultSet rs = stmt.executeQuery(sql);
            Map<Long, String> hmMl = new HashMap<Long, String>(5);
            int lang_code = -1;
            String iso_code = null;
            FxCacheMBean cache = CacheAdmin.getInstance();
            while (rs != null && rs.next()) {
                if (lang_code != rs.getInt(1)) {
                    if (lang_code != -1 && lang_code != FxLanguage.SYSTEM_ID) {
                        //add
                        FxLanguage lang = new FxLanguage(lang_code, iso_code,
                                new FxString(FxLanguage.DEFAULT_ID, hmMl), true);
                        if (add2cache) {
                            cache.put(CacheAdmin.LANGUAGES_ID, lang.getId(), lang);
                            cache.put(CacheAdmin.LANGUAGES_ISO, lang.getIso2digit(), lang);
                        }
                        alLang.add(lang);
                    }
                    lang_code = rs.getInt(1);
                    iso_code = rs.getString(2);
                    hmMl.clear();
                }
                hmMl.put(rs.getLong(3), rs.getString(4));
            }
            if (lang_code != -1 && lang_code != FxLanguage.SYSTEM_ID) {
                //add
                FxLanguage lang = new FxLanguage(lang_code, iso_code, new FxString(FxLanguage.DEFAULT_ID, hmMl),
                        true);
                if (add2cache && used) {
                    cache.put(CacheAdmin.LANGUAGES_ID, lang.getId(), lang);
                    cache.put(CacheAdmin.LANGUAGES_ISO, lang.getIso2digit(), lang);
                }
                alLang.add(lang);
            }
            if (add2cache && used) {
                cache.put(CacheAdmin.LANGUAGES_ALL, CACHE_KEY_ALL_LANG_IDS, alLang);
            }
        } catch (SQLException e) {
            LOG.error(e, e);
        } catch (FxCacheException e) {
            LOG.error(e, e);
        } finally {
            Database.closeObjects(LanguageBean.class, con, stmt);
        }
        return alLang;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void activateLanguage(FxLanguage language) throws FxApplicationException {
        List<FxLanguage> available = loadAvailable();
        if (available.contains(language)) {
            LOG.info("Language " + language + " is already active.");
            return;
        }
        available.add(language);
        setAvailable(available, false);
    }

    /**
     * {@inheritDoc}
     */
    @Override
    @TransactionAttribute(TransactionAttributeType.REQUIRED)
    public void setAvailable(List<FxLanguage> available, boolean ignoreUsage) throws FxApplicationException {
        FxPermissionUtils.checkRole(FxContext.getUserTicket(), Role.GlobalSupervisor);
        Connection con = null;
        PreparedStatement ps = null;
        if (available == null || available.size() == 0)
            throw new FxInvalidParameterException("available", "ex.language.noAvailable");
        try {
            con = Database.getDbConnection();
            if (!ignoreUsage) {
                List<FxLanguage> orgLang = loadAvailable(true);
                boolean found;
                for (FxLanguage org : orgLang) {
                    found = false;
                    for (FxLanguage tmp : available) {
                        if (tmp.getId() == org.getId()) {
                            found = true;
                            break;
                        }
                    }
                    if (!found && hasUsages(con, org))
                        throw new FxInvalidParameterException("available", "ex.language.removeUsed",
                                org.getLabel());
                }
            }
            ps = con.prepareStatement("UPDATE " + TBL_LANG + " SET INUSE=?, DISPPOS=?");
            ps.setBoolean(1, false);
            ps.setNull(2, java.sql.Types.INTEGER);
            ps.executeUpdate();
            ps.close();
            int pos = 0;
            ps = con.prepareStatement("UPDATE " + TBL_LANG + " SET INUSE=?, DISPPOS=? WHERE LANG_CODE=?");
            ps.setBoolean(1, true);
            for (FxLanguage lang : available) {
                ps.setInt(2, pos++);
                ps.setLong(3, lang.getId());
                ps.addBatch();
            }
            ps.executeBatch();
            StructureLoader.updateLanguages(FxContext.get().getDivisionId(), loadAll(true, true));
        } catch (FxCacheException e) {
            LOG.error(e, e);
        } catch (SQLException e) {
            throw new FxUpdateException(LOG, e, "ex.db.sqlError", e.getMessage());
        } finally {
            Database.closeObjects(LanguageBean.class, con, ps);
        }
    }

    /**
     * Check if the given language is referenced from a table
     *
     * @param con      an open and valid connection
     * @param language the language to check
     * @return if the language is in use
     * @throws FxApplicationException on errors
     * @throws SQLException           on errors
     */
    private boolean hasUsages(Connection con, FxLanguage language) throws FxApplicationException, SQLException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Checking for uses of language " + language.getIso2digit());
        }
        PreparedStatement ps = null;
        try {
            ResultSet rs;
            for (String[] check : LANG_USAGE) {
                if (ps != null)
                    ps.close();
                ps = con.prepareStatement("SELECT COUNT(*) FROM " + check[0] + " WHERE " + check[1] + "=?");
                ps.setLong(1, language.getId());
                rs = ps.executeQuery();
                if (rs != null && rs.next()) {
                    if (rs.getLong(1) > 0) {
                        LOG.info("Language [" + language.getIso2digit() + "] has [" + rs.getLong(1)
                                + "] usages in table " + check[0] + ", column " + check[1]);
                        return true;
                    }
                }
            }
        } finally {
            if (ps != null)
                ps.close();
        }
        return false;
    }
}