blob: 50c9f7da365cbee883fb3bfebe041a9fc8571a7d [file] [log] [blame]
/*
* 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.sling.extensions.mdc.internal;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceFactory;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.ConfigurationPolicy;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Modified;
import org.osgi.service.metatype.annotations.AttributeDefinition;
import org.osgi.service.metatype.annotations.Designate;
import org.osgi.service.metatype.annotations.ObjectClassDefinition;
import org.slf4j.MDC;
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.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* Filter is based on ch.qos.logback.classic.helpers.MDCInsertingServletFilter
*/
@Component(immediate = true,
configurationPolicy = ConfigurationPolicy.REQUIRE,
property = {
"pattern=/.*"
})
@Designate(ocd = MDCInsertingFilter.Config.class)
public class MDCInsertingFilter implements Filter {
public static final String REQUEST_REMOTE_HOST_MDC_KEY = "req.remoteHost";
public static final String REQUEST_USER_AGENT_MDC_KEY = "req.userAgent";
public static final String REQUEST_REQUEST_URI = "req.requestURI";
public static final String REQUEST_QUERY_STRING = "req.queryString";
public static final String REQUEST_REQUEST_URL = "req.requestURL";
public static final String REQUEST_X_FORWARDED_FOR = "req.xForwardedFor";
private static final String[] DEFAULT_KEY_NAMES = {
REQUEST_REMOTE_HOST_MDC_KEY,
REQUEST_USER_AGENT_MDC_KEY,
REQUEST_REQUEST_URI,
REQUEST_QUERY_STRING,
REQUEST_REQUEST_URL,
REQUEST_X_FORWARDED_FOR
};
private Set<String> keyNames = new CopyOnWriteArraySet<>();
private Set<String> headerNames = new CopyOnWriteArraySet<>();
private Set<String> parameterNames = new CopyOnWriteArraySet<>();
private Set<String> cookieNames = new CopyOnWriteArraySet<>();
private ServiceRegistration filterReg;
@ObjectClassDefinition(name = "%mdc.label", description = "%mdc.description")
public @interface Config {
@AttributeDefinition
String[] headers() default {};
@AttributeDefinition
String[] parameters() default {};
@AttributeDefinition
String[] cookies() default {};
}
public void init(FilterConfig filterConfig) throws ServletException {
}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
insertIntoMDC(request);
try {
chain.doFilter(request, response);
} finally {
clearMDC();
}
}
public void destroy() {
}
private void insertIntoMDC(ServletRequest request) {
nullSafePut(REQUEST_REMOTE_HOST_MDC_KEY, request.getRemoteHost());
if (request instanceof HttpServletRequest) {
HttpServletRequest httpRequest = (HttpServletRequest) request;
nullSafePut(REQUEST_REQUEST_URI, httpRequest.getRequestURI());
StringBuffer requestURL = httpRequest.getRequestURL();
if (requestURL != null) {
nullSafePut(REQUEST_REQUEST_URL, requestURL.toString());
}
nullSafePut(REQUEST_QUERY_STRING, httpRequest.getQueryString());
nullSafePut(REQUEST_USER_AGENT_MDC_KEY, httpRequest.getHeader("User-Agent"));
nullSafePut(REQUEST_X_FORWARDED_FOR, httpRequest.getHeader("X-Forwarded-For"));
for(String paramName : parameterNames){
nullSafePut(paramName,httpRequest.getParameter(paramName));
}
for(String headerName :headerNames){
nullSafePut(headerName, httpRequest.getHeader(headerName));
}
Cookie[] cookies = httpRequest.getCookies();
if(cookies != null){
for(Cookie c : cookies){
if(cookieNames.contains(c.getName())){
nullSafePut(c.getName(),c.getValue());
}
}
}
}
}
private void clearMDC() {
for (String key : keyNames) {
MDC.remove(key);
}
}
@Activate
private void activate(BundleContext context, Config config) {
final Dictionary<String, Object> properties = new Hashtable<>();
properties.put("filter.scope", "REQUEST");
//The MDC Filter might be running in a non Sling container. Hence, to avoid
//direct dependency on Sling we use a ServiceFactory
filterReg = context.registerService(Filter.class.getName(),new ServiceFactory() {
private Object instance;
public synchronized Object getService(Bundle bundle, ServiceRegistration serviceRegistration) {
if(instance == null){
instance = new SlingMDCFilter();
}
return instance;
}
public void ungetService(Bundle bundle, ServiceRegistration serviceRegistration, Object o) {
}
}, properties);
modified(config);
}
@Modified
private void modified(Config config){
Set<String> headers = new HashSet<>(Arrays.asList(config.headers()));
headerNames.clear();
headerNames.addAll(headers);
Set<String> cookies = new HashSet<>(Arrays.asList(config.cookies()));
cookieNames.clear();
cookieNames.addAll(cookies);
Set<String> params = new HashSet<>(Arrays.asList(config.parameters()));
parameterNames.clear();
parameterNames.addAll(params);
List<String> keyList = new ArrayList<>();
keyList.addAll(headerNames);
keyList.addAll(cookieNames);
keyList.addAll(parameterNames);
keyList.addAll(Arrays.asList(DEFAULT_KEY_NAMES));
this.keyNames.clear();
this.keyNames.addAll(keyList);
}
@Deactivate
private void deactivate(){
if(filterReg != null){
filterReg.unregister();
}
}
private void nullSafePut(String key,String value){
if(key != null && value != null){
MDC.put(key,value);
}
}
}