org.sakaiproject.messagebundle.impl.MessageBundleServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for org.sakaiproject.messagebundle.impl.MessageBundleServiceImpl.java

Source

/**********************************************************************************
 * $URL: https://source.sakaiproject.org/svn/kernel/branches/SAK-18678/api/src/main/java/org/sakaiproject/site/api/Site.java $
 * $Id: Site.java 81275 2010-08-14 09:24:56Z david.horwitz@uct.ac.za $
 ***********************************************************************************
 *
 * Copyright (c) 2003, 2004, 2005, 2006, 2008 Sakai Foundation
 *
 * Licensed under the Educational Community License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.opensource.org/licenses/ECL-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 **********************************************************************************/
package org.sakaiproject.messagebundle.impl;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.sakaiproject.messagebundle.api.MessageBundleService;
import org.sakaiproject.messagebundle.api.MessageBundleProperty;
import org.hibernate.*;
import org.hibernate.type.BasicType;
import org.hibernate.type.StandardBasicTypes;
import org.hibernate.type.Type;
import org.springframework.beans.BeanUtils;
import org.springframework.orm.hibernate3.HibernateCallback;
import org.springframework.orm.hibernate3.SessionHolder;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
import org.springframework.transaction.support.TransactionSynchronizationManager;

import java.sql.SQLException;
import java.util.*;
import java.util.Map.Entry;

/**
 * Responsible for managing the message bundle data in a database.  Provides search capabilities
 * for finding keys based on values.
 *
 * Created by IntelliJ IDEA.
 * User: jbush
 * Date: Mar 16, 2010
 * Time: 1:38:05 PM
 * To change this template use File | Settings | File Templates.
 */
public class MessageBundleServiceImpl extends HibernateDaoSupport implements MessageBundleService {
    private static Log logger = LogFactory.getLog(MessageBundleServiceImpl.class);

    /**
      * list of bundles that we've already indexed, only want to update once per startup
      */
    private Set<String> indexedList = new HashSet<String>();
    /**
     * number of millis before running saveOrUpdateTask again to clear queue
     */
    private long scheduleDelay = 5000;
    /**
     * whether or not to save bundle data right away, or queue up for processing in another thread.
     */
    private boolean scheduleSaves = true;

    /**
     * Timer used to schedule processing
     */
    private Timer timer = new Timer(true);
    /**
     *  Queue of method invocations to save or update message bundle data
     */
    private List<SaveOrUpdateCall> queue = Collections.synchronizedList(new ArrayList<>());

    public void init() {
        timer.schedule(new SaveOrUpdateTask(), 0, scheduleDelay);
    }

    public int getSearchCount(String searchQuery, String module, String baseName, String locale) {
        List<String> values = new ArrayList<String>();
        List<BasicType> types = new ArrayList<BasicType>();
        StringBuffer queryString = new StringBuffer("");

        try {
            if (StringUtils.isNotEmpty(searchQuery)) {
                queryString.append("(defaultValue like ? OR value like ? OR propertyName = ?)");
                values.add("%" + searchQuery + "%");
                values.add("%" + searchQuery + "%");
                values.add(searchQuery);
                types.add(StandardBasicTypes.STRING);
                types.add(StandardBasicTypes.STRING);
                types.add(StandardBasicTypes.STRING);
            }
            if (StringUtils.isNotEmpty(module)) {
                if (queryString.length() > 0) {
                    queryString.append(" AND ");
                }
                queryString.append("moduleName = ? ");
                values.add(module);
                types.add(StandardBasicTypes.STRING);

            }
            if (StringUtils.isNotEmpty(baseName)) {
                if (queryString.length() > 0) {
                    queryString.append(" AND ");
                }
                queryString.append("baseName = ?");
                values.add(baseName);
                types.add(StandardBasicTypes.STRING);

            }
            if (StringUtils.isNotEmpty(locale)) {
                if (queryString.length() > 0) {
                    queryString.append(" AND ");
                }
                queryString.append("locale = ?");
                values.add(locale);
                types.add(StandardBasicTypes.STRING);

            }
            if (queryString.length() > 0) {
                queryString.insert(0, "select count(*) from MessageBundleProperty where ");
            } else {
                queryString.insert(0, "select count(*) from MessageBundleProperty");
            }
            Integer count = null;
            try {
                Query query = getSessionFactory().getCurrentSession().createQuery(queryString.toString());
                query.setParameters(values.toArray(), (Type[]) types.toArray(new Type[types.size()]));
                count = (Integer) query.uniqueResult();
            } catch (HibernateException e) {
                throw new RuntimeException(e.getMessage(), e);
            }
            return count.intValue();
        } catch (Exception e) {
            logger.error("problem searching the message bundle data", e);
        }
        return 0;
    }

