Remove ListenableFuture in CustomFilterAdapter
[aaa.git] / aaa-filterchain / src / main / java / org / opendaylight / aaa / filterchain / filters / CustomFilterAdapter.java
1 /*
2  * Copyright (c) 2016 - 2017 Brocade Communications Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8
9 package org.opendaylight.aaa.filterchain.filters;
10
11 import com.google.common.collect.ImmutableList;
12 import java.io.IOException;
13 import java.util.Collections;
14 import java.util.List;
15 import java.util.stream.Collectors;
16 import javax.servlet.Filter;
17 import javax.servlet.FilterChain;
18 import javax.servlet.FilterConfig;
19 import javax.servlet.ServletException;
20 import javax.servlet.ServletRequest;
21 import javax.servlet.ServletResponse;
22 import org.opendaylight.aaa.filterchain.configuration.CustomFilterAdapterConfiguration;
23 import org.opendaylight.aaa.filterchain.configuration.CustomFilterAdapterListener;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26
27 /**
28  * Recreates the Chain of Responsibility pattern for
29  * <code>javax.servlet.Filter</code>(s). Jersey 1.17 does not include the
30  * ability to programmatically add Filter(s), as Filter chains are defined at
31  * compile time within the <code>web.xml</code> file. This Adapter dynamically
32  * adds the capability to dynamically insert links into the filter chain.
33  *
34  * <p>
35  * This Adapter is enabled by placing the <code>CustomFilterAdapter</code> in
36  * the Servlet's <code>web.xml</code> definition (ideally directly after the
37  * <code>AAAFilter</code> Filter, as ordering is honored directly).
38  *
39  * <code>CustomFilterAdapter.doFilter(...)</code> calls
40  * <code>AAAFilterChain.doFilter(...)</code>, which honors the injected filter
41  * chain links, and then continues the original filter chain.
42  *
43  * <p>
44  * This code was designed specifically to work with the common, generic
45  * <code>javax.servlet.Filter</code> interface; thus, certain choices, such as
46  * creating a new <code>AAAFilterChain</code> per request, were necessary to
47  * preserve the existing API contracts (i.e., the injected chain is stored as a
48  * local variable in <code>AAAFilterChain</code> so it may be used in existing
49  * methods (could not be passed as a parameter), and if a new chain was not
50  * spawned each time, there is a risk that the existingChain changes in the
51  * middle of requests, causing inconsistent behavior.
52  */
53 public class CustomFilterAdapter implements Filter, CustomFilterAdapterListener {
54
55     private static final Logger LOG = LoggerFactory.getLogger(CustomFilterAdapter.class);
56
57     private final CustomFilterAdapterConfiguration customFilterAdapterConfig;
58
59     private FilterConfig filterConfig;
60
61     /**
62      * Stores the injected filter chain. TODO can this be an ArrayList?
63      */
64     private volatile List<Filter> injectedFilterChain = Collections.emptyList();
65
66     public CustomFilterAdapter(CustomFilterAdapterConfiguration customFilterAdapterConfig) {
67         this.customFilterAdapterConfig = customFilterAdapterConfig;
68     }
69
70     @Override
71     public void destroy() {
72         LOG.info("Destroying CustomFilterAdapter");
73     }
74
75     @Override
76     public void doFilter(final ServletRequest request, final ServletResponse response, final FilterChain chain)
77             throws IOException, ServletException {
78
79         // chain is the existing chain of responsibility, and filterChain
80         // contains the new links to inject into the existing chain. Since
81         // Jersey spawns <code>chain</code> for each request, a new chain
82         List<Filter> localFilterChain = injectedFilterChain;
83         if (!localFilterChain.isEmpty()) {
84             AAAFilterChain.createAAAFilterChain().doFilter(request, response, chain, localFilterChain);
85         } else {
86             chain.doFilter(request, response);
87         }
88     }
89
90     @Override
91     public void init(final FilterConfig newFilterConfig) throws ServletException {
92         LOG.info("Initializing CustomFilterAdapter");
93
94         this.filterConfig = newFilterConfig;
95
96         // register as a listener for config admin changes
97         customFilterAdapterConfig.registerCustomFilterAdapterConfigurationListener(CustomFilterAdapter.this);
98     }
99
100     /**
101      * Updates the injected filter chain.
102      *
103      * @param filterChain
104      *            The injected chain
105      */
106     private void setInjectedFilterChain(final List<Filter> filterChain) {
107         this.injectedFilterChain = ImmutableList.copyOf(filterChain);
108         final String commaSeperatedFilterChain = this.injectedFilterChain.stream()
109                 .map(i -> i.getClass().getSimpleName()).collect(Collectors.joining(","));
110         LOG.info("Injecting a new filter chain with {} Filters: {}", filterChain.size(), commaSeperatedFilterChain);
111     }
112
113     @Override
114     public void updateInjectedFilters(final List<Filter> injectedFilters) {
115         this.setInjectedFilterChain(injectedFilters);
116     }
117
118     @Override
119     public FilterConfig getFilterConfig() {
120         return this.filterConfig;
121     }
122 }