com.tacitknowledge.flip.servlet.FlipOverrideFilter.java Source code

Java tutorial

Introduction

Here is the source code for com.tacitknowledge.flip.servlet.FlipOverrideFilter.java

Source

/* Copyright 2012 Tacit Knowledge
*
* 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.
*/
package com.tacitknowledge.flip.servlet;

import java.io.IOException;
import java.util.Map;
import java.util.regex.Pattern;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import com.google.common.base.Predicate;
import com.google.common.collect.Maps;
import com.tacitknowledge.flip.model.FeatureDescriptor;
import com.tacitknowledge.flip.model.FeatureState;
import com.tacitknowledge.flip.properties.FeatureDescriptorsMap;

/**
 * Filter used to override a specific toggle for the current session.
 * <p>
 * To override a toggle, add the toggle override prefix ("flip" by default), followed by a period
 * and the feature name, and set it to either 'enabled' or 'disabled'. For example, to turn off the
 * "self-destruct" toggle, add "flip.self-destruct=disabled" to the URL.
 * </p>
 * <p>
 * The override prefix can be set via the "request-param-prefix" init value. The default is "flip".
 * </p>
 *
 * @author Serghei Soloviov <ssoloviov@tacitknowledge.com>
 * @author Petric Coroli <pcoroli@tacitknowledge.com>
 */
public class FlipOverrideFilter implements Filter {
    public static final String SESSION_FEATURES_KEY = "FLIP_SESSION_FEATURES";

    private static final String DEFAULT_REQUEST_PARAM_PREFIX = "flip.";

    private static final String REQUEST_PARAM_PREFIX_CONFIG_KEY = "request-param-prefix";

    private String requestParameterPrefix = DEFAULT_REQUEST_PARAM_PREFIX;

    public void init(final FilterConfig filterConfig) throws ServletException {
        String prefix = filterConfig.getInitParameter(REQUEST_PARAM_PREFIX_CONFIG_KEY);
        if (prefix != null && prefix.trim().length() > 0) {
            requestParameterPrefix = prefix.trim() + ".";
        }
    }

    /**
     * {@inheritDoc }
     */
    public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
            throws IOException, ServletException {
        applyFeaturesFromRequestParameters(request);

        chain.doFilter(request, response);
        FlipWebContext.clear();
    }

    /**
     * {@inheritDoc }
     */
    public void destroy() {

    }

    /**
     * The method which uses the request parameters and apply them into the session
     * attribute {@link #SESSION_FEATURES_KEY}. This session attribute holds the
     * map {@link FeatureDescriptorsMap}. The method do not creates the session. So
     * if the session do not exists then the request parameters will be ignored.
     * If in the session under key {@link #SESSION_FEATURES_KEY} persists the 
     * object other than of type {@link FeatureDescriptorsMap} or there is
     * no object in the session then the new object will be created and stored there. 
     * 
     * @param request the request to process 
     */
    private void applyFeaturesFromRequestParameters(final ServletRequest request) {
        final HttpServletRequest httpRequest = (HttpServletRequest) request;
        final HttpSession session = httpRequest.getSession(false);

        FeatureDescriptorsMap featureDescriptors;
        if (session != null) {
            featureDescriptors = getSafeSessionFeatures(session);
            session.setAttribute(SESSION_FEATURES_KEY, featureDescriptors);
        } else {
            featureDescriptors = new FeatureDescriptorsMap();
        }

        applyRequestFeaturesToFeatureDescriptorsMap(request, featureDescriptors);
        FlipWebContext.setFeatureDescriptors(featureDescriptors);
    }

    /**
     * Returns the {@link FeatureDescriptorsMap} object stored in the session
     * under {@link #SESSION_FEATURES_KEY} key. If there is no such object or the 
     * object is of another type the new object will be created. It do not stores
     * the object into the session back.
     * 
     * @param session the session object where to extract the {@link FeatureDescriptorsMap}
     * @return the {@link FeatureDescriptorsMap} stored in the session or new one created on other cases.
     */
    private FeatureDescriptorsMap getSafeSessionFeatures(final HttpSession session) {
        final Object sessionObject = session.getAttribute(SESSION_FEATURES_KEY);

        if (sessionObject != null && sessionObject instanceof FeatureDescriptorsMap) {
            return (FeatureDescriptorsMap) sessionObject;
        }

        return new FeatureDescriptorsMap();
    }

    /**
     * Moves the features descriptors generated on request parameters to the {@link FeatureDescriptorsMap}.
     * 
     * @param request the request where to extract the request parameters.
     * @param featureDescriptors the {@link FeatureDescriptorsMap} where to store the {@link FeatureDescriptor} objects.
     */
    private void applyRequestFeaturesToFeatureDescriptorsMap(final ServletRequest request,
            final FeatureDescriptorsMap featureDescriptors) {
        final Map<String, FeatureDescriptor> transformedParamMap = Maps.transformEntries(request.getParameterMap(),
                new RequestParametersTransformer());
        final Map<String, FeatureDescriptor> parameterMap = Maps.filterEntries(transformedParamMap,
                new RequestParametersFilter());

        for (final FeatureDescriptor descriptor : parameterMap.values()) {
            featureDescriptors.put(descriptor.getName(), descriptor);
        }
    }

    private class RequestParametersTransformer
            implements Maps.EntryTransformer<String, String[], FeatureDescriptor> {
        public FeatureDescriptor transformEntry(final String key, final String[] value) {
            if (value == null || value.length == 0) {
                return null;
            }

            try {
                final FeatureState state = FeatureState.valueOf(value[0].toUpperCase());

                final FeatureDescriptor featureDescriptor = new FeatureDescriptor();
                featureDescriptor.setName(key.replaceFirst("^" + Pattern.quote(requestParameterPrefix), ""));
                featureDescriptor.setState(state);
                return featureDescriptor;
            } catch (final IllegalArgumentException e) {
                return null;
            }
        }
    }

    private class RequestParametersFilter implements Predicate<Map.Entry<String, FeatureDescriptor>> {
        public boolean apply(final Map.Entry<String, FeatureDescriptor> input) {
            return input.getKey().startsWith(requestParameterPrefix) && input.getValue() != null;
        }
    }
}