/*
 *  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.tomcat.util.scan;

import java.util.HashSet;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import org.apache.tomcat.JarScanFilter;
import org.apache.tomcat.JarScanType;
import org.apache.tomcat.util.file.Matcher;

public class StandardJarScanFilter implements JarScanFilter {

    private final ReadWriteLock configurationLock =
            new ReentrantReadWriteLock();

    private static final String defaultSkip;
    private static final String defaultScan;
    private static final Set<String> defaultSkipSet = new HashSet<>();
    private static final Set<String> defaultScanSet = new HashSet<>();

    static {
        // Initialize defaults. There are no setter methods for them.
        defaultSkip = System.getProperty(Constants.SKIP_JARS_PROPERTY);
        populateSetFromAttribute(defaultSkip, defaultSkipSet);
        defaultScan = System.getProperty(Constants.SCAN_JARS_PROPERTY);
        populateSetFromAttribute(defaultScan, defaultScanSet);
    }

    private String tldSkip;
    private String tldScan;
    private final Set<String> tldSkipSet;
    private final Set<String> tldScanSet;
    private boolean defaultTldScan = true;

    private String pluggabilitySkip;
    private String pluggabilityScan;
    private final Set<String> pluggabilitySkipSet;
    private final Set<String> pluggabilityScanSet;
    private boolean defaultPluggabilityScan = true;

    
This is the standard implementation of JarScanFilter. By default, the following filtering rules are used:
  • JARs that match neither the skip nor the scan list will be included in scan results.
  • JARs that match the skip list but not the scan list will be excluded from scan results.
  • JARs that match the scan list will be included from scan results.
The default skip list and default scan list are obtained from the system properties Constants.SKIP_JARS_PROPERTY and Constants.SCAN_JARS_PROPERTY respectively. These default values may be over-ridden for the JarScanType.TLD and JarScanType.PLUGGABILITY scans. The filtering rules may also be modified for these scan types using setDefaultTldScan(boolean) and setDefaultPluggabilityScan(boolean). If set to false, the following filtering rules are used for associated type:
  • JARs that match neither the skip nor the scan list will be excluded from scan results.
  • JARs that match the scan list but not the skip list will be included in scan results.
  • JARs that match the skip list will be excluded from scan results.
/** * This is the standard implementation of {@link JarScanFilter}. By default, * the following filtering rules are used: * <ul> * <li>JARs that match neither the skip nor the scan list will be included * in scan results.</li> * <li>JARs that match the skip list but not the scan list will be excluded * from scan results.</li> * <li>JARs that match the scan list will be included from scan results. * </li> * </ul> * The default skip list and default scan list are obtained from the system * properties {@link Constants#SKIP_JARS_PROPERTY} and * {@link Constants#SCAN_JARS_PROPERTY} respectively. These default values * may be over-ridden for the {@link JarScanType#TLD} and * {@link JarScanType#PLUGGABILITY} scans. The filtering rules may also be * modified for these scan types using {@link #setDefaultTldScan(boolean)} * and {@link #setDefaultPluggabilityScan(boolean)}. If set to * <code>false</code>, the following filtering rules are used for associated * type: * <ul> * <li>JARs that match neither the skip nor the scan list will be excluded * from scan results.</li> * <li>JARs that match the scan list but not the skip list will be included * in scan results.</li> * <li>JARs that match the skip list will be excluded from scan results. * </li> * </ul> */
public StandardJarScanFilter() { tldSkip = defaultSkip; tldSkipSet = new HashSet<>(defaultSkipSet); tldScan = defaultScan; tldScanSet = new HashSet<>(defaultScanSet); pluggabilitySkip = defaultSkip; pluggabilitySkipSet = new HashSet<>(defaultSkipSet); pluggabilityScan = defaultScan; pluggabilityScanSet = new HashSet<>(defaultScanSet); } public String getTldSkip() { return tldSkip; } public void setTldSkip(String tldSkip) { this.tldSkip = tldSkip; Lock writeLock = configurationLock.writeLock(); writeLock.lock(); try { populateSetFromAttribute(tldSkip, tldSkipSet); } finally { writeLock.unlock(); } } public String getTldScan() { return tldScan; } public void setTldScan(String tldScan) { this.tldScan = tldScan; Lock writeLock = configurationLock.writeLock(); writeLock.lock(); try { populateSetFromAttribute(tldScan, tldScanSet); } finally { writeLock.unlock(); } } public boolean isDefaultTldScan() { return defaultTldScan; } public void setDefaultTldScan(boolean defaultTldScan) { this.defaultTldScan = defaultTldScan; } public String getPluggabilitySkip() { return pluggabilitySkip; } public void setPluggabilitySkip(String pluggabilitySkip) { this.pluggabilitySkip = pluggabilitySkip; Lock writeLock = configurationLock.writeLock(); writeLock.lock(); try { populateSetFromAttribute(pluggabilitySkip, pluggabilitySkipSet); } finally { writeLock.unlock(); } } public String getPluggabilityScan() { return pluggabilityScan; } public void setPluggabilityScan(String pluggabilityScan) { this.pluggabilityScan = pluggabilityScan; Lock writeLock = configurationLock.writeLock(); writeLock.lock(); try { populateSetFromAttribute(pluggabilityScan, pluggabilityScanSet); } finally { writeLock.unlock(); } } public boolean isDefaultPluggabilityScan() { return defaultPluggabilityScan; } public void setDefaultPluggabilityScan(boolean defaultPluggabilityScan) { this.defaultPluggabilityScan = defaultPluggabilityScan; } @Override public boolean check(JarScanType jarScanType, String jarName) { Lock readLock = configurationLock.readLock(); readLock.lock(); try { final boolean defaultScan; final Set<String> toSkip; final Set<String> toScan; switch (jarScanType) { case TLD: { defaultScan = defaultTldScan; toSkip = tldSkipSet; toScan = tldScanSet; break; } case PLUGGABILITY: { defaultScan = defaultPluggabilityScan; toSkip = pluggabilitySkipSet; toScan = pluggabilityScanSet; break; } case OTHER: default: { defaultScan = true; toSkip = defaultSkipSet; toScan = defaultScanSet; } } if (defaultScan) { if (Matcher.matchName(toSkip, jarName)) { if (Matcher.matchName(toScan, jarName)) { return true; } else { return false; } } return true; } else { if (Matcher.matchName(toScan, jarName)) { if (Matcher.matchName(toSkip, jarName)) { return false; } else { return true; } } return false; } } finally { readLock.unlock(); } } private static void populateSetFromAttribute(String attribute, Set<String> set) { set.clear(); if (attribute != null) { StringTokenizer tokenizer = new StringTokenizer(attribute, ","); while (tokenizer.hasMoreElements()) { String token = tokenizer.nextToken().trim(); if (token.length() > 0) { set.add(token); } } } } }