1 /*
2  *  Licensed to the Apache Software Foundation (ASF) under one or more
3  *  contributor license agreements.  See the NOTICE file distributed with
4  *  this work for additional information regarding copyright ownership.
5  *  The ASF licenses this file to You under the Apache License, Version 2.0
6  *  (the "License"); you may not use this file except in compliance with
7  *  the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  */

17 package org.apache.tomcat.util.scan;
18
19 import java.util.HashSet;
20 import java.util.Set;
21 import java.util.StringTokenizer;
22 import java.util.concurrent.locks.Lock;
23 import java.util.concurrent.locks.ReadWriteLock;
24 import java.util.concurrent.locks.ReentrantReadWriteLock;
25
26 import org.apache.tomcat.JarScanFilter;
27 import org.apache.tomcat.JarScanType;
28 import org.apache.tomcat.util.file.Matcher;
29
30 public class StandardJarScanFilter implements JarScanFilter {
31
32     private final ReadWriteLock configurationLock =
33             new ReentrantReadWriteLock();
34
35     private static final String defaultSkip;
36     private static final String defaultScan;
37     private static final Set<String> defaultSkipSet = new HashSet<>();
38     private static final Set<String> defaultScanSet = new HashSet<>();
39     private static final boolean defaultSkipAll;
40
41     static {
42         // Initialize defaults. There are no setter methods for them.
43         defaultSkip = System.getProperty(Constants.SKIP_JARS_PROPERTY);
44         populateSetFromAttribute(defaultSkip, defaultSkipSet);
45         defaultSkipAll = defaultSkipSet.contains("*") || defaultSkipSet.contains("*.jar");
46         defaultScan = System.getProperty(Constants.SCAN_JARS_PROPERTY);
47         populateSetFromAttribute(defaultScan, defaultScanSet);
48     }
49
50     private String tldSkip;
51     private String tldScan;
52     private final Set<String> tldSkipSet;
53     private final Set<String> tldScanSet;
54     private boolean defaultTldScan = true;
55
56     private String pluggabilitySkip;
57     private String pluggabilityScan;
58     private final Set<String> pluggabilitySkipSet;
59     private final Set<String> pluggabilityScanSet;
60     private boolean defaultPluggabilityScan = true;
61
62     /**
63      * This is the standard implementation of {@link JarScanFilter}. By default,
64      * the following filtering rules are used:
65      * <ul>
66      * <li>JARs that match neither the skip nor the scan list will be included
67      *     in scan results.</li>
68      * <li>JARs that match the skip list but not the scan list will be excluded
69      *     from scan results.</li>
70      * <li>JARs that match the scan list will be included from scan results.
71      *     </li>
72      * </ul>
73      * The default skip list and default scan list are obtained from the system
74      * properties {@link Constants#SKIP_JARS_PROPERTY} and
75      * {@link Constants#SCAN_JARS_PROPERTY} respectively. These default values
76      * may be over-ridden for the {@link JarScanType#TLD} and
77      * {@link JarScanType#PLUGGABILITY} scans. The filtering rules may also be
78      * modified for these scan types using {@link #setDefaultTldScan(boolean)}
79      * and {@link #setDefaultPluggabilityScan(boolean)}. If set to
80      * <code>false</code>, the following filtering rules are used for associated
81      * type:
82      * <ul>
83      * <li>JARs that match neither the skip nor the scan list will be excluded
84      *     from scan results.</li>
85      * <li>JARs that match the scan list but not the skip list will be included
86      *     in scan results.</li>
87      * <li>JARs that match the skip list will be excluded from scan results.
88      *     </li>
89      * </ul>
90      */

91     public StandardJarScanFilter() {
92         tldSkip = defaultSkip;
93         tldSkipSet = new HashSet<>(defaultSkipSet);
94         tldScan = defaultScan;
95         tldScanSet = new HashSet<>(defaultScanSet);
96         pluggabilitySkip = defaultSkip;
97         pluggabilitySkipSet = new HashSet<>(defaultSkipSet);
98         pluggabilityScan = defaultScan;
99         pluggabilityScanSet = new HashSet<>(defaultScanSet);
100     }
101
102
103     public String getTldSkip() {
104         return tldSkip;
105     }
106
107
108     public void setTldSkip(String tldSkip) {
109         this.tldSkip = tldSkip;
110         Lock writeLock = configurationLock.writeLock();
111         writeLock.lock();
112         try {
113             populateSetFromAttribute(tldSkip, tldSkipSet);
114         } finally {
115             writeLock.unlock();
116         }
117     }
118
119
120     public String getTldScan() {
121         return tldScan;
122     }
123
124
125     public void setTldScan(String tldScan) {
126         this.tldScan = tldScan;
127         Lock writeLock = configurationLock.writeLock();
128         writeLock.lock();
129         try {
130             populateSetFromAttribute(tldScan, tldScanSet);
131         } finally {
132             writeLock.unlock();
133         }
134     }
135
136
137     @Override
138     public boolean isSkipAll() {
139         return defaultSkipAll;
140     }
141
142
143     public boolean isDefaultTldScan() {
144         return defaultTldScan;
145     }
146
147
148     public void setDefaultTldScan(boolean defaultTldScan) {
149         this.defaultTldScan = defaultTldScan;
150     }
151
152
153     public String getPluggabilitySkip() {
154         return pluggabilitySkip;
155     }
156
157
158     public void setPluggabilitySkip(String pluggabilitySkip) {
159         this.pluggabilitySkip = pluggabilitySkip;
160         Lock writeLock = configurationLock.writeLock();
161         writeLock.lock();
162         try {
163             populateSetFromAttribute(pluggabilitySkip, pluggabilitySkipSet);
164         } finally {
165             writeLock.unlock();
166         }
167     }
168
169
170     public String getPluggabilityScan() {
171         return pluggabilityScan;
172     }
173
174
175     public void setPluggabilityScan(String pluggabilityScan) {
176         this.pluggabilityScan = pluggabilityScan;
177         Lock writeLock = configurationLock.writeLock();
178         writeLock.lock();
179         try {
180             populateSetFromAttribute(pluggabilityScan, pluggabilityScanSet);
181         } finally {
182             writeLock.unlock();
183         }
184     }
185
186
187     public boolean isDefaultPluggabilityScan() {
188         return defaultPluggabilityScan;
189     }
190
191
192     public void setDefaultPluggabilityScan(boolean defaultPluggabilityScan) {
193         this.defaultPluggabilityScan = defaultPluggabilityScan;
194     }
195
196
197     @Override
198     public boolean check(JarScanType jarScanType, String jarName) {
199         Lock readLock = configurationLock.readLock();
200         readLock.lock();
201         try {
202             final boolean defaultScan;
203             final Set<String> toSkip;
204             final Set<String> toScan;
205             switch (jarScanType) {
206                 case TLD: {
207                     defaultScan = defaultTldScan;
208                     toSkip = tldSkipSet;
209                     toScan = tldScanSet;
210                     break;
211                 }
212                 case PLUGGABILITY: {
213                     defaultScan = defaultPluggabilityScan;
214                     toSkip = pluggabilitySkipSet;
215                     toScan = pluggabilityScanSet;
216                     break;
217                 }
218                 case OTHER:
219                 default: {
220                     defaultScan = true;
221                     toSkip = defaultSkipSet;
222                     toScan = defaultScanSet;
223                 }
224             }
225             if (defaultScan) {
226                 if (Matcher.matchName(toSkip, jarName)) {
227                     if (Matcher.matchName(toScan, jarName)) {
228                         return true;
229                     } else {
230                         return false;
231                     }
232                 }
233                 return true;
234             } else {
235                 if (Matcher.matchName(toScan, jarName)) {
236                     if (Matcher.matchName(toSkip, jarName)) {
237                         return false;
238                     } else {
239                         return true;
240                     }
241                 }
242                 return false;
243             }
244         } finally {
245             readLock.unlock();
246         }
247     }
248
249     private static void populateSetFromAttribute(String attribute, Set<String> set) {
250         set.clear();
251         if (attribute != null) {
252             StringTokenizer tokenizer = new StringTokenizer(attribute, ",");
253             while (tokenizer.hasMoreElements()) {
254                 String token = tokenizer.nextToken().trim();
255                 if (token.length() > 0) {
256                     set.add(token);
257                 }
258             }
259         }
260     }
261 }
262