org.ff4j.store.FeatureStoreSpringJdbc.java Source code

Java tutorial

Introduction

Here is the source code for org.ff4j.store.FeatureStoreSpringJdbc.java

Source

package org.ff4j.store;

/*
 * #%L ff4j-store-jdbc %% Copyright (C) 2013 Ff4J %% Licensed under the Apache 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.apache.org/licenses/LICENSE-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. #L%
 */

import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.sql.DataSource;

import org.ff4j.core.Feature;
import org.ff4j.core.FeatureStore;
import org.ff4j.exception.FeatureAlreadyExistException;
import org.ff4j.exception.FeatureNotFoundException;
import org.ff4j.exception.GroupNotFoundException;
import org.ff4j.property.Property;
import org.ff4j.store.rowmapper.CustomPropertyRowMapper;
import org.ff4j.store.rowmapper.FeatureRowMapper;
import org.ff4j.store.rowmapper.RoleRowMapper;
import org.ff4j.utils.MappingUtil;
import org.ff4j.utils.Util;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.dao.EmptyResultDataAccessException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.SingleColumnRowMapper;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

/**
 * Implementation of {@link FeatureStore} to work with RDBMS through JDBC.
 * 
 * @author <a href="mailto:cedrick.lunven@gmail.com">Cedrick LUNVEN</a>
 */
@Repository
public class FeatureStoreSpringJdbc extends AbstractFeatureStore {

    /** Row Mapper for FlipPoint. */
    private static final FeatureRowMapper FMAPPER = new FeatureRowMapper();

    /** Mapper for custom properties. */
    private static final CustomPropertyRowMapper PMAPPER = new CustomPropertyRowMapper();

    /** Error message. */
    public static final String FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY = "Feature identifier cannot be null nor empty";

    /** Error message. */
    public static final String GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY = "Groupname cannot be null nor empty";

    /** SQL DataSource. */
    private DataSource dataSource;

    /** Access to storage. */
    private JdbcTemplate jdbcTemplate;

    /** Query builder. */
    private JdbcQueryBuilder queryBuilder;

    /** {@inheritDoc} */
    public void enable(String uid) {
        Util.assertHasLength(uid);
        if (!exist(uid)) {
            throw new FeatureNotFoundException(uid);
        }
        getJdbcTemplate().update(getQueryBuilder().enableFeature(), uid);
    }

    /** {@inheritDoc} */
    public void disable(String uid) {
        Util.assertHasLength(uid);
        if (!exist(uid)) {
            throw new FeatureNotFoundException(uid);
        }
        getJdbcTemplate().update(getQueryBuilder().disableFeature(), uid);
    }

    /** {@inheritDoc} */
    public boolean exist(String uid) {
        Util.assertHasLength(uid);
        return 1 == getJdbcTemplate().queryForObject(getQueryBuilder().existFeature(), Integer.class, uid);
    }

    /** {@inheritDoc} */
    public Feature read(String uid) {
        Util.assertHasLength(uid);
        try {
            Feature feature = getJdbcTemplate().queryForObject(getQueryBuilder().getFeature(), FMAPPER, uid);
            readProperties(feature);
            readPermissions(feature);
            return feature;
        } catch (EmptyResultDataAccessException ex) {
            throw new FeatureNotFoundException(uid, ex);
        }
    }

    /**
     * Query children properties.
     *
     * @param fp
     */
    private void readProperties(Feature fp) {
        List<Property<?>> listOfProps = getJdbcTemplate().query(getQueryBuilder().getFeatureProperties(), PMAPPER,
                fp.getUid());
        for (Property<?> ap : listOfProps) {
            fp.getCustomProperties().put(ap.getName(), ap);
        }
    }

    /**
     * Query children roles.
     *
     * @param fp
     */
    private void readPermissions(Feature fp) {
        fp.getPermissions().addAll(getJdbcTemplate().query(getQueryBuilder().getRoles(),
                new SingleColumnRowMapper<String>(), fp.getUid()));
    }

    /** {@inheritDoc} */
    @Override
    @Transactional
    public void create(Feature fp) {
        Util.assertNotNull(fp);
        if (exist(fp.getUid())) {
            throw new FeatureAlreadyExistException(fp.getUid());
        }
        createCoreFeature(fp);
        createPermissions(fp);
        createProperties(fp);
    }

    private void createCoreFeature(Feature fp) {
        // Transaction wraps the method, could pipe several sql queries
        String strategyColumn = null;
        String expressionColumn = null;
        if (fp.getFlippingStrategy() != null) {
            strategyColumn = fp.getFlippingStrategy().getClass().getCanonicalName();
            expressionColumn = MappingUtil.fromMap(fp.getFlippingStrategy().getInitParams());
        }
        getJdbcTemplate().update(getQueryBuilder().createFeature(), fp.getUid(), fp.isEnable() ? 1 : 0,
                fp.getDescription(), strategyColumn, expressionColumn, fp.getGroup());
    }

