/****************************************************************************** * JBoss, a division of Red Hat * * Copyright 2011, Red Hat Middleware, LLC, and individual * * contributors as indicated by the @authors tag. See the * * copyright.txt in the distribution for a full listing of * * individual contributors. * * * * This is free software; you can redistribute it and/or modify it * * under the terms of the GNU Lesser General Public License as * * published by the Free Software Foundation; either version 2.1 of * * the License, or (at your option) any later version. * * * * This software 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 * * Lesser General Public License for more details. * * * * You should have received a copy of the GNU Lesser General Public * * License along with this software; if not, write to the Free * * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * * 02110-1301 USA, or see the FSF site: http://www.fsf.org. * ******************************************************************************/ package org.gatein.web.redirect; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.regex.PatternSyntaxException; import org.exoplatform.portal.config.model.DevicePropertyCondition; import org.exoplatform.portal.config.model.PortalRedirect; import org.exoplatform.portal.config.model.RedirectCondition; import org.exoplatform.portal.config.model.UserAgentConditions; import org.gatein.common.logging.Logger; import org.gatein.common.logging.LoggerFactory; import org.gatein.web.redirect.api.RedirectKey; import org.gatein.web.redirect.api.RedirectType; /** * Determines what site, if any, should be used for a redirect. Bases this decisions on the portal's redirect conditions and the * device properties available. * * TODO: create an interface for this and configure the service using the kernel TODO: update the tests for this to use * portal.xml files instead of creating the configurations manually in code * * @author <a href="mailto:mwringe@redhat.com">Matt Wringe</a> * @version $Revision$ */ public class Redirector { protected static Logger log = LoggerFactory.getLogger(Redirector.class); public RedirectKey getRedirectSite(ArrayList<PortalRedirect> portalRedirects, String userAgentString, Map<String, String> deviceProperties) { if (userAgentString == null) { // if the uas is null, then just transform it into 'null'. This allows the admin to configure how to handle the // 'null' the null case easily. // since the uas is user modifiable, setting it to whatever we want here will not cause any problems. userAgentString = "null"; } boolean needsBrowserDetection = false; if (portalRedirects != null && !portalRedirects.isEmpty()) { for (PortalRedirect redirect : portalRedirects) { if (redirect.isEnabled()) { RedirectType redirectCondition = checkConditions(userAgentString, deviceProperties, redirect.getConditions()); if (redirectCondition == RedirectType.REDIRECT) { log.debug("Found a match with UAS " + userAgentString + " and DeviceProperties " + deviceProperties + ". Setting redirect to : " + redirect.getRedirectSite()); return RedirectKey.redirect(redirect.getRedirectSite()); } else if (redirectCondition == RedirectType.NEEDDEVICEINFO) { needsBrowserDetection = true; } } } if (needsBrowserDetection == false) { log.debug("Could not find a match with the specified uas and device properties. Returning NO_REDIRECT_DETECTED"); return RedirectKey.noRedirect(); } else { log.debug("Found a match with the specified uas but it requires device properties. Returning NO_REDIRECT_DETECTED"); return RedirectKey.needDeviceInfo(); } } else { log.debug("No UserAgentString specified and no device properties. Returning NO_REDIRECT_DETECTED"); return RedirectKey.noRedirect(); } } protected RedirectType checkConditions(String userAgentString, Map<String, String> deviceProperties, List<RedirectCondition> conditions) { log.debug("Checking conditions for redirect with " + userAgentString + " and device properties " + deviceProperties); if (conditions != null) { for (RedirectCondition condition : conditions) { boolean userAgentStringMatch = checkUserAgentStrings(userAgentString, condition.getUserAgentConditions()); log.debug("UserAgentStringMatch : " + userAgentStringMatch); if (userAgentStringMatch == true) { if (condition.getDeviceProperties() == null || condition.getDeviceProperties().isEmpty()) { log.debug("UserAgentStringMatch and no device detection has been specified. Using Redirect"); return RedirectType.REDIRECT; } else { if (deviceProperties == null) { log.debug("Conditional device properties exists, but no deviceProperties available. Using Browser Detection"); return RedirectType.NEEDDEVICEINFO; } else { boolean devicePropertiesMatch = checkDeviceProperties(deviceProperties, condition.getDeviceProperties()); if (devicePropertiesMatch == true) { log.debug("UserAgentStringMatch and device properties match. Using Redirect"); return RedirectType.REDIRECT; } else { return RedirectType.NOREDIRECT; } } } } } } return RedirectType.NOREDIRECT; } protected boolean checkDeviceProperties(Map<String, String> deviceProperties, List<DevicePropertyCondition> conditions) { if (conditions != null && !conditions.isEmpty()) { if (deviceProperties == null) { return false; } for (DevicePropertyCondition deviceProperty : conditions) { if (deviceProperties.containsKey(deviceProperty.getPropertyName())) { if (!checkProperty(deviceProperties.get(deviceProperty.getPropertyName()), deviceProperty)) { return false; } } else { return false; } } } return true; // if the deviceProperties are null or empty, then the condition is matched } protected boolean checkProperty(String propertyValue, DevicePropertyCondition deviceProperty) { if (deviceProperty.getGreaterThan() != null) { try { Float propertyValueFloat = Float.parseFloat(propertyValue); if (propertyValueFloat <= deviceProperty.getGreaterThan()) { return false; } } catch (NumberFormatException nfe) { log.debug("Encountered a NumberFormatException trying to parse a property value (" + propertyValue + ") which should be a float."); return false; } } if (deviceProperty.getLessThan() != null) { try { Float propertyValueFloat = Float.parseFloat(propertyValue); if (propertyValueFloat >= deviceProperty.getLessThan()) { return false; } } catch (NumberFormatException nfe) { log.debug("Encountered a NumberFormatException trying to parse a property value (" + propertyValue + ") which should be a float."); return false; } } if (deviceProperty.getEquals() != null) { if (!propertyValue.equals(deviceProperty.getEquals())) { return false; } } if (deviceProperty.getMatches() != null) { try { Pattern pattern = Pattern.compile(deviceProperty.getMatches()); if (propertyValue == null) { propertyValue = ""; } Matcher matcher = pattern.matcher(propertyValue); if (!matcher.find()) { return false; } } catch (PatternSyntaxException pse) { log.debug("Encountered a PatternSyntaxException trying to compile a device property pattern (" + deviceProperty.getMatches() + ") which should be a valid string for a pattern."); return false; } } return true; } /** * Determines if the current user agent string passes the redirect conditions. * * @param userAgentString The user agent string * @param condition The conditions to check against * @return True if the user agent string passes the conditions */ protected boolean checkUserAgentStrings(String userAgentString, UserAgentConditions condition) { // Check the black list first for user agent string matches, fail immediately if any match if (condition.getDoesNotContain() != null && !condition.getDoesNotContain().isEmpty()) { if (userAgentContains(userAgentString, condition.getDoesNotContain())) { return false; } } if (condition.getContains() != null && !condition.getContains().isEmpty()) { return userAgentContains(userAgentString, condition.getContains()); } else { // if we have no contains or contains is empty, we can never match to this return false; } } protected boolean userAgentContains(String userAgentString, List<String> contains) { if (userAgentString != null) { for (String contain : contains) { Pattern pattern = Pattern.compile(contain); Matcher matcher = pattern.matcher(userAgentString); if (matcher.find()) { return true; } } } return false; } }