This commit is contained in:
2024-11-30 19:03:49 +08:00
commit 1e6763c160
3806 changed files with 737676 additions and 0 deletions

View File

@@ -0,0 +1,296 @@
/*
* 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.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.jar.JarEntry;
import java.util.jar.Manifest;
import org.apache.tomcat.Jar;
import org.apache.tomcat.util.compat.JreCompat;
/**
* Base implementation of Jar for implementations that use a JarInputStream to
* access the JAR file.
*/
public abstract class AbstractInputStreamJar implements Jar {
private final URL jarFileURL;
private NonClosingJarInputStream jarInputStream = null;
private JarEntry entry = null;
private Boolean multiRelease = null;
private Map<String,String> mrMap = null;
public AbstractInputStreamJar(URL jarFileUrl) {
this.jarFileURL = jarFileUrl;
}
@Override
public URL getJarFileURL() {
return jarFileURL;
}
@Override
public void nextEntry() {
if (jarInputStream == null) {
try {
reset();
} catch (IOException e) {
entry = null;
return;
}
}
try {
entry = jarInputStream.getNextJarEntry();
if (multiRelease.booleanValue()) {
// Skip base entries where there is a multi-release entry
// Skip multi-release entries that are not being used
while (entry != null &&
(mrMap.keySet().contains(entry.getName()) ||
entry.getName().startsWith("META-INF/versions/") &&
!mrMap.values().contains(entry.getName()))) {
entry = jarInputStream.getNextJarEntry();
}
} else {
// Skip multi-release entries
while (entry != null && entry.getName().startsWith("META-INF/versions/")) {
entry = jarInputStream.getNextJarEntry();
}
}
} catch (IOException ioe) {
entry = null;
}
}
@Override
public String getEntryName() {
// Given how the entry name is used, there is no requirement to convert
// the name for a multi-release entry to the corresponding base name.
if (entry == null) {
return null;
} else {
return entry.getName();
}
}
@Override
public InputStream getEntryInputStream() throws IOException {
return jarInputStream;
}
@Override
@Deprecated
public boolean entryExists(String name) throws IOException {
return false;
}
@Override
public InputStream getInputStream(String name) throws IOException {
gotoEntry(name);
if (entry == null) {
return null;
} else {
// Clear the entry so that multiple calls to this method for the
// same entry will result in a new InputStream for each call
// (BZ 60798)
entry = null;
return jarInputStream;
}
}
@Override
public long getLastModified(String name) throws IOException {
gotoEntry(name);
if (entry == null) {
return -1;
} else {
return entry.getTime();
}
}
@Override
public boolean exists(String name) throws IOException {
gotoEntry(name);
return entry != null;
}
@Override
public String getURL(String entry) {
StringBuilder result = new StringBuilder("jar:");
result.append(getJarFileURL().toExternalForm());
result.append("!/");
result.append(entry);
return result.toString();
}
@Override
public Manifest getManifest() throws IOException {
reset();
return jarInputStream.getManifest();
}
@Override
public void reset() throws IOException {
closeStream();
entry = null;
jarInputStream = createJarInputStream();
// Only perform multi-release processing on first access
if (multiRelease == null) {
if (JreCompat.isJre9Available()) {
Manifest manifest = jarInputStream.getManifest();
if (manifest == null) {
multiRelease = Boolean.FALSE;
} else {
String mrValue = manifest.getMainAttributes().getValue("Multi-Release");
if (mrValue == null) {
multiRelease = Boolean.FALSE;
} else {
multiRelease = Boolean.valueOf(mrValue);
}
}
} else {
multiRelease = Boolean.FALSE;
}
if (multiRelease.booleanValue()) {
if (mrMap == null) {
populateMrMap();
}
}
}
}
protected void closeStream() {
if (jarInputStream != null) {
try {
jarInputStream.reallyClose();
} catch (IOException ioe) {
// Ignore
}
}
}
protected abstract NonClosingJarInputStream createJarInputStream() throws IOException;
private void gotoEntry(String name) throws IOException {
boolean needsReset = true;
if (multiRelease == null) {
reset();
needsReset = false;
}
// Need to convert requested name to multi-release name (if one exists)
if (multiRelease.booleanValue()) {
String mrName = mrMap.get(name);
if (mrName != null) {
name = mrName;
}
} else if (name.startsWith("META-INF/versions/")) {
entry = null;
return;
}
if (entry != null && name.equals(entry.getName())) {
return;
}
if (needsReset) {
reset();
}
JarEntry jarEntry = jarInputStream.getNextJarEntry();
while (jarEntry != null) {
if (name.equals(jarEntry.getName())) {
entry = jarEntry;
break;
}
jarEntry = jarInputStream.getNextJarEntry();
}
}
private void populateMrMap() throws IOException {
int targetVersion = JreCompat.getInstance().jarFileRuntimeMajorVersion();
Map<String,Integer> mrVersions = new HashMap<>();
JarEntry jarEntry = jarInputStream.getNextJarEntry();
// Tracking the base name and the latest valid version found is
// sufficient to be able to create the renaming map required
while (jarEntry != null) {
String name = jarEntry.getName();
if (name.startsWith("META-INF/versions/") && name.endsWith(".class")) {
// Get the base name and version for this versioned entry
int i = name.indexOf('/', 18);
if (i > 0) {
String baseName = name.substring(i + 1);
int version = Integer.parseInt(name.substring(18, i));
// Ignore any entries targeting for a later version than
// the target for this runtime
if (version <= targetVersion) {
Integer mappedVersion = mrVersions.get(baseName);
if (mappedVersion == null) {
// No version found for this name. Create one.
mrVersions.put(baseName, Integer.valueOf(version));
} else {
// Ignore any entry for which we have already found
// a later version
if (version > mappedVersion.intValue()) {
// Replace the earlier version
mrVersions.put(baseName, Integer.valueOf(version));
}
}
}
}
}
jarEntry = jarInputStream.getNextJarEntry();
}
mrMap = new HashMap<>();
for (Entry<String,Integer> mrVersion : mrVersions.entrySet()) {
mrMap.put(mrVersion.getKey() , "META-INF/versions/" + mrVersion.getValue().toString() +
"/" + mrVersion.getKey());
}
// Reset stream back to the beginning of the JAR
closeStream();
jarInputStream = createJarInputStream();
}
}