    /**
      * schedule timer task to save/update the bundle data.  We are using Timer to offload the work,
      * otherwise intial loads of tools will appear very slow, this way it happens in the background.
      * In the original rSmart impl JMS was used, but since the MessageService is in contrib not core
      * we need another solution to avoid that dependency. Currently we are using a java.util.Timer and
      * scheduled Timer task to queue up and process the calls to this method.  This is a similar
      * strategy to that used in BaseDigestService.
      *
      * @param baseName
      * @param moduleName
      * @param newBundle
      * @param loc
      */
    public void saveOrUpdate(String baseName, String moduleName, ResourceBundle newBundle, Locale loc) {
        if (StringUtils.isBlank(baseName) || StringUtils.isBlank(moduleName) || loc == null || newBundle == null) {
            return;
        }

        if (scheduleSaves) {
            queueBundle(baseName, moduleName, convertResourceBundleToMap(newBundle), loc);
        } else {
            saveOrUpdateInternal(baseName, moduleName, convertResourceBundleToMap(newBundle), loc);
        }
    }

    /**
     * internal work for responding to a save or update request.  This method will add new bundles data
     * if it doesn't exist, otherwise updates the data preserving any current value if its been modified.
     * This approach allows for upgrades to automatically detect and persist new keys, as well as updating
     * any default values that flow in from an upgrade.
     * @param baseName
     * @param moduleName
     * @param newBundle
     * @param loc
     */
    protected void saveOrUpdateInternal(String baseName, String moduleName, Map<String, String> newBundle,
            Locale loc) {
        String keyName = getIndexKeyName(baseName, moduleName, loc.toString());
        if (indexedList.contains(keyName)) {
            if (logger.isDebugEnabled())
                logger.debug("skip saveOrUpdate() as its already happened once for :" + keyName);
            return;
        }

        Set<Entry<String, String>> entrySet = newBundle.entrySet();
        Iterator<Entry<String, String>> entries = entrySet.iterator();

        while (entries.hasNext()) {
            Entry<String, String> entry = entries.next();
            String key = entry.getKey();
            MessageBundleProperty mbp = new MessageBundleProperty();
            mbp.setBaseName(baseName);
            mbp.setModuleName(moduleName);
            mbp.setLocale(loc.toString());
            mbp.setPropertyName(key);
            MessageBundleProperty existingMbp = getProperty(mbp);
            if (existingMbp != null) {
                //don"t update id or value, we don't want to loose that data
                BeanUtils.copyProperties(mbp, existingMbp, new String[] { "id", "defaultValue", "value" });
                if (logger.isDebugEnabled())
                    logger.debug("updating message bundle data for : "
                            + getIndexKeyName(mbp.getBaseName(), mbp.getModuleName(), mbp.getLocale()));
                updateMessageBundleProperty(existingMbp);
            } else {
                mbp.setDefaultValue(entry.getValue());
                if (logger.isDebugEnabled())
                    logger.debug("adding message bundle data for : "
                            + getIndexKeyName(mbp.getBaseName(), mbp.getModuleName(), mbp.getLocale()));
                updateMessageBundleProperty(mbp);
            }
        }
        indexedList.add(getIndexKeyName(baseName, moduleName, loc.toString()));
    }

