init
This commit is contained in:
296
java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java
Normal file
296
java/org/apache/tomcat/util/scan/AbstractInputStreamJar.java
Normal 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();
|
||||
}
|
||||
}
|
||||
37
java/org/apache/tomcat/util/scan/Constants.java
Normal file
37
java/org/apache/tomcat/util/scan/Constants.java
Normal 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";
|
||||
}
|
||||
71
java/org/apache/tomcat/util/scan/JarFactory.java
Normal file
71
java/org/apache/tomcat/util/scan/JarFactory.java
Normal 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);
|
||||
}
|
||||
}
|
||||
221
java/org/apache/tomcat/util/scan/JarFileUrlJar.java
Normal file
221
java/org/apache/tomcat/util/scan/JarFileUrlJar.java
Normal 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;
|
||||
}
|
||||
}
|
||||
65
java/org/apache/tomcat/util/scan/JarFileUrlNestedJar.java
Normal file
65
java/org/apache/tomcat/util/scan/JarFileUrlNestedJar.java
Normal 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));
|
||||
}
|
||||
}
|
||||
25
java/org/apache/tomcat/util/scan/LocalStrings.properties
Normal file
25
java/org/apache/tomcat/util/scan/LocalStrings.properties
Normal 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
|
||||
16
java/org/apache/tomcat/util/scan/LocalStrings_de.properties
Normal file
16
java/org/apache/tomcat/util/scan/LocalStrings_de.properties
Normal 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
|
||||
19
java/org/apache/tomcat/util/scan/LocalStrings_es.properties
Normal file
19
java/org/apache/tomcat/util/scan/LocalStrings_es.properties
Normal 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
|
||||
25
java/org/apache/tomcat/util/scan/LocalStrings_fr.properties
Normal file
25
java/org/apache/tomcat/util/scan/LocalStrings_fr.properties
Normal 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
|
||||
25
java/org/apache/tomcat/util/scan/LocalStrings_ja.properties
Normal file
25
java/org/apache/tomcat/util/scan/LocalStrings_ja.properties
Normal 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 ファイルを検査しています。
|
||||
25
java/org/apache/tomcat/util/scan/LocalStrings_ko.properties
Normal file
25
java/org/apache/tomcat/util/scan/LocalStrings_ko.properties
Normal 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을 스캔합니다.
|
||||
@@ -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
|
||||
@@ -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();
|
||||
}
|
||||
}
|
||||
155
java/org/apache/tomcat/util/scan/ReferenceCountedJar.java
Normal file
155
java/org/apache/tomcat/util/scan/ReferenceCountedJar.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
260
java/org/apache/tomcat/util/scan/StandardJarScanFilter.java
Normal file
260
java/org/apache/tomcat/util/scan/StandardJarScanFilter.java
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
506
java/org/apache/tomcat/util/scan/StandardJarScanner.java
Normal file
506
java/org/apache/tomcat/util/scan/StandardJarScanner.java
Normal file
@@ -0,0 +1,506 @@
|
||||
/*
|
||||
* 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.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Deque;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
import java.util.jar.Attributes;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.Jar;
|
||||
import org.apache.tomcat.JarScanFilter;
|
||||
import org.apache.tomcat.JarScanType;
|
||||
import org.apache.tomcat.JarScanner;
|
||||
import org.apache.tomcat.JarScannerCallback;
|
||||
import org.apache.tomcat.util.ExceptionUtils;
|
||||
import org.apache.tomcat.util.buf.UriUtil;
|
||||
import org.apache.tomcat.util.compat.JreCompat;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* The default {@link JarScanner} implementation scans the WEB-INF/lib directory
|
||||
* followed by the provided classloader and then works up the classloader
|
||||
* hierarchy. This implementation is sufficient to meet the requirements of the
|
||||
* Servlet 3.0 specification as well as to provide a number of Tomcat specific
|
||||
* extensions. The extensions are:
|
||||
* <ul>
|
||||
* <li>Scanning the classloader hierarchy (enabled by default)</li>
|
||||
* <li>Testing all files to see if they are JARs (disabled by default)</li>
|
||||
* <li>Testing all directories to see if they are exploded JARs
|
||||
* (disabled by default)</li>
|
||||
* </ul>
|
||||
* All of the extensions may be controlled via configuration.
|
||||
*/
|
||||
public class StandardJarScanner implements JarScanner {
|
||||
|
||||
private final Log log = LogFactory.getLog(StandardJarScanner.class); // must not be static
|
||||
|
||||
/**
|
||||
* The string resources for this package.
|
||||
*/
|
||||
private static final StringManager sm = StringManager.getManager(Constants.Package);
|
||||
|
||||
private static final Set<ClassLoader> CLASSLOADER_HIERARCHY;
|
||||
|
||||
static {
|
||||
Set<ClassLoader> cls = new HashSet<>();
|
||||
|
||||
ClassLoader cl = StandardJarScanner.class.getClassLoader();
|
||||
while (cl != null) {
|
||||
cls.add(cl);
|
||||
cl = cl.getParent();
|
||||
}
|
||||
|
||||
CLASSLOADER_HIERARCHY = Collections.unmodifiableSet(cls);
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the classpath scanning extension.
|
||||
*/
|
||||
private boolean scanClassPath = true;
|
||||
public boolean isScanClassPath() {
|
||||
return scanClassPath;
|
||||
}
|
||||
public void setScanClassPath(boolean scanClassPath) {
|
||||
this.scanClassPath = scanClassPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the JAR file Manifest scanning extension.
|
||||
*/
|
||||
private boolean scanManifest = true;
|
||||
public boolean isScanManifest() {
|
||||
return scanManifest;
|
||||
}
|
||||
public void setScanManifest(boolean scanManifest) {
|
||||
this.scanManifest = scanManifest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the testing all files to see of they are JAR files extension.
|
||||
*/
|
||||
private boolean scanAllFiles = false;
|
||||
public boolean isScanAllFiles() {
|
||||
return scanAllFiles;
|
||||
}
|
||||
public void setScanAllFiles(boolean scanAllFiles) {
|
||||
this.scanAllFiles = scanAllFiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the testing all directories to see of they are exploded JAR
|
||||
* files extension.
|
||||
*/
|
||||
private boolean scanAllDirectories = true;
|
||||
public boolean isScanAllDirectories() {
|
||||
return scanAllDirectories;
|
||||
}
|
||||
public void setScanAllDirectories(boolean scanAllDirectories) {
|
||||
this.scanAllDirectories = scanAllDirectories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the testing of the bootstrap classpath which consists of the
|
||||
* runtime classes provided by the JVM and any installed system extensions.
|
||||
*/
|
||||
private boolean scanBootstrapClassPath = false;
|
||||
public boolean isScanBootstrapClassPath() {
|
||||
return scanBootstrapClassPath;
|
||||
}
|
||||
public void setScanBootstrapClassPath(boolean scanBootstrapClassPath) {
|
||||
this.scanBootstrapClassPath = scanBootstrapClassPath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Controls the filtering of the results from the scan for JARs
|
||||
*/
|
||||
private JarScanFilter jarScanFilter = new StandardJarScanFilter();
|
||||
@Override
|
||||
public JarScanFilter getJarScanFilter() {
|
||||
return jarScanFilter;
|
||||
}
|
||||
@Override
|
||||
public void setJarScanFilter(JarScanFilter jarScanFilter) {
|
||||
this.jarScanFilter = jarScanFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan the provided ServletContext and class loader for JAR files. Each JAR
|
||||
* file found will be passed to the callback handler to be processed.
|
||||
*
|
||||
* @param scanType The type of JAR scan to perform. This is passed to
|
||||
* the filter which uses it to determine how to
|
||||
* filter the results
|
||||
* @param context The ServletContext - used to locate and access
|
||||
* WEB-INF/lib
|
||||
* @param callback The handler to process any JARs found
|
||||
*/
|
||||
@Override
|
||||
public void scan(JarScanType scanType, ServletContext context,
|
||||
JarScannerCallback callback) {
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(sm.getString("jarScan.webinflibStart"));
|
||||
}
|
||||
|
||||
if (jarScanFilter instanceof StandardJarScanFilter) {
|
||||
if (((StandardJarScanFilter) jarScanFilter).isSkipAll())
|
||||
return;
|
||||
}
|
||||
|
||||
Set<URL> processedURLs = new HashSet<>();
|
||||
|
||||
// Scan WEB-INF/lib
|
||||
Set<String> dirList = context.getResourcePaths(Constants.WEB_INF_LIB);
|
||||
if (dirList != null) {
|
||||
for (String path : dirList) {
|
||||
if (path.endsWith(Constants.JAR_EXT) &&
|
||||
getJarScanFilter().check(scanType,
|
||||
path.substring(path.lastIndexOf('/')+1))) {
|
||||
// Need to scan this JAR
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("jarScan.webinflibJarScan", path));
|
||||
}
|
||||
URL url = null;
|
||||
try {
|
||||
url = context.getResource(path);
|
||||
processedURLs.add(url);
|
||||
process(scanType, callback, url, path, true, null);
|
||||
} catch (IOException e) {
|
||||
log.warn(sm.getString("jarScan.webinflibFail", url), e);
|
||||
}
|
||||
} else {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(sm.getString("jarScan.webinflibJarNoScan", path));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Scan WEB-INF/classes
|
||||
try {
|
||||
URL webInfURL = context.getResource(Constants.WEB_INF_CLASSES);
|
||||
if (webInfURL != null) {
|
||||
// WEB-INF/classes will also be included in the URLs returned
|
||||
// by the web application class loader so ensure the class path
|
||||
// scanning below does not re-scan this location.
|
||||
processedURLs.add(webInfURL);
|
||||
|
||||
if (isScanAllDirectories()) {
|
||||
URL url = context.getResource(Constants.WEB_INF_CLASSES + "/META-INF");
|
||||
if (url != null) {
|
||||
try {
|
||||
callback.scanWebInfClasses();
|
||||
} catch (IOException e) {
|
||||
log.warn(sm.getString("jarScan.webinfclassesFail"), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (MalformedURLException e) {
|
||||
// Ignore. Won't happen. URLs are of the correct form.
|
||||
}
|
||||
|
||||
// Scan the classpath
|
||||
if (isScanClassPath()) {
|
||||
doScanClassPath(scanType, context, callback, processedURLs);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void doScanClassPath(JarScanType scanType, ServletContext context,
|
||||
JarScannerCallback callback, Set<URL> processedURLs) {
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(sm.getString("jarScan.classloaderStart"));
|
||||
}
|
||||
|
||||
ClassLoader stopLoader = null;
|
||||
if (!isScanBootstrapClassPath()) {
|
||||
// Stop when we reach the bootstrap class loader
|
||||
stopLoader = ClassLoader.getSystemClassLoader().getParent();
|
||||
}
|
||||
|
||||
ClassLoader classLoader = context.getClassLoader();
|
||||
|
||||
// JARs are treated as application provided until the common class
|
||||
// loader is reached.
|
||||
boolean isWebapp = true;
|
||||
|
||||
// Use a Deque so URLs can be removed as they are processed
|
||||
// and new URLs can be added as they are discovered during
|
||||
// processing.
|
||||
Deque<URL> classPathUrlsToProcess = new LinkedList<>();
|
||||
|
||||
while (classLoader != null && classLoader != stopLoader) {
|
||||
if (classLoader instanceof URLClassLoader) {
|
||||
if (isWebapp) {
|
||||
isWebapp = isWebappClassLoader(classLoader);
|
||||
}
|
||||
|
||||
classPathUrlsToProcess.addAll(
|
||||
Arrays.asList(((URLClassLoader) classLoader).getURLs()));
|
||||
|
||||
processURLs(scanType, callback, processedURLs, isWebapp, classPathUrlsToProcess);
|
||||
}
|
||||
classLoader = classLoader.getParent();
|
||||
}
|
||||
|
||||
if (JreCompat.isJre9Available()) {
|
||||
// The application and platform class loaders are not
|
||||
// instances of URLClassLoader. Use the class path in this
|
||||
// case.
|
||||
addClassPath(classPathUrlsToProcess);
|
||||
// Also add any modules
|
||||
JreCompat.getInstance().addBootModulePath(classPathUrlsToProcess);
|
||||
processURLs(scanType, callback, processedURLs, false, classPathUrlsToProcess);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void processURLs(JarScanType scanType, JarScannerCallback callback,
|
||||
Set<URL> processedURLs, boolean isWebapp, Deque<URL> classPathUrlsToProcess) {
|
||||
|
||||
if (jarScanFilter instanceof StandardJarScanFilter) {
|
||||
if (((StandardJarScanFilter) jarScanFilter).isSkipAll())
|
||||
return;
|
||||
}
|
||||
|
||||
while (!classPathUrlsToProcess.isEmpty()) {
|
||||
URL url = classPathUrlsToProcess.pop();
|
||||
|
||||
if (processedURLs.contains(url)) {
|
||||
// Skip this URL it has already been processed
|
||||
continue;
|
||||
}
|
||||
|
||||
ClassPathEntry cpe = new ClassPathEntry(url);
|
||||
|
||||
// JARs are scanned unless the filter says not to.
|
||||
// Directories are scanned for pluggability scans or
|
||||
// if scanAllDirectories is enabled unless the
|
||||
// filter says not to.
|
||||
if ((cpe.isJar() ||
|
||||
scanType == JarScanType.PLUGGABILITY ||
|
||||
isScanAllDirectories()) &&
|
||||
getJarScanFilter().check(scanType,
|
||||
cpe.getName())) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("jarScan.classloaderJarScan", url));
|
||||
}
|
||||
try {
|
||||
processedURLs.add(url);
|
||||
process(scanType, callback, url, null, isWebapp, classPathUrlsToProcess);
|
||||
} catch (IOException ioe) {
|
||||
log.warn(sm.getString("jarScan.classloaderFail", url), ioe);
|
||||
}
|
||||
} else {
|
||||
// JAR / directory has been skipped
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(sm.getString("jarScan.classloaderJarNoScan", url));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void addClassPath(Deque<URL> classPathUrlsToProcess) {
|
||||
String classPath = System.getProperty("java.class.path");
|
||||
|
||||
if (classPath == null || classPath.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
String[] classPathEntries = classPath.split(File.pathSeparator);
|
||||
for (String classPathEntry : classPathEntries) {
|
||||
File f = new File(classPathEntry);
|
||||
try {
|
||||
classPathUrlsToProcess.add(f.toURI().toURL());
|
||||
} catch (MalformedURLException e) {
|
||||
log.warn(sm.getString("jarScan.classPath.badEntry", classPathEntry), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Since class loader hierarchies can get complicated, this method attempts
|
||||
* to apply the following rule: A class loader is a web application class
|
||||
* loader unless it loaded this class (StandardJarScanner) or is a parent
|
||||
* of the class loader that loaded this class.
|
||||
*
|
||||
* This should mean:
|
||||
* the webapp class loader is an application class loader
|
||||
* the shared class loader is an application class loader
|
||||
* the server class loader is not an application class loader
|
||||
* the common class loader is not an application class loader
|
||||
* the system class loader is not an application class loader
|
||||
* the bootstrap class loader is not an application class loader
|
||||
*/
|
||||
private static boolean isWebappClassLoader(ClassLoader classLoader) {
|
||||
return !CLASSLOADER_HIERARCHY.contains(classLoader);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Scan a URL for JARs with the optional extensions to look at all files
|
||||
* and all directories.
|
||||
*/
|
||||
protected void process(JarScanType scanType, JarScannerCallback callback,
|
||||
URL url, String webappPath, boolean isWebapp, Deque<URL> classPathUrlsToProcess)
|
||||
throws IOException {
|
||||
|
||||
if (log.isTraceEnabled()) {
|
||||
log.trace(sm.getString("jarScan.jarUrlStart", url));
|
||||
}
|
||||
|
||||
if ("jar".equals(url.getProtocol()) || url.getPath().endsWith(Constants.JAR_EXT)) {
|
||||
try (Jar jar = JarFactory.newInstance(url)) {
|
||||
if (isScanManifest()) {
|
||||
processManifest(jar, isWebapp, classPathUrlsToProcess);
|
||||
}
|
||||
callback.scan(jar, webappPath, isWebapp);
|
||||
}
|
||||
} else if ("file".equals(url.getProtocol())) {
|
||||
File f;
|
||||
try {
|
||||
f = new File(url.toURI());
|
||||
if (f.isFile() && isScanAllFiles()) {
|
||||
// Treat this file as a JAR
|
||||
URL jarURL = UriUtil.buildJarUrl(f);
|
||||
try (Jar jar = JarFactory.newInstance(jarURL)) {
|
||||
if (isScanManifest()) {
|
||||
processManifest(jar, isWebapp, classPathUrlsToProcess);
|
||||
}
|
||||
callback.scan(jar, webappPath, isWebapp);
|
||||
}
|
||||
} else if (f.isDirectory()) {
|
||||
if (scanType == JarScanType.PLUGGABILITY) {
|
||||
callback.scan(f, webappPath, isWebapp);
|
||||
} else {
|
||||
File metainf = new File(f.getAbsoluteFile() + File.separator + "META-INF");
|
||||
if (metainf.isDirectory()) {
|
||||
callback.scan(f, webappPath, isWebapp);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Throwable t) {
|
||||
ExceptionUtils.handleThrowable(t);
|
||||
// Wrap the exception and re-throw
|
||||
IOException ioe = new IOException();
|
||||
ioe.initCause(t);
|
||||
throw ioe;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void processManifest(Jar jar, boolean isWebapp,
|
||||
Deque<URL> classPathUrlsToProcess) throws IOException {
|
||||
|
||||
// Not processed for web application JARs nor if the caller did not
|
||||
// provide a Deque of URLs to append to.
|
||||
if (isWebapp || classPathUrlsToProcess == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Manifest manifest = jar.getManifest();
|
||||
if (manifest != null) {
|
||||
Attributes attributes = manifest.getMainAttributes();
|
||||
String classPathAttribute = attributes.getValue("Class-Path");
|
||||
if (classPathAttribute == null) {
|
||||
return;
|
||||
}
|
||||
String[] classPathEntries = classPathAttribute.split(" ");
|
||||
for (String classPathEntry : classPathEntries) {
|
||||
classPathEntry = classPathEntry.trim();
|
||||
if (classPathEntry.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
URL jarURL = jar.getJarFileURL();
|
||||
URL classPathEntryURL;
|
||||
try {
|
||||
URI jarURI = jarURL.toURI();
|
||||
/*
|
||||
* Note: Resolving the relative URLs from the manifest has the
|
||||
* potential to introduce security concerns. However, since
|
||||
* only JARs provided by the container and NOT those provided
|
||||
* by web applications are processed, there should be no
|
||||
* issues.
|
||||
* If this feature is ever extended to include JARs provided
|
||||
* by web applications, checks should be added to ensure that
|
||||
* any relative URL does not step outside the web application.
|
||||
*/
|
||||
URI classPathEntryURI = jarURI.resolve(classPathEntry);
|
||||
classPathEntryURL = classPathEntryURI.toURL();
|
||||
} catch (Exception e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("jarScan.invalidUri", jarURL), e);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
classPathUrlsToProcess.add(classPathEntryURL);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static class ClassPathEntry {
|
||||
|
||||
private final boolean jar;
|
||||
private final String name;
|
||||
|
||||
public ClassPathEntry(URL url) {
|
||||
String path = url.getPath();
|
||||
int end = path.lastIndexOf(Constants.JAR_EXT);
|
||||
if (end != -1) {
|
||||
jar = true;
|
||||
int start = path.lastIndexOf('/', end);
|
||||
name = path.substring(start + 1, end + 4);
|
||||
} else {
|
||||
jar = false;
|
||||
if (path.endsWith("/")) {
|
||||
path = path.substring(0, path.length() - 1);
|
||||
}
|
||||
int start = path.lastIndexOf('/');
|
||||
name = path.substring(start + 1);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public boolean isJar() {
|
||||
return jar;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
49
java/org/apache/tomcat/util/scan/UrlJar.java
Normal file
49
java/org/apache/tomcat/util/scan/UrlJar.java
Normal 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());
|
||||
}
|
||||
}
|
||||
27
java/org/apache/tomcat/util/scan/package.html
Normal file
27
java/org/apache/tomcat/util/scan/package.html
Normal 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>
|
||||
Reference in New Issue
Block a user