Java tutorial
/* 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; } } }