View File

@@ -0,0 +1,37 @@
/*
* 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;
/**
* String constants for the scan package.
*/
public final class Constants {
public static final String Package = "org.apache.tomcat.util.scan";
/* System properties */
public static final String SKIP_JARS_PROPERTY =
"tomcat.util.scan.StandardJarScanFilter.jarsToSkip";
public static final String SCAN_JARS_PROPERTY =
"tomcat.util.scan.StandardJarScanFilter.jarsToScan";
/* Commons strings */
public static final String JAR_EXT = ".jar";
public static final String WEB_INF_LIB = "/WEB-INF/lib/";
public static final String WEB_INF_CLASSES = "/WEB-INF/classes";
}

View File

@@ -0,0 +1,71 @@
/*
* 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.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.regex.Matcher;
import org.apache.tomcat.Jar;
import org.apache.tomcat.util.buf.UriUtil;
/**
* Provide a mechanism to obtain objects that implement {@link Jar}.
*/
public class JarFactory {
private JarFactory() {
// Factory class. Hide public constructor.
}
public static Jar newInstance(URL url) throws IOException {
String urlString = url.toString();
if (urlString.startsWith("jar:file:")) {
if (urlString.endsWith("!/")) {
return new JarFileUrlJar(url, true);
} else {
return new JarFileUrlNestedJar(url);
}
} else if (urlString.startsWith("war:file:")) {
URL jarUrl = UriUtil.warToJar(url);
return new JarFileUrlNestedJar(jarUrl);
} else if (urlString.startsWith("file:")) {
return new JarFileUrlJar(url, false);
} else {
return new UrlJar(url);
}
}
public static URL getJarEntryURL(URL baseUrl, String entryName)
throws MalformedURLException {
String baseExternal = baseUrl.toExternalForm();
if (baseExternal.startsWith("jar")) {
// Assume this is pointing to a JAR file within a WAR. Java doesn't
// support jar:jar:file:... so switch to Tomcat's war:file:...
baseExternal = baseExternal.replaceFirst("^jar:", "war:");
baseExternal = baseExternal.replaceFirst("!/",
Matcher.quoteReplacement(UriUtil.getWarSeparator()));
}
return new URL("jar:" + baseExternal + "!/" + entryName);
}
}

