/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.portals.applications.webcontent.proxy.impl;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.collections.map.LRUMap;
import org.apache.commons.lang.StringUtils;
import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapper;
import org.apache.portals.applications.webcontent.proxy.HttpReverseProxyPathMapperProvider;
import org.apache.portals.applications.webcontent.rewriter.RewriterController;
import org.apache.portals.applications.webcontent.rewriter.rules.Ruleset;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default <CODE>HttpReverseProxyPathMapperProvider</CODE> implementation
*
* @version $Id: DefaultHttpReverseProxyPathMapperProviderImpl.java 833059 2009-11-05 15:38:39Z woonsan $
*/
public class DefaultHttpReverseProxyPathMapperProviderImpl implements HttpReverseProxyPathMapperProvider
{
private static String [] GLOB_EXPR_SEARCHES = { "*", ".", "/", "?", "+" };
private static String [] GLOB_EXPR_REPLACES = { "([^\\/]+)", "\\.", "\\/", "\\?", "\\+" };
private static String [] GROUP_REF_SEARCHES = { "$1", "$2", "$3", "$4", "$5", "$6", "$7", "$8", "$9" };
private static String [] GROUP_REF_REPLACES = { "([^\\/]+)", "([^\\/]+)", "([^\\/]+)", "([^\\/]+)", "([^\\/]+)",
"([^\\/]+)", "([^\\/]+)", "([^\\/]+)", "([^\\/]+)" };
private static Logger log = LoggerFactory.getLogger(DefaultHttpReverseProxyPathMapperProviderImpl.class);
/**
* Maximum matching path part count
*/
private int maxMatchingPathPartCount = 2;
/**
* Static proxy path mappers
*/
private Map<String, HttpReverseProxyPathMapper> staticProxyPathMappersMap;
/**
* Dynamic proxy path mappers
*/
private Map dynamicProxyPathMappersMap;
/**
* Maximum size of dynamic proxy path map
*/
private int dynamicProxyPathMapperCacheCount;
/**
* Proxy path Glob mappers
*/
private List<HttpReverseProxyPathMapper> proxyPathGlobMappers;
/**
* rewriter controller mappers
*/
private Map<HttpReverseProxyPathMapper, RewriterController> rewriterControllerMap;
/**
* rewriter rulesets
*/
private Map<HttpReverseProxyPathMapper, Ruleset> rewriterRulesetMap;
private Map<HttpReverseProxyPathMapper, Pattern> localBasePathGlobPatternMap;
private Map<HttpReverseProxyPathMapper, Pattern> remoteBaseURLGlobPatternMap;
public DefaultHttpReverseProxyPathMapperProviderImpl(List<HttpReverseProxyPathMapper> proxyPathMappers,
Map<HttpReverseProxyPathMapper, RewriterController> rewriterControllerMap,
Map<HttpReverseProxyPathMapper, Ruleset> rewriterRulesetMap)
{
this(proxyPathMappers, 1000, rewriterControllerMap, rewriterRulesetMap);
}
public DefaultHttpReverseProxyPathMapperProviderImpl(List<HttpReverseProxyPathMapper> proxyPathMappers,
int dynamicProxyPathMapperCacheCount,
Map<HttpReverseProxyPathMapper, RewriterController> rewriterControllerMap,
Map<HttpReverseProxyPathMapper, Ruleset> rewriterRulesetMap)
{
this.dynamicProxyPathMapperCacheCount = dynamicProxyPathMapperCacheCount;
this.rewriterControllerMap = rewriterControllerMap;
this.rewriterRulesetMap = rewriterRulesetMap;
this.staticProxyPathMappersMap = new HashMap<String, HttpReverseProxyPathMapper>();
this.dynamicProxyPathMappersMap = Collections.synchronizedMap(new LRUMap(this.dynamicProxyPathMapperCacheCount));
this.proxyPathGlobMappers = new ArrayList<HttpReverseProxyPathMapper>();
this.localBasePathGlobPatternMap = Collections.synchronizedMap(new HashMap<HttpReverseProxyPathMapper, Pattern>());
this.remoteBaseURLGlobPatternMap = Collections.synchronizedMap(new HashMap<HttpReverseProxyPathMapper, Pattern>());
for (HttpReverseProxyPathMapper proxyPathMapper : proxyPathMappers)
{
String localBasePath = proxyPathMapper.getLocalBasePath();
String remoteBaseURL = proxyPathMapper.getRemoteBaseURL();
if (!StringUtils.contains(proxyPathMapper.getLocalBasePath(), '*'))
{
staticProxyPathMappersMap.put(StringUtils.removeEnd(localBasePath, "/"), proxyPathMapper);
staticProxyPathMappersMap.put(StringUtils.removeEnd(remoteBaseURL, "/"), proxyPathMapper);
}
else
{
this.proxyPathGlobMappers.add(proxyPathMapper);
try
{
String expr = StringUtils.replaceEach(proxyPathMapper.getLocalBasePath(), GLOB_EXPR_SEARCHES, GLOB_EXPR_REPLACES);
localBasePathGlobPatternMap.put(proxyPathMapper, Pattern.compile(expr));
expr = StringUtils.replaceEach(proxyPathMapper.getRemoteBaseURL(), GLOB_EXPR_SEARCHES, GLOB_EXPR_REPLACES);
expr = StringUtils.replaceEach(expr, GROUP_REF_SEARCHES, GROUP_REF_REPLACES);
remoteBaseURLGlobPatternMap.put(proxyPathMapper, Pattern.compile(expr));
}
catch (Exception e)
{
if (log.isDebugEnabled())
{
log.error("Failed to create regular expression pattern for " + proxyPathMapper.getLocalBasePath(), e);
}
else
{
log.error("Failed to create regular expression pattern for {}. {}", proxyPathMapper.getLocalBasePath(), e);
}
}
}
}
}
public void setMaxMatchingPathPartCount(int maxMatchingPathPartCount)
{
this.maxMatchingPathPartCount = maxMatchingPathPartCount;
}
public int getMaxMatchingPathPartCount()
{
return maxMatchingPathPartCount;
}
public HttpReverseProxyPathMapper findMapper(String pathInfo)
{
String [] pathParts = StringUtils.split(pathInfo, "/", maxMatchingPathPartCount + 1);
int pathPartCount = (pathParts != null ? pathParts.length : 0);
if (pathPartCount == 0)
{
return null;
}
for (int i = Math.min(pathPartCount, maxMatchingPathPartCount); i > 0; i--)
{
String localBasePathKey = "/" + StringUtils.join(pathParts, "/", 0, i);
HttpReverseProxyPathMapper proxyPathMapper = staticProxyPathMappersMap.get(localBasePathKey);
if (proxyPathMapper == null)
{
proxyPathMapper = (HttpReverseProxyPathMapper) dynamicProxyPathMappersMap.get(localBasePathKey);
}
if (proxyPathMapper != null)
{
return proxyPathMapper;
}
}
if (!proxyPathGlobMappers.isEmpty())
{
for (HttpReverseProxyPathMapper proxyPathMapper : proxyPathGlobMappers)
{
Pattern pattern = localBasePathGlobPatternMap.get(proxyPathMapper);
if (pattern == null)
{
continue;
}
Matcher matcher = pattern.matcher(pathInfo);
if (matcher.lookingAt())
{
int groupCount = matcher.groupCount();
String [] replaces = new String[groupCount];
String [] searches = new String[groupCount];
for (int i = 0; i < groupCount; i++)
{
replaces[i] = matcher.group(i + 1);
searches[i] = "$" + (i + 1);
}
//HttpReverseProxyPathMapper cloned = (HttpReverseProxyPathMapper) proxyPathMapper.clone();
String localBasePath = matcher.group(0);
String remoteBaseURL = StringUtils.replaceEach(proxyPathMapper.getRemoteBaseURL(), searches, replaces);
HttpReverseProxyPathMapper derivedMapper =
new DefaultHttpReverseProxyPathMapperImpl(proxyPathMapper.getName() + ":" + localBasePath,
localBasePath,
remoteBaseURL,
proxyPathMapper.getDefaultRequestHeaders(),
proxyPathMapper.getDefaultRequestCookies(),
proxyPathMapper.getRewriteCookiePathIncludes(),
proxyPathMapper.getRewriteCookiePathExcludes());
RewriterController rewriterController = rewriterControllerMap.get(proxyPathMapper);
Ruleset rewriterRules = rewriterRulesetMap.get(proxyPathMapper);
if (rewriterController != null)
{
rewriterControllerMap.put(derivedMapper, rewriterController);
}
if (rewriterRules != null)
{
rewriterRulesetMap.put(derivedMapper, rewriterRules);
}
synchronized (dynamicProxyPathMappersMap)
{
dynamicProxyPathMappersMap.put(StringUtils.removeEnd(localBasePath, "/"), derivedMapper);
dynamicProxyPathMappersMap.put(StringUtils.removeEnd(remoteBaseURL, "/"), derivedMapper);
}
return derivedMapper;
}
}
}
return null;
}
public HttpReverseProxyPathMapper findMapperByRemoteURL(String remoteURL)
{
String [] pathParts = StringUtils.split(remoteURL, "/", maxMatchingPathPartCount + 2);
int pathPartCount = (pathParts != null ? pathParts.length : 0);
if (pathPartCount < 2)
{
return null;
}
String scheme = pathParts[0];
for (int i = Math.min(pathPartCount, maxMatchingPathPartCount + 1); i > 1; i--)
{
String remoteBaseURLKey = scheme + "//" + StringUtils.join(pathParts, "/", 1, i);
HttpReverseProxyPathMapper proxyPathMapper = (HttpReverseProxyPathMapper) staticProxyPathMappersMap.get(remoteBaseURLKey);
if (proxyPathMapper == null)
{
proxyPathMapper = (HttpReverseProxyPathMapper) dynamicProxyPathMappersMap.get(remoteBaseURLKey);
}
if (proxyPathMapper != null)
{
return proxyPathMapper;
}
}
if (!proxyPathGlobMappers.isEmpty())
{
for (HttpReverseProxyPathMapper proxyPathMapper : proxyPathGlobMappers)
{
Pattern pattern = remoteBaseURLGlobPatternMap.get(proxyPathMapper);
if (pattern == null)
{
continue;
}
Matcher matcher = pattern.matcher(remoteURL);
if (matcher.lookingAt())
{
int groupCount = matcher.groupCount();
String [] replaces = new String[groupCount];
String [] searches = new String[groupCount];
for (int i = 0; i < groupCount; i++)
{
replaces[i] = matcher.group(i + 1);
searches[i] = "$" + (i + 1);
}
String remoteBaseURL = matcher.group(0);
String localBasePath = proxyPathMapper.getLocalBasePath();
for (int i = 1; StringUtils.contains(localBasePath, '*'); i++)
{
localBasePath = StringUtils.replaceOnce(localBasePath, "*", "$" + i);
}
localBasePath = StringUtils.replaceEach(localBasePath, searches, replaces);
HttpReverseProxyPathMapper derivedMapper =
new DefaultHttpReverseProxyPathMapperImpl(proxyPathMapper.getName() + ":" + localBasePath,
localBasePath,
remoteBaseURL,
proxyPathMapper.getDefaultRequestHeaders(),
proxyPathMapper.getDefaultRequestCookies(),
proxyPathMapper.getRewriteCookiePathIncludes(),
proxyPathMapper.getRewriteCookiePathExcludes());
RewriterController rewriterController = rewriterControllerMap.get(proxyPathMapper);
Ruleset rewriterRules = rewriterRulesetMap.get(proxyPathMapper);
if (rewriterController != null)
{
rewriterControllerMap.put(derivedMapper, rewriterController);
}
if (rewriterRules != null)
{
rewriterRulesetMap.put(derivedMapper, rewriterRules);
}
synchronized (dynamicProxyPathMappersMap)
{
dynamicProxyPathMappersMap.put(StringUtils.removeEnd(localBasePath, "/"), derivedMapper);
dynamicProxyPathMappersMap.put(StringUtils.removeEnd(remoteBaseURL, "/"), derivedMapper);
}
return derivedMapper;
}
}
}
return null;
}
public RewriterController getRewriterController(HttpReverseProxyPathMapper proxyPathMapper)
{
return rewriterControllerMap.get(proxyPathMapper);
}
public Ruleset getRewriterRuleset(HttpReverseProxyPathMapper proxyPathMapper)
{
return rewriterRulesetMap.get(proxyPathMapper);
}
}