    @SuppressWarnings("unchecked")
    public List<MessageBundleProperty> search(String searchQuery, String module, String baseName, String locale) {
        List<String> values = new ArrayList<String>();
        StringBuffer queryString = new StringBuffer("");

        try {
            if (StringUtils.isNotEmpty(searchQuery)) {
                queryString.append("(defaultValue like ? OR value like ? OR propertyName = ?)");
                values.add("%" + searchQuery + "%");
                values.add("%" + searchQuery + "%");
                values.add(searchQuery);
            }
            if (StringUtils.isNotEmpty(module)) {
                if (queryString.length() > 0) {
                    queryString.append(" AND ");
                }
                queryString.append("moduleName = ? ");
                values.add(module);
            }
            if (StringUtils.isNotEmpty(baseName)) {
                if (queryString.length() > 0) {
                    queryString.append(" AND ");
                }
                queryString.append("baseName = ?");
                values.add(baseName);

            }
            if (StringUtils.isNotEmpty(locale)) {
                if (queryString.length() > 0) {
                    queryString.append(" AND ");
                }
                queryString.append("locale = ?");
                values.add(locale);
            }

            if (queryString.length() > 0) {
                queryString.insert(0, "from MessageBundleProperty where ");
            } else {
                queryString.insert(0, "from MessageBundleProperty");
            }

            return (List<MessageBundleProperty>) getHibernateTemplate().find(queryString.toString(),
                    values.toArray());

        } catch (Exception e) {
            logger.error("problem searching the message bundle data", e);
        }
        return new ArrayList<MessageBundleProperty>();
    }

    private Map<String, String> convertResourceBundleToMap(ResourceBundle resource) {
        Map<String, String> map = new HashMap<String, String>();

        Enumeration<String> keys = resource.getKeys();
        while (keys.hasMoreElements()) {
            String key = keys.nextElement();
            map.put(key, resource.getString(key));
        }

        return map;
    }

    public MessageBundleProperty getMessageBundleProperty(long id) {
        return (MessageBundleProperty) getHibernateTemplate().get(MessageBundleProperty.class, id);
    }

    public void updateMessageBundleProperty(MessageBundleProperty mbp) {
        if (mbp == null)
            return;
        if (mbp.getDefaultValue() == null) {
            mbp.setDefaultValue("");
        }
        getHibernateTemplate().saveOrUpdate(mbp);
    }

    public void deleteMessageBundleProperty(MessageBundleProperty mbp) {
        try {
            getHibernateTemplate().delete(mbp);
        } catch (Exception e) {
            logger.warn("Cound not delete MessageBundleProperty " + mbp + ", " + e.getMessage(), e);
        }
    }

    public MessageBundleProperty getProperty(MessageBundleProperty mbp) {
        if (mbp == null)
            return null;
        Object[] values = new Object[] { mbp.getBaseName(), mbp.getModuleName(), mbp.getPropertyName(),
                mbp.getLocale() };
        String sql = "from MessageBundleProperty where baseName = ? and moduleName = ? and propertyName = ? and locale = ?";
        List<?> results = getHibernateTemplate().find(sql, values);
        if (results.size() == 0) {
            if (logger.isDebugEnabled())
                logger.debug("can't find a message bundle property for : " + mbp);
            return null;
        }
        return (MessageBundleProperty) results.get(0);
    }