View File

@@ -0,0 +1,221 @@
/*
* 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.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.zip.ZipEntry;
import org.apache.tomcat.Jar;
import org.apache.tomcat.util.compat.JreCompat;
/**
* Implementation of {@link Jar} that is optimised for file based JAR URLs that
* refer directly to a JAR file (e.g URLs of the form jar:file: ... .jar!/ or
* file:... .jar).
*/
public class JarFileUrlJar implements Jar {
private final JarFile jarFile;
private final URL jarFileURL;
private final boolean multiRelease;
private Enumeration<JarEntry> entries;
private Set<String> entryNamesSeen;
private JarEntry entry = null;
public JarFileUrlJar(URL url, boolean startsWithJar) throws IOException {
if (startsWithJar) {
// jar:file:...
JarURLConnection jarConn = (JarURLConnection) url.openConnection();
jarConn.setUseCaches(false);
jarFile = jarConn.getJarFile();
jarFileURL = jarConn.getJarFileURL();
} else {
// file:...
File f;
try {
f = new File(url.toURI());
} catch (URISyntaxException e) {
throw new IOException(e);
}
jarFile = JreCompat.getInstance().jarFileNewInstance(f);
jarFileURL = url;
}
multiRelease = JreCompat.getInstance().jarFileIsMultiRelease(jarFile);
}
@Override
public URL getJarFileURL() {
return jarFileURL;
}
@Override
@Deprecated
public boolean entryExists(String name) {
return false;
}
@Override
public InputStream getInputStream(String name) throws IOException {
// JarFile#getEntry() is multi-release aware
ZipEntry entry = jarFile.getEntry(name);
if (entry == null) {
return null;
} else {
return jarFile.getInputStream(entry);
}
}
@Override
public long getLastModified(String name) throws IOException {
// JarFile#getEntry() is multi-release aware
ZipEntry entry = jarFile.getEntry(name);
if (entry == null) {
return -1;
} else {
return entry.getTime();
}
}
@Override
public boolean exists(String name) throws IOException {
// JarFile#getEntry() is multi-release aware
ZipEntry entry = jarFile.getEntry(name);
return entry != null;
}
@Override
public String getURL(String entry) {
StringBuilder result = new StringBuilder("jar:");
result.append(getJarFileURL().toExternalForm());
result.append("!/");
result.append(entry);
return result.toString();
}
@Override
public void close() {
if (jarFile != null) {
try {
jarFile.close();
} catch (IOException e) {
// Ignore
}
}
}
@Override
public void nextEntry() {
// JarFile#entries() is NOT multi-release aware
if (entries == null) {
entries = jarFile.entries();
if (multiRelease) {
entryNamesSeen = new HashSet<>();
}
}
if (multiRelease) {
// Need to ensure that:
// - the one, correct entry is returned where multiple versions
// are available
// - that the order of entries in the JAR doesn't prevent the
// correct entries being returned
// - the case where an entry appears in the versions location
// but not in the the base location is handled correctly
// Enumerate the entries until one is reached that represents an
// entry that has not been seen before.
String name = null;
while (true) {
if (entries.hasMoreElements()) {
entry = entries.nextElement();
name = entry.getName();
// Get 'base' name
if (name.startsWith("META-INF/versions/")) {
int i = name.indexOf('/', 18);
if (i == -1) {
continue;
}
name = name.substring(i + 1);
}
if (name.length() == 0 || entryNamesSeen.contains(name)) {
continue;
}
entryNamesSeen.add(name);
// JarFile.getJarEntry is version aware so use it
entry = jarFile.getJarEntry(entry.getName());
break;
} else {
entry = null;
break;
}
}
} else {
if (entries.hasMoreElements()) {
entry = entries.nextElement();
} else {
entry = null;
}
}
}
@Override
public String getEntryName() {
if (entry == null) {
return null;
} else {
return entry.getName();
}
}
@Override
public InputStream getEntryInputStream() throws IOException {
if (entry == null) {
return null;
} else {
return jarFile.getInputStream(entry);
}
}
@Override
public Manifest getManifest() throws IOException {
return jarFile.getManifest();
}
@Override
public void reset() throws IOException {
entries = null;
entryNamesSeen = null;
entry = null;
}
}