    /**
     * Remove all existing permissions and create new.
     *
     * @param fp
     */
    private void createPermissions(Feature fp) {
        if (fp.getPermissions() != null) {
            getJdbcTemplate().update(getQueryBuilder().deleteRoles(), fp.getUid());
            for (String role : fp.getPermissions()) {
                getJdbcTemplate().update(getQueryBuilder().addRoleToFeature(), fp.getUid(), role);
            }
        }
    }

    /**
     * Remove all existing permissions and create new.
     *
     * @param fp
     */
    private void createProperties(Feature fp) {
        if (fp.getCustomProperties() != null) {
            getJdbcTemplate().update(getQueryBuilder().deleteAllFeatureCustomProperties(), fp.getUid());
            for (String propertyName : fp.getCustomProperties().keySet()) {
                Property<?> ap = fp.getCustomProperties().get(propertyName);
                String fixedValues = null;
                if (ap.getFixedValues() != null && ap.getFixedValues().size() > 0) {
                    fixedValues = ap.getFixedValues().toString();
                    fixedValues = fixedValues.substring(1, fixedValues.length() - 1);
                }
                getJdbcTemplate().update(getQueryBuilder().createFeatureProperty(), ap.getName(), ap.getType(),
                        ap.asString(), ap.getDescription(), fixedValues, fp.getUid());
            }
        }
    }

    /** {@inheritDoc} */
    @Override
    @Transactional
    public void delete(String uid) {
        if (!exist(uid))
            throw new FeatureNotFoundException(uid);
        deletePermissions(uid);
        deleteProperties(uid);
        deleteCoreFeature(uid);
    }

    /**
     * Delete permissions related to a feature.
     *
     * @param featureId
     *      current feature uid
     */
    private void deletePermissions(String featureId) {
        getJdbcTemplate().update(getQueryBuilder().deleteRoles(), featureId);
    }

    /**
     * Delete properties related to a feature.
     *
     * @param featureId
     *      current feature uid
     */
    private void deleteProperties(String featureId) {
        getJdbcTemplate().update(getQueryBuilder().deleteAllFeatureCustomProperties(), featureId);
    }

    /**
     * Delete core feature.
     *
     * @param featureId
     *      current feature uid
     */
    private void deleteCoreFeature(String featureId) {
        getJdbcTemplate().update(getQueryBuilder().deleteFeature(), featureId);
    }

    /** {@inheritDoc} */
    @Transactional
    public void grantRoleOnFeature(String uid, String roleName) {
        if (uid == null || uid.isEmpty()) {
            throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
        }
        if (roleName == null || roleName.isEmpty()) {
            throw new IllegalArgumentException("roleName cannot be null nor empty");
        }
        if (!exist(uid)) {
            throw new FeatureNotFoundException(uid);
        }
        getJdbcTemplate().update(getQueryBuilder().addRoleToFeature(), uid, roleName);
    }

    /** {@inheritDoc} */
    @Transactional
    public void removeRoleFromFeature(String uid, String roleName) {
        if (uid == null || uid.isEmpty()) {
            throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
        }
        if (roleName == null || roleName.isEmpty()) {
            throw new IllegalArgumentException("roleName cannot be null nor empty");
        }
        if (!exist(uid)) {
            throw new FeatureNotFoundException(uid);
        }
        getJdbcTemplate().update(getQueryBuilder().deleteFeatureRole(), uid, roleName);
    }

    /** {@inheritDoc} */
    @Transactional
    public void enableGroup(String groupName) {
        if (groupName == null || groupName.isEmpty()) {
            throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
        }
        if (!existGroup(groupName)) {
            throw new GroupNotFoundException(groupName);
        }
        getJdbcTemplate().update(getQueryBuilder().enableGroup(), groupName);
    }

    /** {@inheritDoc} */
    @Transactional
    public void disableGroup(String groupName) {
        if (groupName == null || groupName.isEmpty()) {
            throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
        }
        if (!existGroup(groupName)) {
            throw new GroupNotFoundException(groupName);
        }
        getJdbcTemplate().update(getQueryBuilder().disableGroup(), groupName);
    }

    /** {@inheritDoc} */
    public boolean existGroup(String groupName) {
        if (groupName == null || groupName.isEmpty()) {
            throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
        }
        int count = getJdbcTemplate().queryForObject(getQueryBuilder().existGroup(), Integer.class, groupName);
        return count > 0;
    }

