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