View File

@@ -0,0 +1,65 @@
/*
* 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.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* Implementation of {@link org.apache.tomcat.Jar} that is optimised for file
* based JAR URLs that refer to a JAR file nested inside a WAR
* (e.g URLs of the form jar:file: ... .war!/ ... .jar).
*/
public class JarFileUrlNestedJar extends AbstractInputStreamJar {
private final JarFile warFile;
private final JarEntry jarEntry;
public JarFileUrlNestedJar(URL url) throws IOException {
super(url);
JarURLConnection jarConn = (JarURLConnection) url.openConnection();
jarConn.setUseCaches(false);
warFile = jarConn.getJarFile();
String urlAsString = url.toString();
int pathStart = urlAsString.indexOf("!/") + 2;
String jarPath = urlAsString.substring(pathStart);
jarEntry = warFile.getJarEntry(jarPath);
}
@Override
public void close() {
closeStream();
if (warFile != null) {
try {
warFile.close();
} catch (IOException e) {
// Ignore
}
}
}
@Override
protected NonClosingJarInputStream createJarInputStream() throws IOException {
return new NonClosingJarInputStream(warFile.getInputStream(jarEntry));
}
}

View File

@@ -0,0 +1,25 @@
# 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.
jarScan.classloaderFail=Failed to scan [{0}] from classloader hierarchy
jarScan.classloaderJarNoScan=Not performing JAR scanning on file [{0}] from classpath
jarScan.classloaderJarScan=Scanning JAR [{0}] from classpath
jarScan.classloaderStart=Scanning for JARs in classloader hierarchy
jarScan.jarUrlStart=Scanning JAR at URL [{0}]
jarScan.webinfclassesFail=Failed to scan /WEB-INF/classes
jarScan.webinflibFail=Failed to scan JAR [{0}] from /WEB-INF/lib
jarScan.webinflibJarNoScan=Not performing JAR scanning on file [{0}] from /WEB-INF/lib
jarScan.webinflibJarScan=Scanning JAR [{0}] from /WEB-INF/lib
jarScan.webinflibStart=Scanning /WEB-INF/lib for JARs

View File

@@ -0,0 +1,16 @@
# 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.
jarScan.classloaderJarNoScan=Werde auf Datei [{0}] aus dem Classpath kein JAR Scanning durchführen

View File

@@ -0,0 +1,19 @@
# 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.
jarScan.classloaderFail=Fallo al escanear [{0}] desde la herarquia classloader hierarchy
jarScan.classloaderJarNoScan=No se ejecutó el escaneo de JAR en el archivo [{0}] de la ruta de clase
jarScan.webinflibJarNoScan=No se ejecutó escaneo JAR en el archivo [{0}] de /WEB-INF/lib\n
jarScan.webinflibStart=Buscando JARs en /WEB-INF/lib

View File

@@ -0,0 +1,25 @@
# 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.
jarScan.classloaderFail=Echec de recherche dans [{0}] de la hiérarchie de chargeurs de classes
jarScan.classloaderJarNoScan=Le JAR [{0}] dans le chemin de classes ne sera pas analysé
jarScan.classloaderJarScan=Analyse du JAR [{0}] du chemin de classes
jarScan.classloaderStart=Recherche dans les JARs de la hiérarchie de chargeurs de classe
jarScan.jarUrlStart=Recherche dans le JAR à l''URL [{0}]
jarScan.webinfclassesFail=Impossible de parcourir /WEB-INF/classes
jarScan.webinflibFail=Échec de scan du JAR [{0}] de /WEB-INF/lib
jarScan.webinflibJarNoScan=Le JAR [{0}] dans /WEB-INF/lib ne sera pas analysé
jarScan.webinflibJarScan=Analyse du JAR [{0}] dans /WEB-INF/lib
jarScan.webinflibStart=Recherche de JARs dans /WEB-INF/lib

