001/* 002 * (C) Copyright 2006-2009 Nuxeo SAS (http://nuxeo.com/) and contributors. 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the GNU Lesser General Public License 006 * (LGPL) version 2.1 which accompanies this distribution, and is available at 007 * http://www.gnu.org/licenses/lgpl.html 008 * 009 * This library is distributed in the hope that it will be useful, 010 * but WITHOUT ANY WARRANTY; without even the implied warranty of 011 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 012 * Lesser General Public License for more details. 013 * 014 * Contributors: 015 * Nuxeo - initial API and implementation 016 * 017 * $Id$ 018 */ 019 020package org.nuxeo.ecm.platform.web.common.requestcontroller.service; 021 022import java.util.HashMap; 023import java.util.LinkedHashMap; 024import java.util.Map; 025import java.util.regex.Matcher; 026import java.util.regex.Pattern; 027 028import javax.servlet.FilterConfig; 029import javax.servlet.http.HttpServletRequest; 030 031import org.apache.commons.logging.Log; 032import org.apache.commons.logging.LogFactory; 033import org.nuxeo.runtime.model.ComponentInstance; 034import org.nuxeo.runtime.model.DefaultComponent; 035 036/** 037 * Runtime component that implements the {@link RequestControllerManager} interface. Contains both the Extension point 038 * logic and the service implementation. 039 * 040 * @author tiry 041 */ 042public class RequestControllerService extends DefaultComponent implements RequestControllerManager { 043 044 public static final String FILTER_CONFIG_EP = "filterConfig"; 045 046 public static final String CORS_CONFIG_EP = "corsConfig"; 047 048 /** 049 * @since 6.0 050 */ 051 public static final String HEADERS_CONFIG_EP = "responseHeaders"; 052 053 private static final Log log = LogFactory.getLog(RequestControllerService.class); 054 055 protected static final Map<String, FilterConfigDescriptor> grantPatterns = new LinkedHashMap<String, FilterConfigDescriptor>(); 056 057 protected static final Map<String, FilterConfigDescriptor> denyPatterns = new LinkedHashMap<String, FilterConfigDescriptor>(); 058 059 // @GuardedBy("itself") 060 protected static final Map<String, RequestFilterConfig> configCache = new LRUCachingMap<String, RequestFilterConfig>( 061 250); 062 063 protected static final Map<String, FilterConfig> filterConfigCache = new LRUCachingMap<>(250); 064 065 protected static final NuxeoCorsFilterDescriptorRegistry corsFilterRegistry = new NuxeoCorsFilterDescriptorRegistry(); 066 067 protected static final NuxeoHeaderDescriptorRegistry headersRegistry = new NuxeoHeaderDescriptorRegistry(); 068 069 protected Map<String, String> headersCache; 070 071 @Override 072 public void registerContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 073 if (FILTER_CONFIG_EP.equals(extensionPoint)) { 074 FilterConfigDescriptor desc = (FilterConfigDescriptor) contribution; 075 registerFilterConfig(desc); 076 } else if (CORS_CONFIG_EP.equals(extensionPoint)) { 077 corsFilterRegistry.addContribution((NuxeoCorsFilterDescriptor) contribution); 078 } else if (HEADERS_CONFIG_EP.equals(extensionPoint)) { 079 headersRegistry.addContribution((NuxeoHeaderDescriptor) contribution); 080 } else { 081 log.error("Unknown ExtensionPoint " + extensionPoint); 082 } 083 } 084 085 public void registerFilterConfig(String name, String pattern, boolean grant, boolean tx, boolean sync, 086 boolean cached, boolean isPrivate, String cacheTime) { 087 FilterConfigDescriptor desc = new FilterConfigDescriptor(name, pattern, grant, tx, sync, cached, isPrivate, 088 cacheTime); 089 registerFilterConfig(desc); 090 } 091 092 public void registerFilterConfig(FilterConfigDescriptor desc) { 093 if (desc.isGrantRule()) { 094 grantPatterns.put(desc.getName(), desc); 095 log.debug("Registered grant filter config"); 096 } else { 097 denyPatterns.put(desc.getName(), desc); 098 log.debug("Registered deny filter config"); 099 } 100 } 101 102 @Override 103 public void unregisterContribution(Object contribution, String extensionPoint, ComponentInstance contributor) { 104 if (CORS_CONFIG_EP.equals(extensionPoint)) { 105 corsFilterRegistry.removeContribution((NuxeoCorsFilterDescriptor) contribution); 106 } 107 } 108 109 /* Service interface */ 110 111 @Override 112 public FilterConfig getCorsConfigForRequest(HttpServletRequest request) { 113 String uri = request.getRequestURI(); 114 FilterConfig filterConfig = null; 115 synchronized (filterConfigCache) { 116 filterConfig = filterConfigCache.get(uri); 117 } 118 119 if (filterConfig == null) { 120 filterConfig = computeCorsFilterConfigForUri(uri); 121 synchronized (filterConfigCache) { 122 filterConfigCache.put(uri, filterConfig); 123 } 124 } 125 126 return filterConfig; 127 } 128 129 public FilterConfig computeCorsFilterConfigForUri(String uri) { 130 NuxeoCorsFilterDescriptor descriptor = corsFilterRegistry.getFirstMatchingDescriptor(uri); 131 return descriptor != null ? descriptor.buildFilterConfig() : null; 132 } 133 134 public RequestFilterConfig getConfigForRequest(HttpServletRequest request) { 135 String uri = request.getRequestURI(); 136 RequestFilterConfig config = null; 137 138 synchronized (configCache) { 139 config = configCache.get(uri); 140 } 141 if (config == null) { 142 config = computeConfigForRequest(uri); 143 synchronized (configCache) { 144 configCache.put(uri, config); 145 } 146 } 147 return config; 148 } 149 150 public RequestFilterConfig computeConfigForRequest(String uri) { 151 // handle deny patterns 152 for (FilterConfigDescriptor desc : denyPatterns.values()) { 153 Pattern pat = desc.getCompiledPattern(); 154 Matcher m = pat.matcher(uri); 155 if (m.matches()) { 156 return new RequestFilterConfigImpl(false, false, false, false, false, ""); 157 } 158 } 159 160 // handle grant patterns 161 for (FilterConfigDescriptor desc : grantPatterns.values()) { 162 Pattern pat = desc.getCompiledPattern(); 163 Matcher m = pat.matcher(uri); 164 if (m.matches()) { 165 return new RequestFilterConfigImpl(desc.useSync(), desc.useTx(), desc.useTxBuffered(), desc.isCached(), 166 desc.isPrivate(), desc.getCacheTime()); 167 } 168 } 169 170 // return deny by default 171 return new RequestFilterConfigImpl(false, false, false, false, false, ""); 172 } 173 174 public Map<String, String> getResponseHeaders() { 175 if (headersCache == null) { 176 headersCache = buildHeadersCache(); 177 } 178 return headersCache; 179 } 180 181 protected static Map<String, String> buildHeadersCache() { 182 Map<String, String> headersCache = new HashMap<String, String>(); 183 for (NuxeoHeaderDescriptor header : headersRegistry.descs.values()) { 184 headersCache.put(header.name, header.getValue()); 185 } 186 return headersCache; 187 } 188}