    public Map<String, String> getBundle(String baseName, String moduleName, Locale loc) {
        Map<String, String> map = new HashMap<String, String>();

        if (StringUtils.isBlank(baseName) || StringUtils.isBlank(moduleName) || loc == null) {
            return map;
        }

        Object[] values = new Object[] { baseName, moduleName, loc.toString() };
        String sql = "from MessageBundleProperty where baseName = ? and moduleName = ? and locale = ? and value != null";

        List<MessageBundleProperty> results = (List<MessageBundleProperty>) getHibernateTemplate().find(sql,
                values);

        for (MessageBundleProperty mbp : results) {
            map.put(mbp.getPropertyName(), mbp.getValue());
        }

        if (map.isEmpty() && logger.isDebugEnabled())
            logger.debug("can't find any values for: " + getIndexKeyName(baseName, moduleName, loc.toString()));
        return map;
    }

    protected String getIndexKeyName(String baseName, String moduleName, String loc) {
        String context = moduleName != null ? moduleName : "";
        return context + "_" + baseName + "_" + loc;
    }

    public int getModifiedPropertiesCount() {
        String query = "select count(*) from MessageBundleProperty where value != null";
        return executeCountQuery(query);
    }

    public int getAllPropertiesCount() {
        String query = "select count(*) from MessageBundleProperty";
        return executeCountQuery(query);
    }

    @SuppressWarnings("unchecked")
    public List<MessageBundleProperty> getAllProperties(String locale, String module) {
        if (StringUtils.isEmpty(locale) && StringUtils.isEmpty(module)) {
            return (List<MessageBundleProperty>) getHibernateTemplate().find("from MessageBundleProperty");
        } else if (module == null || module.length() == 0) {
            return (List<MessageBundleProperty>) getHibernateTemplate()
                    .find("from MessageBundleProperty where locale = ?", locale);
        } else if (locale == null || locale.length() == 0) {
            return (List<MessageBundleProperty>) getHibernateTemplate()
                    .find("from MessageBundleProperty where moduleName = ?", module);
        } else {
            return (List<MessageBundleProperty>) getHibernateTemplate().find(
                    "from MessageBundleProperty where locale = ? and moduleName = ?",
                    new String[] { locale, module });
        }
    }

    @SuppressWarnings("unchecked")
    public int revertAll(final String locale) {
        HibernateCallback callback = new HibernateCallback() {
            public Object doInHibernate(org.hibernate.Session session) throws HibernateException, SQLException {
                String hql = "update MessageBundleProperty set value = null where locale = :locale";
                org.hibernate.Query query = session.createQuery(hql);
                query.setString("locale", locale);
                int rowCount = query.executeUpdate();
                return Integer.valueOf(rowCount);
            }
        };

        try {
            return ((Integer) getHibernateTemplate().execute(callback)).intValue();
        } catch (Exception e) {
            logger.warn("Cound not revert all MessageBundleProperty's " + e.getMessage(), e);
        }

        return 0;
    }

    public int importProperties(List<MessageBundleProperty> properties) {
        int rows = 0;
        for (MessageBundleProperty property : properties) {
            MessageBundleProperty loadedMbp = getProperty(property);
            if (loadedMbp != null) {
                BeanUtils.copyProperties(property, loadedMbp, new String[] { "id" });
                updateMessageBundleProperty(loadedMbp);
            } else {
                updateMessageBundleProperty(property);
            }

            rows++;
        }
        return rows;
    }

    @SuppressWarnings("unchecked")
    public List<String> getAllModuleNames() {
        List<String> retValue = (List<String>) getHibernateTemplate()
                .find("select distinct(moduleName) from MessageBundleProperty  order by moduleName");
        if (retValue == null)
            return new ArrayList<>();
        //force deep load
        retValue.size();
        return retValue;
    }

    @SuppressWarnings("unchecked")
    public List<String> getAllBaseNames() {
        List<String> retValue = (List<String>) getHibernateTemplate()
                .find("select distinct(baseName) from MessageBundleProperty  order by baseName");
        if (retValue == null)
            return new ArrayList<>();
        //force deep load
        retValue.size();
        return retValue;

    }