View File

@@ -0,0 +1,25 @@
# 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.
jarScan.classloaderFail=クラスローダー階層から[{0}]をスキャンできませんでした
jarScan.classloaderJarNoScan=クラスパス中のファイル [{0}] は JAR スキャンをしませんでした。
jarScan.classloaderJarScan=クラスパスの JAR ファイル [{0}] をスキャンします。
jarScan.classloaderStart=クラスローダー階層中の JAR ファイルをスキャンします。
jarScan.jarUrlStart=URL [{0}] の JAR ファイルをスキャンします。
jarScan.webinfclassesFail=/WEB-INF/classesのスキャンに失敗しました
jarScan.webinflibFail=/WEB-INF/libからJAR [{0}]をスキャンできませんでした。
jarScan.webinflibJarNoScan=/WEB-INF/lib のファイル [{0}] は JAR スキャンをしませんでした。
jarScan.webinflibJarScan=/WEB-INF/libからJAR [{0}]のスキャンを行います。
jarScan.webinflibStart=/WEB-INF/lib の JAR ファイルを検査しています。

View File

@@ -0,0 +1,25 @@
# 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.
jarScan.classloaderFail=클래스로더 계층구조로부터 [{0}]을(를) 스캔하지 못했습니다.
jarScan.classloaderJarNoScan=클래스패스로부터 파일 [{0}]에 대한 JAR 스캔을 수행하지 않습니다.
jarScan.classloaderJarScan=클래스패스로부터 JAR [{0}]을(를) 스캔합니다.
jarScan.classloaderStart=클래스로더 계층 구조에서 JAR들을 스캔합니다.
jarScan.jarUrlStart=URL [{0}]에 위치한 JAR를 스캔합니다.
jarScan.webinfclassesFail=/WEB-INF/classes를 스캔하지 못했습니다.
jarScan.webinflibFail=/WEB-INF/lib으로부터 JAR [{0}]을(를) 스캔하지 못했습니다.
jarScan.webinflibJarNoScan=/WEB-INF/lib 내의 파일 [{0}]에 대한 JAR 스캔을 수행하지 않습니다.
jarScan.webinflibJarScan=/WEB-INF/lib으로부터 JAR [{0}]을(를) 스캔합니다.
jarScan.webinflibStart=JAR들을 찾기 위해 /WEB-INF/lib을 스캔합니다.

View File

@@ -0,0 +1,23 @@
# 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.
jarScan.classloaderFail=在多级类加载器中扫描[{0}]失败
jarScan.classloaderJarNoScan=跳过classpath路径[{0}]下的jar包扫描。
jarScan.classloaderJarScan=从classpath扫描JAR[{0}]
jarScan.classloaderStart=在类加载器层次结构中扫描JAR
jarScan.jarUrlStart=正在扫描URL [{0}] 上的JAR文件
jarScan.webinfclassesFail=无法扫描/WEB-INF/classes
jarScan.webinflibJarNoScan=没有扫描到/WEB-INF/lib目录下的JAR [{0}]
jarScan.webinflibStart=扫描./WEB-INF/lib 中的JARs

View File

@@ -0,0 +1,48 @@
/*
* 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.io.IOException;
import java.io.InputStream;
import java.util.jar.JarInputStream;
/**
* When using a {@link JarInputStream} with an XML parser, the stream will be
* closed by the parser. This causes problems if multiple entries from the JAR
* need to be parsed. This implementation makes {{@link #close()} a NO-OP and
* adds {@link #reallyClose()} that will close the stream.
*/
public class NonClosingJarInputStream extends JarInputStream {
public NonClosingJarInputStream(InputStream in, boolean verify)
throws IOException {
super(in, verify);
}
public NonClosingJarInputStream(InputStream in) throws IOException {
super(in);
}
@Override
public void close() throws IOException {
// Make this a NO-OP so that further entries can be read from the stream
}
public void reallyClose() throws IOException {
super.close();
}
}