    /** {@inheritDoc} */
    public Map<String, Feature> readGroup(String groupName) {
        if (groupName == null || groupName.isEmpty()) {
            throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
        }
        if (!existGroup(groupName)) {
            throw new GroupNotFoundException(groupName);
        }
        LinkedHashMap<String, Feature> mapFP = new LinkedHashMap<String, Feature>();
        List<Feature> lFp = getJdbcTemplate().query(getQueryBuilder().getFeatureOfGroup(), FMAPPER, groupName);
        for (Feature flipPoint : lFp) {
            mapFP.put(flipPoint.getUid(), flipPoint);
        }
        return mapFP;
    }

    /** {@inheritDoc} */
    @Transactional
    public void addToGroup(String uid, String groupName) {
        if (uid == null || uid.isEmpty()) {
            throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
        }
        if (groupName == null || groupName.isEmpty()) {
            throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
        }
        if (!exist(uid)) {
            throw new FeatureNotFoundException(uid);
        }
        getJdbcTemplate().update(getQueryBuilder().addFeatureToGroup(), groupName, uid);
    }

    /** {@inheritDoc} */
    @Transactional
    public void removeFromGroup(String uid, String groupName) {
        if (uid == null || uid.isEmpty()) {
            throw new IllegalArgumentException(FEATURE_IDENTIFIER_CANNOT_BE_NULL_NOR_EMPTY);
        }
        if (groupName == null || groupName.isEmpty()) {
            throw new IllegalArgumentException(GROUPNAME_CANNOT_BE_NULL_NOR_EMPTY);
        }
        if (!exist(uid)) {
            throw new FeatureNotFoundException(uid);
        }
        if (!existGroup(groupName)) {
            throw new GroupNotFoundException(groupName);
        }
        getJdbcTemplate().update(getQueryBuilder().addFeatureToGroup(), "", uid);
    }

    /** {@inheritDoc} */
    public Map<String, Feature> readAll() {
        LinkedHashMap<String, Feature> mapFP = new LinkedHashMap<String, Feature>();
        List<Feature> lFp = getJdbcTemplate().query(getQueryBuilder().getAllFeatures(), FMAPPER);
        for (Feature flipPoint : lFp) {
            mapFP.put(flipPoint.getUid(), flipPoint);
        }
        // Populating Roles
        RoleRowMapper rrm = new RoleRowMapper();
        getJdbcTemplate().query(getQueryBuilder().getAllRoles(), rrm);
        Map<String, Set<String>> roles = rrm.getRoles();
        for (Map.Entry<String, Set<String>> featId : roles.entrySet()) {
            if (mapFP.containsKey(featId.getKey())) {
                mapFP.get(featId.getKey()).getPermissions().addAll(featId.getValue());
            }
        }
        return mapFP;
    }

    /** {@inheritDoc} */
    public Set<String> readAllGroups() {
        Set<String> setOfGroup = new HashSet<String>();
        setOfGroup.addAll(
                getJdbcTemplate().query(getQueryBuilder().getAllGroups(), new SingleColumnRowMapper<String>()));
        setOfGroup.remove(null);
        setOfGroup.remove("");
        return setOfGroup;
    }

    /** {@inheritDoc} */
    @Transactional
    public void update(Feature newFeature) {
        Util.assertNotNull(newFeature);
        delete(newFeature.getUid());
        create(newFeature);
    }

    /** {@inheritDoc} */
    @Transactional
    public void clear() {
        getJdbcTemplate().update(getQueryBuilder().deleteAllRoles());
        getJdbcTemplate().update(getQueryBuilder().deleteAllCustomProperties());
        getJdbcTemplate().update(getQueryBuilder().deleteAllFeatures());
    }

    /**
     * @param dataSource
     *            the dataSource to set
     */
    @Required
    public void setDataSource(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    /**
     * Getter accessor for attribute 'jdbcTemplate'.
     * 
     * @return current value of 'jdbcTemplate'
     */
    public JdbcTemplate getJdbcTemplate() {
        if (jdbcTemplate == null) {
            if (dataSource == null) {
                throw new IllegalStateException(
                        "ff4j-jdbc: DatabaseStore has not been properly initialized, datasource is null");
            }
            this.jdbcTemplate = new JdbcTemplate(dataSource);
        }
        return jdbcTemplate;
    }

    /**
    * @return the queryBuilder
    */
    public JdbcQueryBuilder getQueryBuilder() {
        if (queryBuilder == null) {
            queryBuilder = new JdbcQueryBuilder();
        }
        return queryBuilder;
    }

    /**
     * @param queryBuilder the queryBuilder to set
     */
    public void setQueryBuilder(JdbcQueryBuilder queryBuilder) {
        this.queryBuilder = queryBuilder;
    }

}