    public void revert(MessageBundleProperty mbp) {
        if (mbp == null)
            return;
        mbp.setValue(null);
        try {
            getHibernateTemplate().update(mbp);
        } catch (Exception e) {
            logger.warn("Cound not revert MessageBundleProperty " + mbp + ", " + e.getMessage(), e);
        }
    }

    protected int executeCountQuery(String query) {
        Integer count = null;
        try {
            count = (Integer) getSessionFactory().getCurrentSession().createQuery(query).uniqueResult();
        } catch (HibernateException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
        return count.intValue();
    }

    @SuppressWarnings("unchecked")
    public List<MessageBundleProperty> getModifiedProperties(int sortOrder, int sortField, int startingIndex,
            int pageSize) {
        String orderBy = "asc";
        if (sortOrder == SORT_ORDER_DESCENDING) {
            orderBy = "desc";
        }
        String sortFieldName = "id";
        if (sortField == SORT_FIELD_MODULE) {
            sortFieldName = "moduleName";
        }
        if (sortField == SORT_FIELD_PROPERTY) {
            sortFieldName = "propertyName";
        }
        if (sortField == SORT_FIELD_LOCALE) {
            sortFieldName = "locale";
        }
        if (sortField == SORT_FIELD_BASENAME) {
            sortFieldName = "baseName";
        }
        org.hibernate.Query query = null;
        String queryString = "from MessageBundleProperty where value != null order by " + sortFieldName + " "
                + orderBy;
        try {
            query = getSessionFactory().getCurrentSession().createQuery(queryString);
            query.setFirstResult(startingIndex);
            query.setMaxResults(pageSize);
            return query.list();
        } catch (HibernateException e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }

    /**
     * queues up a call to add or update message bundle data
     * @param baseName
     * @param moduleName
     * @param bundleData
     * @param loc
     */
    protected void queueBundle(String baseName, String moduleName, Map<String, String> bundleData, Locale loc) {
        SaveOrUpdateCall call = new SaveOrUpdateCall();
        call.baseName = baseName;
        call.moduleName = moduleName;
        call.bundleData = bundleData;
        call.loc = loc;
        queue.add(call);
    }

    @SuppressWarnings("unchecked")
    public List<String> getLocales() {
        return (List<String>) getHibernateTemplate().find("select distinct(locale) from MessageBundleProperty");
    }

    public void setScheduleDelay(long scheduleDelay) {
        this.scheduleDelay = scheduleDelay;
    }

    public void setScheduleSaves(boolean scheduleSaves) {
        this.scheduleSaves = scheduleSaves;
    }

    // represents one method call, encapsulate this so we can queue them up
    class SaveOrUpdateCall {
        String baseName;
        String moduleName;
        Map<String, String> bundleData;
        Locale loc;
    }

    /**
     *
     */
    class SaveOrUpdateTask extends TimerTask {

        public SaveOrUpdateTask() {
        }

        /**
         * step through queue and call the real saveOrUpdateInternal method to do the work
         */
        public void run() {
            List<SaveOrUpdateCall> queueList = new ArrayList<>(queue);
            Session session = null;
            try {
                // Since we are in a thread that doesn't have a hibernate session
                // we need to manage it here
                session = getSessionFactory().openSession();
                TransactionSynchronizationManager.bindResource(getSessionFactory(), new SessionHolder(session));

                for (SaveOrUpdateCall call : queueList) {
                    try {
                        session.beginTransaction();
                        saveOrUpdateInternal(call.baseName, call.moduleName, call.bundleData, call.loc);
                    } catch (Throwable e) {
                        logger.error("problem saving bundle data:", e);
                        session.getTransaction().rollback();
                    } finally {
                        if (!session.getTransaction().wasRolledBack()) {
                            session.flush();
                            session.getTransaction().commit();
                        }
                        queue.remove(call);
                    }
                }
            } finally {
                if (session != null) {
                    session.close();
                }
                TransactionSynchronizationManager.unbindResource(getSessionFactory());
            }
        }
    }
}