View File

@@ -0,0 +1,155 @@
/*
* 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.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.jar.Manifest;
import org.apache.tomcat.Jar;
/**
* This class provides a wrapper around {@link Jar} that uses reference counting
* to close and re-create the wrapped {@link Jar} instance as required.
*/
public class ReferenceCountedJar implements Jar {
private final URL url;
private Jar wrappedJar;
private int referenceCount = 0;
public ReferenceCountedJar(URL url) throws IOException {
this.url = url;
open();
}
/*
* Note: Returns this instance so it can be used with try-with-resources
*/
private synchronized ReferenceCountedJar open() throws IOException {
if (wrappedJar == null) {
wrappedJar = JarFactory.newInstance(url);
}
referenceCount++;
return this;
}
@Override
public synchronized void close() {
referenceCount--;
if (referenceCount == 0) {
wrappedJar.close();
wrappedJar = null;
}
}
@Override
public URL getJarFileURL() {
return url;
}
@Override
public InputStream getInputStream(String name) throws IOException {
try (ReferenceCountedJar jar = open()) {
return jar.wrappedJar.getInputStream(name);
}
}
@Override
public long getLastModified(String name) throws IOException {
try (ReferenceCountedJar jar = open()) {
return jar.wrappedJar.getLastModified(name);
}
}
@Override
public boolean exists(String name) throws IOException {
try (ReferenceCountedJar jar = open()) {
return jar.wrappedJar.exists(name);
}
}
@Override
public void nextEntry() {
try (ReferenceCountedJar jar = open()) {
jar.wrappedJar.nextEntry();
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
}
@Override
public String getEntryName() {
try (ReferenceCountedJar jar = open()) {
return jar.wrappedJar.getEntryName();
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
}
@Override
public InputStream getEntryInputStream() throws IOException {
try (ReferenceCountedJar jar = open()) {
return jar.wrappedJar.getEntryInputStream();
}
}
@Override
public String getURL(String entry) {
try (ReferenceCountedJar jar = open()) {
return jar.wrappedJar.getURL(entry);
} catch (IOException ioe) {
throw new IllegalStateException(ioe);
}
}
@Override
public Manifest getManifest() throws IOException {
try (ReferenceCountedJar jar = open()) {
return jar.wrappedJar.getManifest();
}
}
@Override
public void reset() throws IOException {
try (ReferenceCountedJar jar = open()) {
jar.wrappedJar.reset();
}
}
@Override
@Deprecated
public boolean entryExists(String name) throws IOException {
try (ReferenceCountedJar jar = open()) {
return jar.wrappedJar.entryExists(name);
}
}
}

View File

@@ -0,0 +1,260 @@
/*
* 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<>();
private static final boolean defaultSkipAll;
static {
// Initialize defaults. There are no setter methods for them.
defaultSkip = System.getProperty(Constants.SKIP_JARS_PROPERTY);
populateSetFromAttribute(defaultSkip, defaultSkipSet);
defaultSkipAll = defaultSkipSet.contains("*") || defaultSkipSet.contains("*.jar");
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 {@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 isSkipAll() {
return defaultSkipAll;
}
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);
}
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,49 @@
/*
* 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.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLConnection;
/**
* Implementation of {@link org.apache.tomcat.Jar} that is optimised for
* non-file based JAR URLs.
*/
public class UrlJar extends AbstractInputStreamJar {
public UrlJar(URL jarFileURL) {
super(jarFileURL);
}
@Override
public void close() {
closeStream();
}
@Override
protected NonClosingJarInputStream createJarInputStream() throws IOException {
JarURLConnection jarConn = (JarURLConnection) getJarFileURL().openConnection();
URL resourceURL = jarConn.getJarFileURL();
URLConnection resourceConn = resourceURL.openConnection();
resourceConn.setUseCaches(false);
return new NonClosingJarInputStream(resourceConn.getInputStream());
}
}

View File

@@ -0,0 +1,27 @@
<!--
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.
-->
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html>
<head>
</head>
<body bgcolor="white">
<p>
This package contains the common classes used to perform configuration scanning
for Catalina and Jasper.
</p>
</body>
</html>