web-dev-qa-db-fra.com

Comment ajouter des filtres à la servlet sans modifier web.xml

J'aimerais pouvoir modifier/configurer les filtres d'une manière différente de web.xml. Voici une configuration statique de 2 filtres. J'aimerais pouvoir avoir un filtre configuré statiquement et permettre à ce filtre de charger des filtres supplémentaires. Je voulais juste savoir si quelqu'un connaissait la lib qui avait déjà ça.

Utilisation de l'API Servlet 2.5

<web-app>
  ...
  <filter>
    <filter-name>MyFilter1</filter-name>
    <filter-class>com.me.MyFilter1</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>MyFilter1</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  ...
  <filter>
    <filter-name>MyFilter2</filter-name>
    <filter-class>com.me.MyFilter2</filter-class>
  </filter>
  <filter-mapping>
    <filter-name>MyFilter2</filter-name>
    <url-pattern>/*</url-pattern>
  </filter-mapping>
  ...
</web-app>

J'ai vu cela fait dans Guice avec GuiceFilter où les filtres sont configurés au moment de l'exécution.

29
TJR

Faites simplement le même travail que le conteneur. C'est à dire. réinventer la roue du modèle de conception chaîne de responsabilité tel qu'il est sous les couvercles utilisés par les filtres de servlet.

public class GodFilter implements Filter {

    private Map<Pattern, Filter> filters = new LinkedHashMap<Pattern, Filter>();

    @Override
    public void init(FilterConfig config) throws ServletException {
        Filter1 filter1 = new Filter1();
        filter1.init(config);
        filters.put(new Pattern("/foo/*"), filter1);

        Filter2 filter2 = new Filter2();
        filter2.init(config);
        filters.put(new Pattern("*.bar"), filter2);

        // ...
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
        HttpServletRequest hsr = (HttpServletRequest) request;
        String path = hsr.getRequestURI().substring(hsr.getContextPath().length());
        GodFilterChain godChain = new GodFilterChain(chain);

        for (Entry<Pattern, Filter> entry : filters.entrySet()) {
            if (entry.getKey().matches(path)) {
                godChain.addFilter(entry.getValue());
            }
        }

        godChain.doFilter(request, response);
    }

    @Override
    public void destroy() {
        for (Filter filter : filters.values()) {
            filter.destroy();
        }
    }

}

avec ces petites classes auxiliaires (qui peuvent si nécessaire être faites private static classes imbriquées des GodFilter ci-dessus):

public class Pattern {

    private int position;
    private String url;

    public Pattern(String url) {
        this.position = url.startsWith("*") ? 1
                      : url.endsWith("*") ? -1
                      : 0;
        this.url = url.replaceAll("/?\\*", "");
    }

    public boolean matches(String path) {
        return (position == -1) ? path.startsWith(url)
             : (position == 1) ? path.endsWith(url)
             : path.equals(url);
    }

}

et

public class GodFilterChain implements FilterChain {

    private FilterChain chain;
    private List<Filter> filters = new ArrayList<Filter>();
    private Iterator<Filter> iterator;

    public GodFilterChain(FilterChain chain) {
        this.chain = chain;
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException {
        if (iterator == null) {
            iterator = filters.iterator();
        }

        if (iterator.hasNext()) {
            iterator.next().doFilter(request, response, this);
        } else {
            chain.doFilter(request, response);
        }
    }

    public void addFilter(Filter filter) {
        if (iterator != null) {
            throw new IllegalStateException();
        }

        filters.add(filter);
    }

}

Si nécessaire, vous pouvez également alimenter un fichier de configuration XML avec tous les filtres possibles afin de vous retrouver avec une configuration plus facile. Vous pouvez utiliser la réflexion pour créer des filtres dans init() de votre GodFilter.

Oh peu importe, c'est ce que le web.xml Et le conteneur font déjà ...

38
BalusC

Servlet 3.0 a le @WebFilter annotation pour définir un filtre. Plus besoin de le déclarer dans web.xml.

Mais le chargement d'un filtre à partir d'un filtre n'est pas pris en charge. Vous pouvez le mettre en œuvre vous-même: c'est "juste" le modèle de chaîne de responsabilité, mais pourquoi le feriez-vous?

15
JB Nizet

Il peut être réalisé en quelques étapes simples, même pour les spécifications de servlet antérieures à la 3.0:

  1. Ajoutez un filtre contenant une collection statique et ordonnée de classes (chaîne).
  2. Mappez le filtre pour intercepter chaque trafic.
  3. Manipulez l'ordre et l'existence de vos classes d'assistance (celles-ci seront appelées en privé par votre filtre lors de l'interception du trafic) dans la chaîne.

Réf: Xstream utilise le même type de modèle pour Serializer, mais pas avec Servlet/Filter. :)

4
Puspendu Banerjee