init
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
public abstract class AbstractArchiveResource extends AbstractResource {
|
||||
|
||||
private final AbstractArchiveResourceSet archiveResourceSet;
|
||||
private final String baseUrl;
|
||||
private final JarEntry resource;
|
||||
private final String codeBaseUrl;
|
||||
private final String name;
|
||||
private boolean readCerts = false;
|
||||
private Certificate[] certificates;
|
||||
|
||||
protected AbstractArchiveResource(AbstractArchiveResourceSet archiveResourceSet,
|
||||
String webAppPath, String baseUrl, JarEntry jarEntry, String codeBaseUrl) {
|
||||
super(archiveResourceSet.getRoot(), webAppPath);
|
||||
this.archiveResourceSet = archiveResourceSet;
|
||||
this.baseUrl = baseUrl;
|
||||
this.resource = jarEntry;
|
||||
this.codeBaseUrl = codeBaseUrl;
|
||||
|
||||
String resourceName = resource.getName();
|
||||
if (resourceName.charAt(resourceName.length() - 1) == '/') {
|
||||
resourceName = resourceName.substring(0, resourceName.length() - 1);
|
||||
}
|
||||
String internalPath = archiveResourceSet.getInternalPath();
|
||||
if (internalPath.length() > 0 && resourceName.equals(
|
||||
internalPath.subSequence(1, internalPath.length()))) {
|
||||
name = "";
|
||||
} else {
|
||||
int index = resourceName.lastIndexOf('/');
|
||||
if (index == -1) {
|
||||
name = resourceName;
|
||||
} else {
|
||||
name = resourceName.substring(index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected AbstractArchiveResourceSet getArchiveResourceSet() {
|
||||
return archiveResourceSet;
|
||||
}
|
||||
|
||||
protected final String getBase() {
|
||||
return archiveResourceSet.getBase();
|
||||
}
|
||||
|
||||
protected final String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
protected final JarEntry getResource() {
|
||||
return resource;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified() {
|
||||
return resource.getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return resource.isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return !resource.isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
if (isDirectory()) {
|
||||
return -1;
|
||||
}
|
||||
return resource.getSize();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCanonicalPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCreation() {
|
||||
return resource.getTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() {
|
||||
String url = baseUrl + resource.getName();
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (MalformedURLException e) {
|
||||
if (getLog().isDebugEnabled()) {
|
||||
getLog().debug(sm.getString("fileResource.getUrlFail", url), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getCodeBase() {
|
||||
try {
|
||||
return new URL(codeBaseUrl);
|
||||
} catch (MalformedURLException e) {
|
||||
if (getLog().isDebugEnabled()) {
|
||||
getLog().debug(sm.getString("fileResource.getUrlFail", codeBaseUrl), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte[] getContent() {
|
||||
long len = getContentLength();
|
||||
|
||||
if (len > Integer.MAX_VALUE) {
|
||||
// Can't create an array that big
|
||||
throw new ArrayIndexOutOfBoundsException(sm.getString(
|
||||
"abstractResource.getContentTooLarge", getWebappPath(),
|
||||
Long.valueOf(len)));
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
// Content is not applicable here (e.g. is a directory)
|
||||
return null;
|
||||
}
|
||||
|
||||
int size = (int) len;
|
||||
byte[] result = new byte[size];
|
||||
|
||||
int pos = 0;
|
||||
try (JarInputStreamWrapper jisw = getJarInputStreamWrapper()) {
|
||||
if (jisw == null) {
|
||||
// An error occurred, don't return corrupted content
|
||||
return null;
|
||||
}
|
||||
while (pos < size) {
|
||||
int n = jisw.read(result, pos, size - pos);
|
||||
if (n < 0) {
|
||||
break;
|
||||
}
|
||||
pos += n;
|
||||
}
|
||||
// Once the stream has been read, read the certs
|
||||
certificates = jisw.getCertificates();
|
||||
readCerts = true;
|
||||
} catch (IOException ioe) {
|
||||
if (getLog().isDebugEnabled()) {
|
||||
getLog().debug(sm.getString("abstractResource.getContentFail",
|
||||
getWebappPath()), ioe);
|
||||
}
|
||||
// Don't return corrupted content
|
||||
return null;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Certificate[] getCertificates() {
|
||||
if (!readCerts) {
|
||||
// TODO - get content first
|
||||
throw new IllegalStateException();
|
||||
}
|
||||
return certificates;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Manifest getManifest() {
|
||||
return archiveResourceSet.getManifest();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final InputStream doGetInputStream() {
|
||||
if (isDirectory()) {
|
||||
return null;
|
||||
}
|
||||
return getJarInputStreamWrapper();
|
||||
}
|
||||
|
||||
protected abstract JarInputStreamWrapper getJarInputStreamWrapper();
|
||||
|
||||
/**
|
||||
* This wrapper assumes that the InputStream was created from a JarFile
|
||||
* obtained from a call to getArchiveResourceSet().openJarFile(). If this is
|
||||
* not the case then the usage counting in AbstractArchiveResourceSet will
|
||||
* break and the JarFile may be unexpectedly closed.
|
||||
*/
|
||||
protected class JarInputStreamWrapper extends InputStream {
|
||||
|
||||
private final JarEntry jarEntry;
|
||||
private final InputStream is;
|
||||
private final AtomicBoolean closed = new AtomicBoolean(false);
|
||||
|
||||
|
||||
public JarInputStreamWrapper(JarEntry jarEntry, InputStream is) {
|
||||
this.jarEntry = jarEntry;
|
||||
this.is = is;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return is.read();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
return is.read(b);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return is.read(b, off, len);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
return is.skip(n);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return is.available();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
if (closed.compareAndSet(false, true)) {
|
||||
// Must only call this once else the usage counting will break
|
||||
archiveResourceSet.closeJarFile();
|
||||
}
|
||||
is.close();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
is.mark(readlimit);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
is.reset();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return is.markSupported();
|
||||
}
|
||||
|
||||
public Certificate[] getCertificates() {
|
||||
return jarEntry.getCertificates();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,335 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.catalina.util.ResourceSet;
|
||||
import org.apache.tomcat.util.compat.JreCompat;
|
||||
|
||||
public abstract class AbstractArchiveResourceSet extends AbstractResourceSet {
|
||||
|
||||
private URL baseUrl;
|
||||
private String baseUrlString;
|
||||
|
||||
private JarFile archive = null;
|
||||
protected HashMap<String,JarEntry> archiveEntries = null;
|
||||
protected final Object archiveLock = new Object();
|
||||
private long archiveUseCount = 0;
|
||||
|
||||
|
||||
protected final void setBaseUrl(URL baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
if (baseUrl == null) {
|
||||
this.baseUrlString = null;
|
||||
} else {
|
||||
this.baseUrlString = baseUrl.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final URL getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
protected final String getBaseUrlString() {
|
||||
return baseUrlString;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final String[] list(String path) {
|
||||
checkPath(path);
|
||||
String webAppMount = getWebAppMount();
|
||||
|
||||
ArrayList<String> result = new ArrayList<>();
|
||||
if (path.startsWith(webAppMount)) {
|
||||
String pathInJar =
|
||||
getInternalPath() + path.substring(webAppMount.length());
|
||||
// Always strip off the leading '/' to get the JAR path
|
||||
if (pathInJar.length() > 0 && pathInJar.charAt(0) == '/') {
|
||||
pathInJar = pathInJar.substring(1);
|
||||
}
|
||||
for (String name : getArchiveEntries(false).keySet()) {
|
||||
if (name.length() > pathInJar.length() &&
|
||||
name.startsWith(pathInJar)) {
|
||||
if (name.charAt(name.length() - 1) == '/') {
|
||||
name = name.substring(
|
||||
pathInJar.length(), name.length() - 1);
|
||||
} else {
|
||||
name = name.substring(pathInJar.length());
|
||||
}
|
||||
if (name.length() == 0) {
|
||||
continue;
|
||||
}
|
||||
if (name.charAt(0) == '/') {
|
||||
name = name.substring(1);
|
||||
}
|
||||
if (name.length() > 0 && name.lastIndexOf('/') == -1) {
|
||||
result.add(name);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!path.endsWith("/")) {
|
||||
path = path + "/";
|
||||
}
|
||||
if (webAppMount.startsWith(path)) {
|
||||
int i = webAppMount.indexOf('/', path.length());
|
||||
if (i == -1) {
|
||||
return new String[] {webAppMount.substring(path.length())};
|
||||
} else {
|
||||
return new String[] {
|
||||
webAppMount.substring(path.length(), i)};
|
||||
}
|
||||
}
|
||||
}
|
||||
return result.toArray(new String[result.size()]);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final Set<String> listWebAppPaths(String path) {
|
||||
checkPath(path);
|
||||
String webAppMount = getWebAppMount();
|
||||
|
||||
ResourceSet<String> result = new ResourceSet<>();
|
||||
if (path.startsWith(webAppMount)) {
|
||||
String pathInJar =
|
||||
getInternalPath() + path.substring(webAppMount.length());
|
||||
// Always strip off the leading '/' to get the JAR path and make
|
||||
// sure it ends in '/'
|
||||
if (pathInJar.length() > 0) {
|
||||
if (pathInJar.charAt(pathInJar.length() - 1) != '/') {
|
||||
pathInJar = pathInJar.substring(1) + '/';
|
||||
}
|
||||
if (pathInJar.charAt(0) == '/') {
|
||||
pathInJar = pathInJar.substring(1);
|
||||
}
|
||||
}
|
||||
|
||||
for (String name : getArchiveEntries(false).keySet()) {
|
||||
if (name.length() > pathInJar.length() && name.startsWith(pathInJar)) {
|
||||
int nextSlash = name.indexOf('/', pathInJar.length());
|
||||
if (nextSlash != -1 && nextSlash != name.length() - 1) {
|
||||
name = name.substring(0, nextSlash + 1);
|
||||
}
|
||||
result.add(webAppMount + '/' + name.substring(getInternalPath().length()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!path.endsWith("/")) {
|
||||
path = path + "/";
|
||||
}
|
||||
if (webAppMount.startsWith(path)) {
|
||||
int i = webAppMount.indexOf('/', path.length());
|
||||
if (i == -1) {
|
||||
result.add(webAppMount + "/");
|
||||
} else {
|
||||
result.add(webAppMount.substring(0, i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
result.setLocked(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the map of entries in the archive. May return null in which case
|
||||
* {@link #getArchiveEntry(String)} should be used.
|
||||
*
|
||||
* @param single Is this request being make to support a single lookup? If
|
||||
* false, a map will always be returned. If true,
|
||||
* implementations may use this as a hint in determining the
|
||||
* optimum way to respond.
|
||||
*
|
||||
* @return The archives entries mapped to their names or null if
|
||||
* {@link #getArchiveEntry(String)} should be used.
|
||||
*/
|
||||
protected abstract HashMap<String,JarEntry> getArchiveEntries(boolean single);
|
||||
|
||||
|
||||
/**
|
||||
* Obtain a single entry from the archive. For performance reasons,
|
||||
* {@link #getArchiveEntries(boolean)} should always be called first and the
|
||||
* archive entry looked up in the map if one is returned. Only if that call
|
||||
* returns null should this method be used.
|
||||
*
|
||||
* @param pathInArchive The path in the archive of the entry required
|
||||
*
|
||||
* @return The specified archive entry or null if it does not exist
|
||||
*/
|
||||
protected abstract JarEntry getArchiveEntry(String pathInArchive);
|
||||
|
||||
@Override
|
||||
public final boolean mkdir(String path) {
|
||||
checkPath(path);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final boolean write(String path, InputStream is, boolean overwrite) {
|
||||
checkPath(path);
|
||||
|
||||
if (is == null) {
|
||||
throw new NullPointerException(
|
||||
sm.getString("dirResourceSet.writeNpe"));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final WebResource getResource(String path) {
|
||||
checkPath(path);
|
||||
String webAppMount = getWebAppMount();
|
||||
WebResourceRoot root = getRoot();
|
||||
|
||||
/*
|
||||
* Implementation notes
|
||||
*
|
||||
* The path parameter passed into this method always starts with '/'.
|
||||
*
|
||||
* The path parameter passed into this method may or may not end with a
|
||||
* '/'. JarFile.getEntry() will return a matching directory entry
|
||||
* whether or not the name ends in a '/'. However, if the entry is
|
||||
* requested without the '/' subsequent calls to JarEntry.isDirectory()
|
||||
* will return false.
|
||||
*
|
||||
* Paths in JARs never start with '/'. Leading '/' need to be removed
|
||||
* before any JarFile.getEntry() call.
|
||||
*/
|
||||
|
||||
// If the JAR has been mounted below the web application root, return
|
||||
// an empty resource for requests outside of the mount point.
|
||||
|
||||
if (path.startsWith(webAppMount)) {
|
||||
String pathInJar = getInternalPath() + path.substring(
|
||||
webAppMount.length(), path.length());
|
||||
// Always strip off the leading '/' to get the JAR path
|
||||
if (pathInJar.length() > 0 && pathInJar.charAt(0) == '/') {
|
||||
pathInJar = pathInJar.substring(1);
|
||||
}
|
||||
if (pathInJar.equals("")) {
|
||||
// Special case
|
||||
// This is a directory resource so the path must end with /
|
||||
if (!path.endsWith("/")) {
|
||||
path = path + "/";
|
||||
}
|
||||
return new JarResourceRoot(root, new File(getBase()),
|
||||
baseUrlString, path);
|
||||
} else {
|
||||
JarEntry jarEntry = null;
|
||||
if (isMultiRelease()) {
|
||||
// Calls JarFile.getJarEntry() which is multi-release aware
|
||||
jarEntry = getArchiveEntry(pathInJar);
|
||||
} else {
|
||||
Map<String,JarEntry> jarEntries = getArchiveEntries(true);
|
||||
if (!(pathInJar.charAt(pathInJar.length() - 1) == '/')) {
|
||||
if (jarEntries == null) {
|
||||
jarEntry = getArchiveEntry(pathInJar + '/');
|
||||
} else {
|
||||
jarEntry = jarEntries.get(pathInJar + '/');
|
||||
}
|
||||
if (jarEntry != null) {
|
||||
path = path + '/';
|
||||
}
|
||||
}
|
||||
if (jarEntry == null) {
|
||||
if (jarEntries == null) {
|
||||
jarEntry = getArchiveEntry(pathInJar);
|
||||
} else {
|
||||
jarEntry = jarEntries.get(pathInJar);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (jarEntry == null) {
|
||||
return new EmptyResource(root, path);
|
||||
} else {
|
||||
return createArchiveResource(jarEntry, path, getManifest());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return new EmptyResource(root, path);
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract boolean isMultiRelease();
|
||||
|
||||
protected abstract WebResource createArchiveResource(JarEntry jarEntry,
|
||||
String webAppPath, Manifest manifest);
|
||||
|
||||
@Override
|
||||
public final boolean isReadOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
if (readOnly) {
|
||||
// This is the hard-coded default - ignore the call
|
||||
return;
|
||||
}
|
||||
|
||||
throw new IllegalArgumentException(
|
||||
sm.getString("abstractArchiveResourceSet.setReadOnlyFalse"));
|
||||
}
|
||||
|
||||
protected JarFile openJarFile() throws IOException {
|
||||
synchronized (archiveLock) {
|
||||
if (archive == null) {
|
||||
archive = JreCompat.getInstance().jarFileNewInstance(getBase());
|
||||
}
|
||||
archiveUseCount++;
|
||||
return archive;
|
||||
}
|
||||
}
|
||||
|
||||
protected void closeJarFile() {
|
||||
synchronized (archiveLock) {
|
||||
archiveUseCount--;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gc() {
|
||||
synchronized (archiveLock) {
|
||||
if (archive != null && archiveUseCount == 0) {
|
||||
try {
|
||||
archive.close();
|
||||
} catch (IOException e) {
|
||||
// Log at least WARN
|
||||
}
|
||||
archive = null;
|
||||
archiveEntries = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.tomcat.util.compat.JrePlatform;
|
||||
import org.apache.tomcat.util.http.RequestUtil;
|
||||
|
||||
public abstract class AbstractFileResourceSet extends AbstractResourceSet {
|
||||
|
||||
protected static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private File fileBase;
|
||||
private String absoluteBase;
|
||||
private String canonicalBase;
|
||||
private boolean readOnly = false;
|
||||
|
||||
protected AbstractFileResourceSet(String internalPath) {
|
||||
setInternalPath(internalPath);
|
||||
}
|
||||
|
||||
protected final File getFileBase() {
|
||||
return fileBase;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
this.readOnly = readOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return readOnly;
|
||||
}
|
||||
|
||||
protected final File file(String name, boolean mustExist) {
|
||||
|
||||
if (name.equals("/")) {
|
||||
name = "";
|
||||
}
|
||||
File file = new File(fileBase, name);
|
||||
|
||||
// If the requested names ends in '/', the Java File API will return a
|
||||
// matching file if one exists. This isn't what we want as it is not
|
||||
// consistent with the Servlet spec rules for request mapping.
|
||||
if (name.endsWith("/") && file.isFile()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If the file/dir must exist but the identified file/dir can't be read
|
||||
// then signal that the resource was not found
|
||||
if (mustExist && !file.canRead()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If allow linking is enabled, files are not limited to being located
|
||||
// under the fileBase so all further checks are disabled.
|
||||
if (getRoot().getAllowLinking()) {
|
||||
return file;
|
||||
}
|
||||
|
||||
// Additional Windows specific checks to handle known problems with
|
||||
// File.getCanonicalPath()
|
||||
if (JrePlatform.IS_WINDOWS && isInvalidWindowsFilename(name)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Check that this file is located under the WebResourceSet's base
|
||||
String canPath = null;
|
||||
try {
|
||||
canPath = file.getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
if (canPath == null || !canPath.startsWith(canonicalBase)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Ensure that the file is not outside the fileBase. This should not be
|
||||
// possible for standard requests (the request is normalized early in
|
||||
// the request processing) but might be possible for some access via the
|
||||
// Servlet API (RequestDispatcher, HTTP/2 push etc.) therefore these
|
||||
// checks are retained as an additional safety measure
|
||||
// absoluteBase has been normalized so absPath needs to be normalized as
|
||||
// well.
|
||||
String absPath = normalize(file.getAbsolutePath());
|
||||
if (absoluteBase.length() > absPath.length()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Remove the fileBase location from the start of the paths since that
|
||||
// was not part of the requested path and the remaining check only
|
||||
// applies to the request path
|
||||
absPath = absPath.substring(absoluteBase.length());
|
||||
canPath = canPath.substring(canonicalBase.length());
|
||||
|
||||
// Case sensitivity check
|
||||
// The normalized requested path should be an exact match the equivalent
|
||||
// canonical path. If it is not, possible reasons include:
|
||||
// - case differences on case insensitive file systems
|
||||
// - Windows removing a trailing ' ' or '.' from the file name
|
||||
//
|
||||
// In all cases, a mis-match here results in the resource not being
|
||||
// found
|
||||
//
|
||||
// absPath is normalized so canPath needs to be normalized as well
|
||||
// Can't normalize canPath earlier as canonicalBase is not normalized
|
||||
if (canPath.length() > 0) {
|
||||
canPath = normalize(canPath);
|
||||
}
|
||||
if (!canPath.equals(absPath)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
private boolean isInvalidWindowsFilename(String name) {
|
||||
final int len = name.length();
|
||||
if (len == 0) {
|
||||
return false;
|
||||
}
|
||||
// This consistently ~10 times faster than the equivalent regular
|
||||
// expression irrespective of input length.
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = name.charAt(i);
|
||||
if (c == '\"' || c == '<' || c == '>') {
|
||||
// These characters are disallowed in Windows file names and
|
||||
// there are known problems for file names with these characters
|
||||
// when using File#getCanonicalPath().
|
||||
// Note: There are additional characters that are disallowed in
|
||||
// Windows file names but these are not known to cause
|
||||
// problems when using File#getCanonicalPath().
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Windows does not allow file names to end in ' ' unless specific low
|
||||
// level APIs are used to create the files that bypass various checks.
|
||||
// File names that end in ' ' are known to cause problems when using
|
||||
// File#getCanonicalPath().
|
||||
if (name.charAt(len -1) == ' ') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Return a context-relative path, beginning with a "/", that represents
|
||||
* the canonical version of the specified path after ".." and "." elements
|
||||
* are resolved out. If the specified path attempts to go outside the
|
||||
* boundaries of the current context (i.e. too many ".." path elements
|
||||
* are present), return <code>null</code> instead.
|
||||
*
|
||||
* @param path Path to be normalized
|
||||
*/
|
||||
private String normalize(String path) {
|
||||
return RequestUtil.normalize(path, File.separatorChar == '\\');
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getBaseUrl() {
|
||||
try {
|
||||
return getFileBase().toURI().toURL();
|
||||
} catch (MalformedURLException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This is a NO-OP by default for File based resource sets.
|
||||
*/
|
||||
@Override
|
||||
public void gc() {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------- Lifecycle methods
|
||||
|
||||
@Override
|
||||
protected void initInternal() throws LifecycleException {
|
||||
fileBase = new File(getBase(), getInternalPath());
|
||||
checkType(fileBase);
|
||||
|
||||
this.absoluteBase = normalize(fileBase.getAbsolutePath());
|
||||
|
||||
try {
|
||||
this.canonicalBase = fileBase.getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected abstract void checkType(File file);
|
||||
}
|
||||
106
java/org/apache/catalina/webresources/AbstractResource.java
Normal file
106
java/org/apache/catalina/webresources/AbstractResource.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.InputStream;
|
||||
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.tomcat.util.http.FastHttpDateFormat;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
public abstract class AbstractResource implements WebResource {
|
||||
|
||||
protected static final StringManager sm = StringManager.getManager(AbstractResource.class);
|
||||
|
||||
private final WebResourceRoot root;
|
||||
private final String webAppPath;
|
||||
|
||||
private String mimeType = null;
|
||||
private volatile String weakETag;
|
||||
|
||||
|
||||
protected AbstractResource(WebResourceRoot root, String webAppPath) {
|
||||
this.root = root;
|
||||
this.webAppPath = webAppPath;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final WebResourceRoot getWebResourceRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final String getWebappPath() {
|
||||
return webAppPath;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final String getLastModifiedHttp() {
|
||||
return FastHttpDateFormat.formatDate(getLastModified());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final String getETag() {
|
||||
if (weakETag == null) {
|
||||
synchronized (this) {
|
||||
if (weakETag == null) {
|
||||
long contentLength = getContentLength();
|
||||
long lastModified = getLastModified();
|
||||
if ((contentLength >= 0) || (lastModified >= 0)) {
|
||||
weakETag = "W/\"" + contentLength + "-" +
|
||||
lastModified + "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return weakETag;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setMimeType(String mimeType) {
|
||||
this.mimeType = mimeType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final String getMimeType() {
|
||||
return mimeType;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public final InputStream getInputStream() {
|
||||
InputStream is = doGetInputStream();
|
||||
|
||||
if (is == null || !root.getTrackLockedFiles()) {
|
||||
return is;
|
||||
}
|
||||
|
||||
return new TrackedInputStream(root, getName(), is);
|
||||
}
|
||||
|
||||
protected abstract InputStream doGetInputStream();
|
||||
|
||||
|
||||
protected abstract Log getLog();
|
||||
}
|
||||
140
java/org/apache/catalina/webresources/AbstractResourceSet.java
Normal file
140
java/org/apache/catalina/webresources/AbstractResourceSet.java
Normal file
@@ -0,0 +1,140 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.LifecycleState;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.catalina.WebResourceSet;
|
||||
import org.apache.catalina.util.LifecycleBase;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
public abstract class AbstractResourceSet extends LifecycleBase
|
||||
implements WebResourceSet {
|
||||
|
||||
private WebResourceRoot root;
|
||||
private String base;
|
||||
private String internalPath = "";
|
||||
private String webAppMount;
|
||||
private boolean classLoaderOnly;
|
||||
private boolean staticOnly;
|
||||
private Manifest manifest;
|
||||
|
||||
|
||||
protected static final StringManager sm = StringManager.getManager(AbstractResourceSet.class);
|
||||
|
||||
|
||||
protected final void checkPath(String path) {
|
||||
if (path == null || path.length() == 0 || path.charAt(0) != '/') {
|
||||
throw new IllegalArgumentException(
|
||||
sm.getString("abstractResourceSet.checkPath", path));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void setRoot(WebResourceRoot root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
protected final WebResourceRoot getRoot() {
|
||||
return root;
|
||||
}
|
||||
|
||||
|
||||
protected final String getInternalPath() {
|
||||
return internalPath;
|
||||
}
|
||||
|
||||
public final void setInternalPath(String internalPath) {
|
||||
checkPath(internalPath);
|
||||
// Optimise internal processing
|
||||
if (internalPath.equals("/")) {
|
||||
this.internalPath = "";
|
||||
} else {
|
||||
this.internalPath = internalPath;
|
||||
}
|
||||
}
|
||||
|
||||
public final void setWebAppMount(String webAppMount) {
|
||||
checkPath(webAppMount);
|
||||
// Optimise internal processing
|
||||
if (webAppMount.equals("/")) {
|
||||
this.webAppMount = "";
|
||||
} else {
|
||||
this.webAppMount = webAppMount;
|
||||
}
|
||||
}
|
||||
|
||||
protected final String getWebAppMount() {
|
||||
return webAppMount;
|
||||
}
|
||||
|
||||
public final void setBase(String base) {
|
||||
this.base = base;
|
||||
}
|
||||
|
||||
protected final String getBase() {
|
||||
return base;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getClassLoaderOnly() {
|
||||
return classLoaderOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClassLoaderOnly(boolean classLoaderOnly) {
|
||||
this.classLoaderOnly = classLoaderOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getStaticOnly() {
|
||||
return staticOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStaticOnly(boolean staticOnly) {
|
||||
this.staticOnly = staticOnly;
|
||||
}
|
||||
|
||||
protected final void setManifest(Manifest manifest) {
|
||||
this.manifest = manifest;
|
||||
}
|
||||
|
||||
protected final Manifest getManifest() {
|
||||
return manifest;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------- Lifecycle methods
|
||||
@Override
|
||||
protected final void startInternal() throws LifecycleException {
|
||||
setState(LifecycleState.STARTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void stopInternal() throws LifecycleException {
|
||||
setState(LifecycleState.STOPPING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected final void destroyInternal() throws LifecycleException {
|
||||
gc();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
public abstract class AbstractSingleArchiveResource extends AbstractArchiveResource {
|
||||
|
||||
protected AbstractSingleArchiveResource(AbstractArchiveResourceSet archiveResourceSet, String webAppPath,
|
||||
String baseUrl, JarEntry jarEntry, String codeBaseUrl) {
|
||||
super(archiveResourceSet, webAppPath, baseUrl, jarEntry, codeBaseUrl);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected JarInputStreamWrapper getJarInputStreamWrapper() {
|
||||
JarFile jarFile = null;
|
||||
try {
|
||||
jarFile = getArchiveResourceSet().openJarFile();
|
||||
// Need to create a new JarEntry so the certificates can be read
|
||||
JarEntry jarEntry = jarFile.getJarEntry(getResource().getName());
|
||||
InputStream is = jarFile.getInputStream(jarEntry);
|
||||
return new JarInputStreamWrapper(jarEntry, is);
|
||||
} catch (IOException e) {
|
||||
if (getLog().isDebugEnabled()) {
|
||||
getLog().debug(sm.getString("jarResource.getInputStreamFail",
|
||||
getResource().getName(), getBaseUrl()), e);
|
||||
}
|
||||
if (jarFile != null) {
|
||||
getArchiveResourceSet().closeJarFile();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.tomcat.util.buf.UriUtil;
|
||||
import org.apache.tomcat.util.compat.JreCompat;
|
||||
|
||||
/**
|
||||
* Base class for a {@link org.apache.catalina.WebResourceSet} based on a
|
||||
* single, rather than nested, archive.
|
||||
*/
|
||||
public abstract class AbstractSingleArchiveResourceSet extends AbstractArchiveResourceSet {
|
||||
|
||||
private volatile Boolean multiRelease;
|
||||
|
||||
/**
|
||||
* A no argument constructor is required for this to work with the digester.
|
||||
*/
|
||||
public AbstractSingleArchiveResourceSet() {
|
||||
}
|
||||
|
||||
|
||||
public AbstractSingleArchiveResourceSet(WebResourceRoot root, String webAppMount, String base,
|
||||
String internalPath) throws IllegalArgumentException {
|
||||
setRoot(root);
|
||||
setWebAppMount(webAppMount);
|
||||
setBase(base);
|
||||
setInternalPath(internalPath);
|
||||
|
||||
if (getRoot().getState().isAvailable()) {
|
||||
try {
|
||||
start();
|
||||
} catch (LifecycleException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected HashMap<String,JarEntry> getArchiveEntries(boolean single) {
|
||||
synchronized (archiveLock) {
|
||||
if (archiveEntries == null && !single) {
|
||||
JarFile jarFile = null;
|
||||
archiveEntries = new HashMap<>();
|
||||
try {
|
||||
jarFile = openJarFile();
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
archiveEntries.put(entry.getName(), entry);
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// Should never happen
|
||||
archiveEntries = null;
|
||||
throw new IllegalStateException(ioe);
|
||||
} finally {
|
||||
if (jarFile != null) {
|
||||
closeJarFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
return archiveEntries;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected JarEntry getArchiveEntry(String pathInArchive) {
|
||||
JarFile jarFile = null;
|
||||
try {
|
||||
jarFile = openJarFile();
|
||||
return jarFile.getJarEntry(pathInArchive);
|
||||
} catch (IOException ioe) {
|
||||
// Should never happen
|
||||
throw new IllegalStateException(ioe);
|
||||
} finally {
|
||||
if (jarFile != null) {
|
||||
closeJarFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean isMultiRelease() {
|
||||
if (multiRelease == null) {
|
||||
synchronized (archiveLock) {
|
||||
if (multiRelease == null) {
|
||||
JarFile jarFile = null;
|
||||
try {
|
||||
jarFile = openJarFile();
|
||||
multiRelease = Boolean.valueOf(
|
||||
JreCompat.getInstance().jarFileIsMultiRelease(jarFile));
|
||||
} catch (IOException ioe) {
|
||||
// Should never happen
|
||||
throw new IllegalStateException(ioe);
|
||||
} finally {
|
||||
if (jarFile != null) {
|
||||
closeJarFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return multiRelease.booleanValue();
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------- Lifecycle methods
|
||||
@Override
|
||||
protected void initInternal() throws LifecycleException {
|
||||
|
||||
try (JarFile jarFile = JreCompat.getInstance().jarFileNewInstance(getBase())) {
|
||||
setManifest(jarFile.getManifest());
|
||||
} catch (IOException ioe) {
|
||||
throw new IllegalArgumentException(ioe);
|
||||
}
|
||||
|
||||
try {
|
||||
setBaseUrl(UriUtil.buildJarSafeUrl(new File(getBase())));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
329
java/org/apache/catalina/webresources/Cache.java
Normal file
329
java/org/apache/catalina/webresources/Cache.java
Normal file
@@ -0,0 +1,329 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentMap;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
public class Cache {
|
||||
|
||||
private static final Log log = LogFactory.getLog(Cache.class);
|
||||
protected static final StringManager sm = StringManager.getManager(Cache.class);
|
||||
|
||||
private static final long TARGET_FREE_PERCENT_GET = 5;
|
||||
private static final long TARGET_FREE_PERCENT_BACKGROUND = 10;
|
||||
|
||||
// objectMaxSize must be < maxSize/20
|
||||
private static final int OBJECT_MAX_SIZE_FACTOR = 20;
|
||||
|
||||
private final StandardRoot root;
|
||||
private final AtomicLong size = new AtomicLong(0);
|
||||
|
||||
private long ttl = 5000;
|
||||
private long maxSize = 10 * 1024 * 1024;
|
||||
private int objectMaxSize = (int) maxSize/OBJECT_MAX_SIZE_FACTOR;
|
||||
|
||||
private AtomicLong lookupCount = new AtomicLong(0);
|
||||
private AtomicLong hitCount = new AtomicLong(0);
|
||||
|
||||
private final ConcurrentMap<String,CachedResource> resourceCache =
|
||||
new ConcurrentHashMap<>();
|
||||
|
||||
public Cache(StandardRoot root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
protected WebResource getResource(String path, boolean useClassLoaderResources) {
|
||||
|
||||
if (noCache(path)) {
|
||||
return root.getResourceInternal(path, useClassLoaderResources);
|
||||
}
|
||||
|
||||
lookupCount.incrementAndGet();
|
||||
|
||||
CachedResource cacheEntry = resourceCache.get(path);
|
||||
|
||||
if (cacheEntry != null && !cacheEntry.validateResource(useClassLoaderResources)) {
|
||||
removeCacheEntry(path);
|
||||
cacheEntry = null;
|
||||
}
|
||||
|
||||
if (cacheEntry == null) {
|
||||
// Local copy to ensure consistency
|
||||
int objectMaxSizeBytes = getObjectMaxSizeBytes();
|
||||
CachedResource newCacheEntry = new CachedResource(this, root, path, getTtl(),
|
||||
objectMaxSizeBytes, useClassLoaderResources);
|
||||
|
||||
// Concurrent callers will end up with the same CachedResource
|
||||
// instance
|
||||
cacheEntry = resourceCache.putIfAbsent(path, newCacheEntry);
|
||||
|
||||
if (cacheEntry == null) {
|
||||
// newCacheEntry was inserted into the cache - validate it
|
||||
cacheEntry = newCacheEntry;
|
||||
cacheEntry.validateResource(useClassLoaderResources);
|
||||
|
||||
// Even if the resource content larger than objectMaxSizeBytes
|
||||
// there is still benefit in caching the resource metadata
|
||||
|
||||
long delta = cacheEntry.getSize();
|
||||
size.addAndGet(delta);
|
||||
|
||||
if (size.get() > maxSize) {
|
||||
// Process resources unordered for speed. Trades cache
|
||||
// efficiency (younger entries may be evicted before older
|
||||
// ones) for speed since this is on the critical path for
|
||||
// request processing
|
||||
long targetSize = maxSize * (100 - TARGET_FREE_PERCENT_GET) / 100;
|
||||
long newSize = evict(targetSize, resourceCache.values().iterator());
|
||||
if (newSize > maxSize) {
|
||||
// Unable to create sufficient space for this resource
|
||||
// Remove it from the cache
|
||||
removeCacheEntry(path);
|
||||
log.warn(sm.getString("cache.addFail", path, root.getContext().getName()));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Another thread added the entry to the cache
|
||||
// Make sure it is validated
|
||||
cacheEntry.validateResource(useClassLoaderResources);
|
||||
}
|
||||
} else {
|
||||
hitCount.incrementAndGet();
|
||||
}
|
||||
|
||||
return cacheEntry;
|
||||
}
|
||||
|
||||
protected WebResource[] getResources(String path, boolean useClassLoaderResources) {
|
||||
lookupCount.incrementAndGet();
|
||||
|
||||
// Don't call noCache(path) since the class loader only caches
|
||||
// individual resources. Therefore, always cache collections here
|
||||
|
||||
CachedResource cacheEntry = resourceCache.get(path);
|
||||
|
||||
if (cacheEntry != null && !cacheEntry.validateResources(useClassLoaderResources)) {
|
||||
removeCacheEntry(path);
|
||||
cacheEntry = null;
|
||||
}
|
||||
|
||||
if (cacheEntry == null) {
|
||||
// Local copy to ensure consistency
|
||||
int objectMaxSizeBytes = getObjectMaxSizeBytes();
|
||||
CachedResource newCacheEntry = new CachedResource(this, root, path, getTtl(),
|
||||
objectMaxSizeBytes, useClassLoaderResources);
|
||||
|
||||
// Concurrent callers will end up with the same CachedResource
|
||||
// instance
|
||||
cacheEntry = resourceCache.putIfAbsent(path, newCacheEntry);
|
||||
|
||||
if (cacheEntry == null) {
|
||||
// newCacheEntry was inserted into the cache - validate it
|
||||
cacheEntry = newCacheEntry;
|
||||
cacheEntry.validateResources(useClassLoaderResources);
|
||||
|
||||
// Content will not be cached but we still need metadata size
|
||||
long delta = cacheEntry.getSize();
|
||||
size.addAndGet(delta);
|
||||
|
||||
if (size.get() > maxSize) {
|
||||
// Process resources unordered for speed. Trades cache
|
||||
// efficiency (younger entries may be evicted before older
|
||||
// ones) for speed since this is on the critical path for
|
||||
// request processing
|
||||
long targetSize = maxSize * (100 - TARGET_FREE_PERCENT_GET) / 100;
|
||||
long newSize = evict(targetSize, resourceCache.values().iterator());
|
||||
if (newSize > maxSize) {
|
||||
// Unable to create sufficient space for this resource
|
||||
// Remove it from the cache
|
||||
removeCacheEntry(path);
|
||||
log.warn(sm.getString("cache.addFail", path));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Another thread added the entry to the cache
|
||||
// Make sure it is validated
|
||||
cacheEntry.validateResources(useClassLoaderResources);
|
||||
}
|
||||
} else {
|
||||
hitCount.incrementAndGet();
|
||||
}
|
||||
|
||||
return cacheEntry.getWebResources();
|
||||
}
|
||||
|
||||
protected void backgroundProcess() {
|
||||
// Create an ordered set of all cached resources with the least recently
|
||||
// used first. This is a background process so we can afford to take the
|
||||
// time to order the elements first
|
||||
TreeSet<CachedResource> orderedResources =
|
||||
new TreeSet<>(new EvictionOrder());
|
||||
orderedResources.addAll(resourceCache.values());
|
||||
|
||||
Iterator<CachedResource> iter = orderedResources.iterator();
|
||||
|
||||
long targetSize =
|
||||
maxSize * (100 - TARGET_FREE_PERCENT_BACKGROUND) / 100;
|
||||
long newSize = evict(targetSize, iter);
|
||||
|
||||
if (newSize > targetSize) {
|
||||
log.info(sm.getString("cache.backgroundEvictFail",
|
||||
Long.valueOf(TARGET_FREE_PERCENT_BACKGROUND),
|
||||
root.getContext().getName(),
|
||||
Long.valueOf(newSize / 1024)));
|
||||
}
|
||||
}
|
||||
|
||||
private boolean noCache(String path) {
|
||||
// Don't cache classes. The class loader handles this.
|
||||
// Don't cache JARs. The ResourceSet handles this.
|
||||
if ((path.endsWith(".class") &&
|
||||
(path.startsWith("/WEB-INF/classes/") || path.startsWith("/WEB-INF/lib/")))
|
||||
||
|
||||
(path.startsWith("/WEB-INF/lib/") && path.endsWith(".jar"))) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private long evict(long targetSize, Iterator<CachedResource> iter) {
|
||||
|
||||
long now = System.currentTimeMillis();
|
||||
|
||||
long newSize = size.get();
|
||||
|
||||
while (newSize > targetSize && iter.hasNext()) {
|
||||
CachedResource resource = iter.next();
|
||||
|
||||
// Don't expire anything that has been checked within the TTL
|
||||
if (resource.getNextCheck() > now) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Remove the entry from the cache
|
||||
removeCacheEntry(resource.getWebappPath());
|
||||
|
||||
newSize = size.get();
|
||||
}
|
||||
|
||||
return newSize;
|
||||
}
|
||||
|
||||
void removeCacheEntry(String path) {
|
||||
// With concurrent calls for the same path, the entry is only removed
|
||||
// once and the cache size is only updated (if required) once.
|
||||
CachedResource cachedResource = resourceCache.remove(path);
|
||||
if (cachedResource != null) {
|
||||
long delta = cachedResource.getSize();
|
||||
size.addAndGet(-delta);
|
||||
}
|
||||
}
|
||||
|
||||
public long getTtl() {
|
||||
return ttl;
|
||||
}
|
||||
|
||||
public void setTtl(long ttl) {
|
||||
this.ttl = ttl;
|
||||
}
|
||||
|
||||
public long getMaxSize() {
|
||||
// Internally bytes, externally kilobytes
|
||||
return maxSize / 1024;
|
||||
}
|
||||
|
||||
public void setMaxSize(long maxSize) {
|
||||
// Internally bytes, externally kilobytes
|
||||
this.maxSize = maxSize * 1024;
|
||||
}
|
||||
|
||||
public long getLookupCount() {
|
||||
return lookupCount.get();
|
||||
}
|
||||
|
||||
public long getHitCount() {
|
||||
return hitCount.get();
|
||||
}
|
||||
|
||||
public void setObjectMaxSize(int objectMaxSize) {
|
||||
if (objectMaxSize * 1024L > Integer.MAX_VALUE) {
|
||||
log.warn(sm.getString("cache.objectMaxSizeTooBigBytes", Integer.valueOf(objectMaxSize)));
|
||||
this.objectMaxSize = Integer.MAX_VALUE;
|
||||
}
|
||||
// Internally bytes, externally kilobytes
|
||||
this.objectMaxSize = objectMaxSize * 1024;
|
||||
}
|
||||
|
||||
public int getObjectMaxSize() {
|
||||
// Internally bytes, externally kilobytes
|
||||
return objectMaxSize / 1024;
|
||||
}
|
||||
|
||||
public int getObjectMaxSizeBytes() {
|
||||
return objectMaxSize;
|
||||
}
|
||||
|
||||
void enforceObjectMaxSizeLimit() {
|
||||
long limit = maxSize / OBJECT_MAX_SIZE_FACTOR;
|
||||
if (limit > Integer.MAX_VALUE) {
|
||||
return;
|
||||
}
|
||||
if (objectMaxSize > limit) {
|
||||
log.warn(sm.getString("cache.objectMaxSizeTooBig",
|
||||
Integer.valueOf(objectMaxSize / 1024), Integer.valueOf((int)limit / 1024)));
|
||||
objectMaxSize = (int) limit;
|
||||
}
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
resourceCache.clear();
|
||||
size.set(0);
|
||||
}
|
||||
|
||||
public long getSize() {
|
||||
return size.get() / 1024;
|
||||
}
|
||||
|
||||
private static class EvictionOrder implements Comparator<CachedResource> {
|
||||
|
||||
@Override
|
||||
public int compare(CachedResource cr1, CachedResource cr2) {
|
||||
long nc1 = cr1.getNextCheck();
|
||||
long nc2 = cr2.getNextCheck();
|
||||
|
||||
// Oldest resource should be first (so iterator goes from oldest to
|
||||
// youngest.
|
||||
if (nc1 == nc2) {
|
||||
return 0;
|
||||
} else if (nc1 > nc2) {
|
||||
return -1;
|
||||
} else {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
601
java/org/apache/catalina/webresources/CachedResource.java
Normal file
601
java/org/apache/catalina/webresources/CachedResource.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
public class ClasspathURLStreamHandler extends URLStreamHandler {
|
||||
|
||||
private static final StringManager sm =
|
||||
StringManager.getManager(ClasspathURLStreamHandler.class);
|
||||
|
||||
|
||||
@Override
|
||||
protected URLConnection openConnection(URL u) throws IOException {
|
||||
String path = u.getPath();
|
||||
|
||||
// Thread context class loader first
|
||||
URL classpathUrl = Thread.currentThread().getContextClassLoader().getResource(path);
|
||||
if (classpathUrl == null) {
|
||||
// This class's class loader if no joy with the tccl
|
||||
classpathUrl = ClasspathURLStreamHandler.class.getResource(path);
|
||||
}
|
||||
|
||||
if (classpathUrl == null) {
|
||||
throw new FileNotFoundException(sm.getString("classpathUrlStreamHandler.notFound", u));
|
||||
}
|
||||
|
||||
return classpathUrl.openConnection();
|
||||
}
|
||||
}
|
||||
279
java/org/apache/catalina/webresources/DirResourceSet.java
Normal file
279
java/org/apache/catalina/webresources/DirResourceSet.java
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.util.Set;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.catalina.WebResourceRoot.ResourceSetType;
|
||||
import org.apache.catalina.util.ResourceSet;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Represents a {@link org.apache.catalina.WebResourceSet} based on a directory.
|
||||
*/
|
||||
public class DirResourceSet extends AbstractFileResourceSet {
|
||||
|
||||
private static final Log log = LogFactory.getLog(DirResourceSet.class);
|
||||
|
||||
/**
|
||||
* A no argument constructor is required for this to work with the digester.
|
||||
*/
|
||||
public DirResourceSet() {
|
||||
super("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link org.apache.catalina.WebResourceSet} based on a
|
||||
* directory.
|
||||
*
|
||||
* @param root The {@link WebResourceRoot} this new
|
||||
* {@link org.apache.catalina.WebResourceSet} will
|
||||
* be added to.
|
||||
* @param webAppMount The path within the web application at which this
|
||||
* {@link org.apache.catalina.WebResourceSet} will
|
||||
* be mounted. For example, to add a directory of
|
||||
* JARs to a web application, the directory would
|
||||
* be mounted at "/WEB-INF/lib/"
|
||||
* @param base The absolute path to the directory on the file
|
||||
* system from which the resources will be served.
|
||||
* @param internalPath The path within this new {@link
|
||||
* org.apache.catalina.WebResourceSet} where
|
||||
* resources will be served from.
|
||||
*/
|
||||
public DirResourceSet(WebResourceRoot root, String webAppMount, String base,
|
||||
String internalPath) {
|
||||
super(internalPath);
|
||||
setRoot(root);
|
||||
setWebAppMount(webAppMount);
|
||||
setBase(base);
|
||||
|
||||
if (root.getContext().getAddWebinfClassesResources()) {
|
||||
File f = new File(base, internalPath);
|
||||
f = new File(f, "/WEB-INF/classes/META-INF/resources");
|
||||
|
||||
if (f.isDirectory()) {
|
||||
root.createWebResourceSet(ResourceSetType.RESOURCE_JAR, "/",
|
||||
f.getAbsolutePath(), null, "/");
|
||||
}
|
||||
}
|
||||
|
||||
if (getRoot().getState().isAvailable()) {
|
||||
try {
|
||||
start();
|
||||
} catch (LifecycleException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public WebResource getResource(String path) {
|
||||
checkPath(path);
|
||||
String webAppMount = getWebAppMount();
|
||||
WebResourceRoot root = getRoot();
|
||||
if (path.startsWith(webAppMount)) {
|
||||
File f = file(path.substring(webAppMount.length()), false);
|
||||
if (f == null) {
|
||||
return new EmptyResource(root, path);
|
||||
}
|
||||
if (!f.exists()) {
|
||||
return new EmptyResource(root, path, f);
|
||||
}
|
||||
if (f.isDirectory() && path.charAt(path.length() - 1) != '/') {
|
||||
path = path + '/';
|
||||
}
|
||||
return new FileResource(root, path, f, isReadOnly(), getManifest());
|
||||
} else {
|
||||
return new EmptyResource(root, path);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] list(String path) {
|
||||
checkPath(path);
|
||||
String webAppMount = getWebAppMount();
|
||||
if (path.startsWith(webAppMount)) {
|
||||
File f = file(path.substring(webAppMount.length()), true);
|
||||
if (f == null) {
|
||||
return EMPTY_STRING_ARRAY;
|
||||
}
|
||||
String[] result = f.list();
|
||||
if (result == null) {
|
||||
return EMPTY_STRING_ARRAY;
|
||||
} else {
|
||||
return result;
|
||||
}
|
||||
} else {
|
||||
if (!path.endsWith("/")) {
|
||||
path = path + "/";
|
||||
}
|
||||
if (webAppMount.startsWith(path)) {
|
||||
int i = webAppMount.indexOf('/', path.length());
|
||||
if (i == -1) {
|
||||
return new String[] {webAppMount.substring(path.length())};
|
||||
} else {
|
||||
return new String[] {
|
||||
webAppMount.substring(path.length(), i)};
|
||||
}
|
||||
}
|
||||
return EMPTY_STRING_ARRAY;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> listWebAppPaths(String path) {
|
||||
checkPath(path);
|
||||
String webAppMount = getWebAppMount();
|
||||
ResourceSet<String> result = new ResourceSet<>();
|
||||
if (path.startsWith(webAppMount)) {
|
||||
File f = file(path.substring(webAppMount.length()), true);
|
||||
if (f != null) {
|
||||
File[] list = f.listFiles();
|
||||
if (list != null) {
|
||||
for (File entry : list) {
|
||||
StringBuilder sb = new StringBuilder(path);
|
||||
if (path.charAt(path.length() - 1) != '/') {
|
||||
sb.append('/');
|
||||
}
|
||||
sb.append(entry.getName());
|
||||
if (entry.isDirectory()) {
|
||||
sb.append('/');
|
||||
}
|
||||
result.add(sb.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!path.endsWith("/")) {
|
||||
path = path + "/";
|
||||
}
|
||||
if (webAppMount.startsWith(path)) {
|
||||
int i = webAppMount.indexOf('/', path.length());
|
||||
if (i == -1) {
|
||||
result.add(webAppMount + "/");
|
||||
} else {
|
||||
result.add(webAppMount.substring(0, i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
result.setLocked(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String path) {
|
||||
checkPath(path);
|
||||
if (isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
String webAppMount = getWebAppMount();
|
||||
if (path.startsWith(webAppMount)) {
|
||||
File f = file(path.substring(webAppMount.length()), false);
|
||||
if (f == null) {
|
||||
return false;
|
||||
}
|
||||
return f.mkdir();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean write(String path, InputStream is, boolean overwrite) {
|
||||
checkPath(path);
|
||||
|
||||
if (is == null) {
|
||||
throw new NullPointerException(
|
||||
sm.getString("dirResourceSet.writeNpe"));
|
||||
}
|
||||
|
||||
if (isReadOnly()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// write() is meant to create a file so ensure that the path doesn't
|
||||
// end in '/'
|
||||
if (path.endsWith("/")) {
|
||||
return false;
|
||||
}
|
||||
|
||||
File dest = null;
|
||||
String webAppMount = getWebAppMount();
|
||||
if (path.startsWith(webAppMount)) {
|
||||
dest = file(path.substring(webAppMount.length()), false);
|
||||
if (dest == null) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (dest.exists() && !overwrite) {
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
if (overwrite) {
|
||||
Files.copy(is, dest.toPath(), StandardCopyOption.REPLACE_EXISTING);
|
||||
} else {
|
||||
Files.copy(is, dest.toPath());
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkType(File file) {
|
||||
if (file.isDirectory() == false) {
|
||||
throw new IllegalArgumentException(sm.getString("dirResourceSet.notDirectory",
|
||||
getBase(), File.separator, getInternalPath()));
|
||||
}
|
||||
}
|
||||
|
||||
//-------------------------------------------------------- Lifecycle methods
|
||||
@Override
|
||||
protected void initInternal() throws LifecycleException {
|
||||
super.initInternal();
|
||||
// Is this an exploded web application?
|
||||
if (getWebAppMount().equals("")) {
|
||||
// Look for a manifest
|
||||
File mf = file("META-INF/MANIFEST.MF", true);
|
||||
if (mf != null && mf.isFile()) {
|
||||
try (FileInputStream fis = new FileInputStream(mf)) {
|
||||
setManifest(new Manifest(fis));
|
||||
} catch (IOException e) {
|
||||
log.warn(sm.getString("dirResourceSet.manifestFail", mf.getAbsolutePath()), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
172
java/org/apache/catalina/webresources/EmptyResource.java
Normal file
172
java/org/apache/catalina/webresources/EmptyResource.java
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
|
||||
public class EmptyResource implements WebResource {
|
||||
|
||||
private final WebResourceRoot root;
|
||||
private final String webAppPath;
|
||||
private final File file;
|
||||
|
||||
public EmptyResource(WebResourceRoot root, String webAppPath) {
|
||||
this(root, webAppPath, null);
|
||||
}
|
||||
|
||||
public EmptyResource(WebResourceRoot root, String webAppPath, File file) {
|
||||
this.root = root;
|
||||
this.webAppPath = webAppPath;
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getLastModifiedHttp() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
int index = webAppPath.lastIndexOf('/');
|
||||
if (index == -1) {
|
||||
return webAppPath;
|
||||
} else {
|
||||
return webAppPath.substring(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCanonicalPath() {
|
||||
if (file == null) {
|
||||
return null;
|
||||
} else {
|
||||
try {
|
||||
return file.getCanonicalPath();
|
||||
} catch (IOException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWebappPath() {
|
||||
return webAppPath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getETag() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMimeType(String mimeType) {
|
||||
// NOOP
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMimeType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getContent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCreation() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getCodeBase() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate[] getCertificates() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Manifest getManifest() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WebResourceRoot getWebResourceRoot() {
|
||||
return root;
|
||||
}
|
||||
}
|
||||
179
java/org/apache/catalina/webresources/EmptyResourceSet.java
Normal file
179
java/org/apache/catalina/webresources/EmptyResourceSet.java
Normal file
@@ -0,0 +1,179 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.LifecycleState;
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.catalina.WebResourceSet;
|
||||
import org.apache.catalina.util.LifecycleBase;
|
||||
|
||||
/**
|
||||
* A {@link WebResourceSet} implementation that is not backed by a file system
|
||||
* and behaves as if it has no resources available. This is primarily used in
|
||||
* embedded mode when the web application is configured entirely
|
||||
* programmatically and does not use any static resources from the file system.
|
||||
*/
|
||||
public class EmptyResourceSet extends LifecycleBase implements WebResourceSet {
|
||||
|
||||
private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
|
||||
private WebResourceRoot root;
|
||||
private boolean classLoaderOnly;
|
||||
private boolean staticOnly;
|
||||
|
||||
public EmptyResourceSet(WebResourceRoot root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This implementation always returns an {@link EmptyResource}.
|
||||
*/
|
||||
@Override
|
||||
public WebResource getResource(String path) {
|
||||
return new EmptyResource(root, path);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This implementation always returns an empty array.
|
||||
*/
|
||||
@Override
|
||||
public String[] list(String path) {
|
||||
return EMPTY_STRING_ARRAY;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This implementation always returns an empty set.
|
||||
*/
|
||||
@Override
|
||||
public Set<String> listWebAppPaths(String path) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This implementation always returns false.
|
||||
*/
|
||||
@Override
|
||||
public boolean mkdir(String path) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This implementation always returns false.
|
||||
*/
|
||||
@Override
|
||||
public boolean write(String path, InputStream is, boolean overwrite) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setRoot(WebResourceRoot root) {
|
||||
this.root = root;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getClassLoaderOnly() {
|
||||
return classLoaderOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setClassLoaderOnly(boolean classLoaderOnly) {
|
||||
this.classLoaderOnly = classLoaderOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean getStaticOnly() {
|
||||
return staticOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setStaticOnly(boolean staticOnly) {
|
||||
this.staticOnly = staticOnly;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This implementation always returns null.
|
||||
*/
|
||||
@Override
|
||||
public URL getBaseUrl() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* Calls to this method will be ignored as this implementation always read
|
||||
* only.
|
||||
*/
|
||||
@Override
|
||||
public void setReadOnly(boolean readOnly) {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* This implementation always returns true.
|
||||
*/
|
||||
@Override
|
||||
public boolean isReadOnly() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gc() {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initInternal() throws LifecycleException {
|
||||
// NO-OP
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void startInternal() throws LifecycleException {
|
||||
setState(LifecycleState.STARTING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void stopInternal() throws LifecycleException {
|
||||
setState(LifecycleState.STOPPING);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void destroyInternal() throws LifecycleException {
|
||||
// NO-OP
|
||||
}
|
||||
}
|
||||
106
java/org/apache/catalina/webresources/ExtractingRoot.java
Normal file
106
java/org/apache/catalina/webresources/ExtractingRoot.java
Normal file
@@ -0,0 +1,106 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.catalina.startup.ExpandWar;
|
||||
import org.apache.catalina.util.IOTools;
|
||||
import org.apache.tomcat.util.res.StringManager;
|
||||
|
||||
/**
|
||||
* If the main resources are packaged as a WAR file then any JARs will be
|
||||
* extracted to the work directory and used from there.
|
||||
*/
|
||||
public class ExtractingRoot extends StandardRoot {
|
||||
|
||||
private static final StringManager sm = StringManager.getManager(ExtractingRoot.class);
|
||||
|
||||
private static final String APPLICATION_JARS_DIR = "application-jars";
|
||||
|
||||
@Override
|
||||
protected void processWebInfLib() throws LifecycleException {
|
||||
|
||||
// Don't extract JAR files unless the application is deployed as a
|
||||
// packed WAR file.
|
||||
if (!super.isPackedWarFile()) {
|
||||
super.processWebInfLib();
|
||||
return;
|
||||
}
|
||||
|
||||
File expansionTarget = getExpansionTarget();
|
||||
if (!expansionTarget.isDirectory()) {
|
||||
if (!expansionTarget.mkdirs()) {
|
||||
throw new LifecycleException(
|
||||
sm.getString("extractingRoot.targetFailed", expansionTarget));
|
||||
}
|
||||
}
|
||||
|
||||
WebResource[] possibleJars = listResources("/WEB-INF/lib", false);
|
||||
|
||||
for (WebResource possibleJar : possibleJars) {
|
||||
if (possibleJar.isFile() && possibleJar.getName().endsWith(".jar")) {
|
||||
try {
|
||||
File dest = new File(expansionTarget, possibleJar.getName());
|
||||
dest = dest.getCanonicalFile();
|
||||
try (InputStream sourceStream = possibleJar.getInputStream();
|
||||
OutputStream destStream= new FileOutputStream(dest)) {
|
||||
IOTools.flow(sourceStream, destStream);
|
||||
}
|
||||
|
||||
createWebResourceSet(ResourceSetType.CLASSES_JAR,
|
||||
"/WEB-INF/classes", dest.toURI().toURL(), "/");
|
||||
} catch (IOException ioe) {
|
||||
throw new LifecycleException(
|
||||
sm.getString("extractingRoot.jarFailed", possibleJar.getName()), ioe);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private File getExpansionTarget() {
|
||||
File tmpDir = (File) getContext().getServletContext().getAttribute(ServletContext.TEMPDIR);
|
||||
File expansionTarget = new File(tmpDir, APPLICATION_JARS_DIR);
|
||||
return expansionTarget;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean isPackedWarFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void stopInternal() throws LifecycleException {
|
||||
super.stopInternal();
|
||||
|
||||
if (super.isPackedWarFile()) {
|
||||
// Remove the extracted JARs from the work directory
|
||||
File expansionTarget = getExpansionTarget();
|
||||
ExpandWar.delete(expansionTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
297
java/org/apache/catalina/webresources/FileResource.java
Normal file
297
java/org/apache/catalina/webresources/FileResource.java
Normal file
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.BasicFileAttributes;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Represents a single resource (file or directory) that is located on a file
|
||||
* system.
|
||||
*/
|
||||
public class FileResource extends AbstractResource {
|
||||
|
||||
private static final Log log = LogFactory.getLog(FileResource.class);
|
||||
|
||||
private static final boolean PROPERTIES_NEED_CONVERT;
|
||||
static {
|
||||
boolean isEBCDIC = false;
|
||||
try {
|
||||
String encoding = System.getProperty("file.encoding");
|
||||
if (encoding.contains("EBCDIC")) {
|
||||
isEBCDIC = true;
|
||||
}
|
||||
} catch (SecurityException e) {
|
||||
// Ignore
|
||||
}
|
||||
PROPERTIES_NEED_CONVERT = isEBCDIC;
|
||||
}
|
||||
|
||||
|
||||
private final File resource;
|
||||
private final String name;
|
||||
private final boolean readOnly;
|
||||
private final Manifest manifest;
|
||||
private final boolean needConvert;
|
||||
|
||||
public FileResource(WebResourceRoot root, String webAppPath,
|
||||
File resource, boolean readOnly, Manifest manifest) {
|
||||
super(root,webAppPath);
|
||||
this.resource = resource;
|
||||
|
||||
if (webAppPath.charAt(webAppPath.length() - 1) == '/') {
|
||||
String realName = resource.getName() + '/';
|
||||
if (webAppPath.endsWith(realName)) {
|
||||
name = resource.getName();
|
||||
} else {
|
||||
// This is the root directory of a mounted ResourceSet
|
||||
// Need to return the mounted name, not the real name
|
||||
int endOfName = webAppPath.length() - 1;
|
||||
name = webAppPath.substring(
|
||||
webAppPath.lastIndexOf('/', endOfName - 1) + 1,
|
||||
endOfName);
|
||||
}
|
||||
} else {
|
||||
// Must be a file
|
||||
name = resource.getName();
|
||||
}
|
||||
|
||||
this.readOnly = readOnly;
|
||||
this.manifest = manifest;
|
||||
this.needConvert = PROPERTIES_NEED_CONVERT && name.endsWith(".properties");
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified() {
|
||||
return resource.lastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return resource.exists();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return resource.isDirectory();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return resource.isFile();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete() {
|
||||
if (readOnly) {
|
||||
return false;
|
||||
}
|
||||
return resource.delete();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
return getContentLengthInternal(needConvert);
|
||||
}
|
||||
|
||||
private long getContentLengthInternal(boolean convert) {
|
||||
if (convert) {
|
||||
byte[] content = getContent();
|
||||
if (content == null) {
|
||||
return -1;
|
||||
} else {
|
||||
return content.length;
|
||||
}
|
||||
}
|
||||
|
||||
if (isDirectory()) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return resource.length();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCanonicalPath() {
|
||||
try {
|
||||
return resource.getCanonicalPath();
|
||||
} catch (IOException ioe) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("fileResource.getCanonicalPathFail",
|
||||
resource.getPath()), ioe);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead() {
|
||||
return resource.canRead();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream doGetInputStream() {
|
||||
if (needConvert) {
|
||||
byte[] content = getContent();
|
||||
if (content == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new ByteArrayInputStream(content);
|
||||
}
|
||||
}
|
||||
try {
|
||||
return new FileInputStream(resource);
|
||||
} catch (FileNotFoundException fnfe) {
|
||||
// Race condition (file has been deleted) - not an error
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public final byte[] getContent() {
|
||||
// Use internal version to avoid loop when needConvert is true
|
||||
long len = getContentLengthInternal(false);
|
||||
|
||||
if (len > Integer.MAX_VALUE) {
|
||||
// Can't create an array that big
|
||||
throw new ArrayIndexOutOfBoundsException(sm.getString(
|
||||
"abstractResource.getContentTooLarge", getWebappPath(),
|
||||
Long.valueOf(len)));
|
||||
}
|
||||
|
||||
if (len < 0) {
|
||||
// Content is not applicable here (e.g. is a directory)
|
||||
return null;
|
||||
}
|
||||
|
||||
int size = (int) len;
|
||||
byte[] result = new byte[size];
|
||||
|
||||
int pos = 0;
|
||||
try (InputStream is = new FileInputStream(resource)) {
|
||||
while (pos < size) {
|
||||
int n = is.read(result, pos, size - pos);
|
||||
if (n < 0) {
|
||||
break;
|
||||
}
|
||||
pos += n;
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
if (getLog().isDebugEnabled()) {
|
||||
getLog().debug(sm.getString("abstractResource.getContentFail",
|
||||
getWebappPath()), ioe);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
if (needConvert) {
|
||||
// Workaround for certain files on platforms that use
|
||||
// EBCDIC encoding, when they are read through FileInputStream.
|
||||
// See commit message of rev.303915 for original details
|
||||
// https://svn.apache.org/viewvc?view=revision&revision=303915
|
||||
String str = new String(result);
|
||||
try {
|
||||
result = str.getBytes(StandardCharsets.UTF_8);
|
||||
} catch (Exception e) {
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getCreation() {
|
||||
try {
|
||||
BasicFileAttributes attrs = Files.readAttributes(resource.toPath(),
|
||||
BasicFileAttributes.class);
|
||||
return attrs.creationTime().toMillis();
|
||||
} catch (IOException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("fileResource.getCreationFail",
|
||||
resource.getPath()), e);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() {
|
||||
if (resource.exists()) {
|
||||
try {
|
||||
return resource.toURI().toURL();
|
||||
} catch (MalformedURLException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("fileResource.getUrlFail",
|
||||
resource.getPath()), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getCodeBase() {
|
||||
if (getWebappPath().startsWith("/WEB-INF/classes/") && name.endsWith(".class")) {
|
||||
return getWebResourceRoot().getResource("/WEB-INF/classes/").getURL();
|
||||
} else {
|
||||
return getURL();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate[] getCertificates() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Manifest getManifest() {
|
||||
return manifest;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Log getLog() {
|
||||
return log;
|
||||
}
|
||||
}
|
||||
176
java/org/apache/catalina/webresources/FileResourceSet.java
Normal file
176
java/org/apache/catalina/webresources/FileResourceSet.java
Normal file
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.catalina.util.ResourceSet;
|
||||
|
||||
/**
|
||||
* Represents a {@link org.apache.catalina.WebResourceSet} based on a single
|
||||
* file.
|
||||
*/
|
||||
public class FileResourceSet extends AbstractFileResourceSet {
|
||||
|
||||
/**
|
||||
* A no argument constructor is required for this to work with the digester.
|
||||
*/
|
||||
public FileResourceSet() {
|
||||
super("/");
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@link org.apache.catalina.WebResourceSet} based on a
|
||||
* file.
|
||||
*
|
||||
* @param root The {@link WebResourceRoot} this new
|
||||
* {@link org.apache.catalina.WebResourceSet} will
|
||||
* be added to.
|
||||
* @param webAppMount The path within the web application at which this
|
||||
* {@link org.apache.catalina.WebResourceSet} will
|
||||
* be mounted. For example, to add a directory of
|
||||
* JARs to a web application, the directory would
|
||||
* be mounted at "WEB-INF/lib/"
|
||||
* @param base The absolute path to the file on the file system
|
||||
* from which the resource will be served.
|
||||
* @param internalPath The path within this new {@link
|
||||
* org.apache.catalina.WebResourceSet} where
|
||||
* resources will be served from.
|
||||
*/
|
||||
public FileResourceSet(WebResourceRoot root, String webAppMount,
|
||||
String base, String internalPath) {
|
||||
super(internalPath);
|
||||
setRoot(root);
|
||||
setWebAppMount(webAppMount);
|
||||
setBase(base);
|
||||
|
||||
if (getRoot().getState().isAvailable()) {
|
||||
try {
|
||||
start();
|
||||
} catch (LifecycleException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public WebResource getResource(String path) {
|
||||
checkPath(path);
|
||||
|
||||
String webAppMount = getWebAppMount();
|
||||
WebResourceRoot root = getRoot();
|
||||
if (path.equals(webAppMount)) {
|
||||
File f = file("", true);
|
||||
if (f == null) {
|
||||
return new EmptyResource(root, path);
|
||||
}
|
||||
return new FileResource(root, path, f, isReadOnly(), null);
|
||||
}
|
||||
|
||||
if (path.charAt(path.length() - 1) != '/') {
|
||||
path = path + '/';
|
||||
}
|
||||
|
||||
if (webAppMount.startsWith(path)) {
|
||||
String name = path.substring(0, path.length() - 1);
|
||||
name = name.substring(name.lastIndexOf('/') + 1);
|
||||
if (name.length() > 0) {
|
||||
return new VirtualResource(root, path, name);
|
||||
}
|
||||
}
|
||||
return new EmptyResource(root, path);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String[] list(String path) {
|
||||
checkPath(path);
|
||||
|
||||
if (path.charAt(path.length() - 1) != '/') {
|
||||
path = path + '/';
|
||||
}
|
||||
String webAppMount = getWebAppMount();
|
||||
|
||||
if (webAppMount.startsWith(path)) {
|
||||
webAppMount = webAppMount.substring(path.length());
|
||||
if (webAppMount.equals(getFileBase().getName())) {
|
||||
return new String[] {getFileBase().getName()};
|
||||
} else {
|
||||
// Virtual directory
|
||||
int i = webAppMount.indexOf('/');
|
||||
if (i > 0) {
|
||||
return new String[] {webAppMount.substring(0, i)};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return EMPTY_STRING_ARRAY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> listWebAppPaths(String path) {
|
||||
checkPath(path);
|
||||
|
||||
ResourceSet<String> result = new ResourceSet<>();
|
||||
|
||||
if (path.charAt(path.length() - 1) != '/') {
|
||||
path = path + '/';
|
||||
}
|
||||
String webAppMount = getWebAppMount();
|
||||
|
||||
if (webAppMount.startsWith(path)) {
|
||||
webAppMount = webAppMount.substring(path.length());
|
||||
if (webAppMount.equals(getFileBase().getName())) {
|
||||
result.add(path + getFileBase().getName());
|
||||
} else {
|
||||
// Virtual directory
|
||||
int i = webAppMount.indexOf('/');
|
||||
if (i > 0) {
|
||||
result.add(path + webAppMount.substring(0, i + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
result.setLocked(true);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean mkdir(String path) {
|
||||
checkPath(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean write(String path, InputStream is, boolean overwrite) {
|
||||
checkPath(path);
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void checkType(File file) {
|
||||
if (file.isFile() == false) {
|
||||
throw new IllegalArgumentException(sm.getString("fileResourceSet.notFile",
|
||||
getBase(), File.separator, getInternalPath()));
|
||||
}
|
||||
}
|
||||
}
|
||||
43
java/org/apache/catalina/webresources/JarResource.java
Normal file
43
java/org/apache/catalina/webresources/JarResource.java
Normal file
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.util.jar.JarEntry;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
/**
|
||||
* Represents a single resource (file or directory) that is located within a
|
||||
* JAR.
|
||||
*/
|
||||
public class JarResource extends AbstractSingleArchiveResource {
|
||||
|
||||
private static final Log log = LogFactory.getLog(JarResource.class);
|
||||
|
||||
|
||||
public JarResource(AbstractArchiveResourceSet archiveResourceSet, String webAppPath,
|
||||
String baseUrl, JarEntry jarEntry) {
|
||||
super(archiveResourceSet, webAppPath, "jar:" + baseUrl + "!/", jarEntry, baseUrl);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Log getLog() {
|
||||
return log;
|
||||
}
|
||||
}
|
||||
161
java/org/apache/catalina/webresources/JarResourceRoot.java
Normal file
161
java/org/apache/catalina/webresources/JarResourceRoot.java
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.security.cert.Certificate;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
|
||||
public class JarResourceRoot extends AbstractResource {
|
||||
|
||||
private static final Log log = LogFactory.getLog(JarResourceRoot.class);
|
||||
|
||||
private final File base;
|
||||
private final String baseUrl;
|
||||
private final String name;
|
||||
|
||||
public JarResourceRoot(WebResourceRoot root, File base, String baseUrl,
|
||||
String webAppPath) {
|
||||
super(root, webAppPath);
|
||||
// Validate the webAppPath before going any further
|
||||
if (!webAppPath.endsWith("/")) {
|
||||
throw new IllegalArgumentException(sm.getString(
|
||||
"jarResourceRoot.invalidWebAppPath", webAppPath));
|
||||
}
|
||||
this.base = base;
|
||||
this.baseUrl = "jar:" + baseUrl;
|
||||
// Extract the name from the webAppPath
|
||||
// Strip the trailing '/' character
|
||||
String resourceName = webAppPath.substring(0, webAppPath.length() - 1);
|
||||
int i = resourceName.lastIndexOf('/');
|
||||
if (i > -1) {
|
||||
resourceName = resourceName.substring(i + 1);
|
||||
}
|
||||
name = resourceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getLastModified() {
|
||||
return base.lastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean exists() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean delete() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getContentLength() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCanonicalPath() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean canRead() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InputStream doGetInputStream() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getContent() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCreation() {
|
||||
return base.lastModified();
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getURL() {
|
||||
String url = baseUrl + "!/";
|
||||
try {
|
||||
return new URL(url);
|
||||
} catch (MalformedURLException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("fileResource.getUrlFail", url), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public URL getCodeBase() {
|
||||
try {
|
||||
return new URL(baseUrl);
|
||||
} catch (MalformedURLException e) {
|
||||
if (getLog().isDebugEnabled()) {
|
||||
getLog().debug(sm.getString("fileResource.getUrlFail", baseUrl), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@Override
|
||||
protected Log getLog() {
|
||||
return log;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Certificate[] getCertificates() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Manifest getManifest() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
68
java/org/apache/catalina/webresources/JarResourceSet.java
Normal file
68
java/org/apache/catalina/webresources/JarResourceSet.java
Normal file
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
|
||||
/**
|
||||
* Represents a {@link org.apache.catalina.WebResourceSet} based on a JAR file.
|
||||
*/
|
||||
public class JarResourceSet extends AbstractSingleArchiveResourceSet {
|
||||
|
||||
/**
|
||||
* A no argument constructor is required for this to work with the digester.
|
||||
*/
|
||||
public JarResourceSet() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link org.apache.catalina.WebResourceSet} based on a JAR
|
||||
* file.
|
||||
*
|
||||
* @param root The {@link WebResourceRoot} this new
|
||||
* {@link org.apache.catalina.WebResourceSet} will
|
||||
* be added to.
|
||||
* @param webAppMount The path within the web application at which this
|
||||
* {@link org.apache.catalina.WebResourceSet} will
|
||||
* be mounted.
|
||||
* @param base The absolute path to the JAR file on the file system
|
||||
* from which the resources will be served.
|
||||
* @param internalPath The path within this new {@link
|
||||
* org.apache.catalina.WebResourceSet} where
|
||||
* resources will be served from. E.g. for a
|
||||
* resource JAR, this would be "META-INF/resources"
|
||||
*
|
||||
* @throws IllegalArgumentException if the webAppMount or internalPath is
|
||||
* not valid (valid paths must start with '/')
|
||||
*/
|
||||
public JarResourceSet(WebResourceRoot root, String webAppMount, String base,
|
||||
String internalPath) throws IllegalArgumentException {
|
||||
super(root, webAppMount, base, internalPath);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected WebResource createArchiveResource(JarEntry jarEntry,
|
||||
String webAppPath, Manifest manifest) {
|
||||
return new JarResource(this, webAppPath, getBaseUrlString(), jarEntry);
|
||||
}
|
||||
}
|
||||
98
java/org/apache/catalina/webresources/JarWarResource.java
Normal file
98
java/org/apache/catalina/webresources/JarWarResource.java
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.buf.UriUtil;
|
||||
|
||||
/**
|
||||
* Represents a single resource (file or directory) that is located within a
|
||||
* JAR that in turn is located in a WAR file.
|
||||
*/
|
||||
public class JarWarResource extends AbstractArchiveResource {
|
||||
|
||||
private static final Log log = LogFactory.getLog(JarWarResource.class);
|
||||
|
||||
private final String archivePath;
|
||||
|
||||
public JarWarResource(AbstractArchiveResourceSet archiveResourceSet, String webAppPath,
|
||||
String baseUrl, JarEntry jarEntry, String archivePath) {
|
||||
|
||||
super(archiveResourceSet, webAppPath,
|
||||
"jar:war:" + baseUrl + UriUtil.getWarSeparator() + archivePath + "!/",
|
||||
jarEntry, "war:" + baseUrl + UriUtil.getWarSeparator() + archivePath);
|
||||
this.archivePath = archivePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected JarInputStreamWrapper getJarInputStreamWrapper() {
|
||||
JarFile warFile = null;
|
||||
JarInputStream jarIs = null;
|
||||
JarEntry entry = null;
|
||||
try {
|
||||
warFile = getArchiveResourceSet().openJarFile();
|
||||
JarEntry jarFileInWar = warFile.getJarEntry(archivePath);
|
||||
InputStream isInWar = warFile.getInputStream(jarFileInWar);
|
||||
|
||||
jarIs = new JarInputStream(isInWar);
|
||||
entry = jarIs.getNextJarEntry();
|
||||
while (entry != null &&
|
||||
!entry.getName().equals(getResource().getName())) {
|
||||
entry = jarIs.getNextJarEntry();
|
||||
}
|
||||
|
||||
if (entry == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new JarInputStreamWrapper(entry, jarIs);
|
||||
} catch (IOException e) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug(sm.getString("jarResource.getInputStreamFail",
|
||||
getResource().getName(), getBaseUrl()), e);
|
||||
}
|
||||
// Ensure jarIs is closed if there is an exception
|
||||
entry = null;
|
||||
return null;
|
||||
} finally {
|
||||
if (entry == null) {
|
||||
if (jarIs != null) {
|
||||
try {
|
||||
jarIs.close();
|
||||
} catch (IOException ioe) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
if (warFile != null) {
|
||||
getArchiveResourceSet().closeJarFile();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Log getLog() {
|
||||
return log;
|
||||
}
|
||||
}
|
||||
279
java/org/apache/catalina/webresources/JarWarResourceSet.java
Normal file
279
java/org/apache/catalina/webresources/JarWarResourceSet.java
Normal file
@@ -0,0 +1,279 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.MalformedURLException;
|
||||
import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.apache.catalina.LifecycleException;
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
import org.apache.tomcat.util.buf.UriUtil;
|
||||
import org.apache.tomcat.util.compat.JreCompat;
|
||||
|
||||
/**
|
||||
* Represents a {@link org.apache.catalina.WebResourceSet} based on a JAR file
|
||||
* that is nested inside a packed WAR file. This is only intended for internal
|
||||
* use within Tomcat and therefore cannot be created via configuration.
|
||||
*/
|
||||
public class JarWarResourceSet extends AbstractArchiveResourceSet {
|
||||
|
||||
private final String archivePath;
|
||||
|
||||
/**
|
||||
* Creates a new {@link org.apache.catalina.WebResourceSet} based on a JAR
|
||||
* file that is nested inside a WAR.
|
||||
*
|
||||
* @param root The {@link WebResourceRoot} this new
|
||||
* {@link org.apache.catalina.WebResourceSet} will
|
||||
* be added to.
|
||||
* @param webAppMount The path within the web application at which this
|
||||
* {@link org.apache.catalina.WebResourceSet} will
|
||||
* be mounted.
|
||||
* @param base The absolute path to the WAR file on the file system
|
||||
* in which the JAR is located.
|
||||
* @param archivePath The path within the WAR file where the JAR file is
|
||||
* located.
|
||||
* @param internalPath The path within this new {@link
|
||||
* org.apache.catalina.WebResourceSet} where
|
||||
* resources will be served from. E.g. for a
|
||||
* resource JAR, this would be "META-INF/resources"
|
||||
*
|
||||
* @throws IllegalArgumentException if the webAppMount or internalPath is
|
||||
* not valid (valid paths must start with '/')
|
||||
*/
|
||||
public JarWarResourceSet(WebResourceRoot root, String webAppMount,
|
||||
String base, String archivePath, String internalPath)
|
||||
throws IllegalArgumentException {
|
||||
setRoot(root);
|
||||
setWebAppMount(webAppMount);
|
||||
setBase(base);
|
||||
this.archivePath = archivePath;
|
||||
setInternalPath(internalPath);
|
||||
|
||||
if (getRoot().getState().isAvailable()) {
|
||||
try {
|
||||
start();
|
||||
} catch (LifecycleException e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected WebResource createArchiveResource(JarEntry jarEntry,
|
||||
String webAppPath, Manifest manifest) {
|
||||
return new JarWarResource(this, webAppPath, getBaseUrlString(), jarEntry, archivePath);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* JarWar can't optimise for a single resource so the Map is always
|
||||
* returned.
|
||||
*/
|
||||
@Override
|
||||
protected HashMap<String,JarEntry> getArchiveEntries(boolean single) {
|
||||
synchronized (archiveLock) {
|
||||
if (archiveEntries == null) {
|
||||
JarFile warFile = null;
|
||||
InputStream jarFileIs = null;
|
||||
archiveEntries = new HashMap<>();
|
||||
boolean multiRelease = false;
|
||||
try {
|
||||
warFile = openJarFile();
|
||||
JarEntry jarFileInWar = warFile.getJarEntry(archivePath);
|
||||
jarFileIs = warFile.getInputStream(jarFileInWar);
|
||||
|
||||
try (TomcatJarInputStream jarIs = new TomcatJarInputStream(jarFileIs)) {
|
||||
JarEntry entry = jarIs.getNextJarEntry();
|
||||
while (entry != null) {
|
||||
archiveEntries.put(entry.getName(), entry);
|
||||
entry = jarIs.getNextJarEntry();
|
||||
}
|
||||
Manifest m = jarIs.getManifest();
|
||||
setManifest(m);
|
||||
if (m != null && JreCompat.isJre9Available()) {
|
||||
String value = m.getMainAttributes().getValue("Multi-Release");
|
||||
if (value != null) {
|
||||
multiRelease = Boolean.parseBoolean(value);
|
||||
}
|
||||
}
|
||||
// Hack to work-around JarInputStream swallowing these
|
||||
// entries. TomcatJarInputStream is used above which
|
||||
// extends JarInputStream and the method that creates
|
||||
// the entries over-ridden so we can a) tell if the
|
||||
// entries are present and b) cache them so we can
|
||||
// access them here.
|
||||
entry = jarIs.getMetaInfEntry();
|
||||
if (entry != null) {
|
||||
archiveEntries.put(entry.getName(), entry);
|
||||
}
|
||||
entry = jarIs.getManifestEntry();
|
||||
if (entry != null) {
|
||||
archiveEntries.put(entry.getName(), entry);
|
||||
}
|
||||
}
|
||||
if (multiRelease) {
|
||||
processArchivesEntriesForMultiRelease();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
// Should never happen
|
||||
archiveEntries = null;
|
||||
throw new IllegalStateException(ioe);
|
||||
} finally {
|
||||
if (warFile != null) {
|
||||
closeJarFile();
|
||||
}
|
||||
if (jarFileIs != null) {
|
||||
try {
|
||||
jarFileIs.close();
|
||||
} catch (IOException e) {
|
||||
// Ignore
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return archiveEntries;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
protected void processArchivesEntriesForMultiRelease() {
|
||||
|
||||
int targetVersion = JreCompat.getInstance().jarFileRuntimeMajorVersion();
|
||||
|
||||
Map<String,VersionedJarEntry> versionedEntries = new HashMap<>();
|
||||
Iterator<Entry<String,JarEntry>> iter = archiveEntries.entrySet().iterator();
|
||||
while (iter.hasNext()) {
|
||||
Entry<String,JarEntry> entry = iter.next();
|
||||
String name = entry.getKey();
|
||||
if (name.startsWith("META-INF/versions/")) {
|
||||
// Remove the multi-release version
|
||||
iter.remove();
|
||||
|
||||
// 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) {
|
||||
VersionedJarEntry versionedJarEntry = versionedEntries.get(baseName);
|
||||
if (versionedJarEntry == null) {
|
||||
// No versioned entry found for this name. Create
|
||||
// one.
|
||||
versionedEntries.put(baseName,
|
||||
new VersionedJarEntry(version, entry.getValue()));
|
||||
} else {
|
||||
// Ignore any entry for which we have already found
|
||||
// a later version
|
||||
if (version > versionedJarEntry.getVersion()) {
|
||||
// Replace the entry targeted at an earlier
|
||||
// version
|
||||
versionedEntries.put(baseName,
|
||||
new VersionedJarEntry(version, entry.getValue()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Entry<String,VersionedJarEntry> versionedJarEntry : versionedEntries.entrySet()) {
|
||||
archiveEntries.put(versionedJarEntry.getKey(),
|
||||
versionedJarEntry.getValue().getJarEntry());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
* <p>
|
||||
* Should never be called since {@link #getArchiveEntries(boolean)} always
|
||||
* returns a Map.
|
||||
*/
|
||||
@Override
|
||||
protected JarEntry getArchiveEntry(String pathInArchive) {
|
||||
throw new IllegalStateException(sm.getString("jarWarResourceSet.codingError"));
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected boolean isMultiRelease() {
|
||||
// This always returns false otherwise the superclass will call
|
||||
// #getArchiveEntry(String)
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//-------------------------------------------------------- Lifecycle methods
|
||||
@Override
|
||||
protected void initInternal() throws LifecycleException {
|
||||
|
||||
try (JarFile warFile = new JarFile(getBase())) {
|
||||
JarEntry jarFileInWar = warFile.getJarEntry(archivePath);
|
||||
InputStream jarFileIs = warFile.getInputStream(jarFileInWar);
|
||||
|
||||
try (JarInputStream jarIs = new JarInputStream(jarFileIs)) {
|
||||
setManifest(jarIs.getManifest());
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
throw new IllegalArgumentException(ioe);
|
||||
}
|
||||
|
||||
try {
|
||||
setBaseUrl(UriUtil.buildJarSafeUrl(new File(getBase())));
|
||||
} catch (MalformedURLException e) {
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static final class VersionedJarEntry {
|
||||
private final int version;
|
||||
private final JarEntry jarEntry;
|
||||
|
||||
public VersionedJarEntry(int version, JarEntry jarEntry) {
|
||||
this.version = version;
|
||||
this.jarEntry = jarEntry;
|
||||
}
|
||||
|
||||
|
||||
public int getVersion() {
|
||||
return version;
|
||||
}
|
||||
|
||||
|
||||
public JarEntry getJarEntry() {
|
||||
return jarEntry;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
# 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.
|
||||
|
||||
abstractArchiveResourceSet.setReadOnlyFalse=Archive based WebResourceSets such as those based on JARs are hard-coded to be read-only and may not be configured to be read-write
|
||||
|
||||
abstractResource.getContentFail=Unable to return [{0}] as a byte array
|
||||
abstractResource.getContentTooLarge=Unable to return [{0}] as a byte array since the resource is [{1}] bytes in size which is larger than the maximum size of a byte array
|
||||
|
||||
abstractResourceSet.checkPath=The requested path [{0}] is not valid. It must begin with "/".
|
||||
|
||||
cache.addFail=Unable to add the resource at [{0}] to the cache for web application [{1}] because there was insufficient free space available after evicting expired cache entries - consider increasing the maximum size of the cache
|
||||
cache.backgroundEvictFail=The background cache eviction process was unable to free [{0}] percent of the cache for Context [{1}] - consider increasing the maximum size of the cache. After eviction approximately [{2}] KB of data remained in the cache.
|
||||
cache.objectMaxSizeTooBig=The value of [{0}]kB for objectMaxSize is larger than the limit of maxSize/20 so has been reduced to [{1}]kB
|
||||
cache.objectMaxSizeTooBigBytes=The value specified for the maximum object size to cache [{0}]kB is greater than Integer.MAX_VALUE bytes which is the maximum size that can be cached. The limit will be set to Integer.MAX_VALUE bytes.
|
||||
|
||||
cachedResource.invalidURL=Unable to create an instance of CachedResourceURLStreamHandler because the URL [{0}] is malformed
|
||||
|
||||
classpathUrlStreamHandler.notFound=Unable to load the resource [{0}] using the thread context class loader or the current class''s class loader
|
||||
|
||||
dirResourceSet.manifestFail=Failed to read manifest from [{0}]
|
||||
dirResourceSet.notDirectory=The directory specified by base and internal path [{0}]{1}[{2}] does not exist.
|
||||
dirResourceSet.writeNpe=The input stream may not be null
|
||||
|
||||
extractingRoot.jarFailed=Failed to extract the JAR file [{0}]
|
||||
extractingRoot.targetFailed=Failed to create the directory [{0}] for extracted JAR files
|
||||
|
||||
fileResource.getCanonicalPathFail=Unable to determine the canonical path for the resource [{0}]
|
||||
fileResource.getCreationFail=Unable to determine the creation time for the resource [{0}]
|
||||
fileResource.getUrlFail=Unable to determine a URL for the resource [{0}]
|
||||
|
||||
fileResourceSet.notFile=The file specified by base and internal path [{0}]{1}[{2}] does not exist.
|
||||
|
||||
jarResource.getInputStreamFail=Unable to obtain an InputStream for the resource [{0}] located in the JAR [{1}]
|
||||
|
||||
jarResourceRoot.invalidWebAppPath=This resource always refers to a directory so the supplied webAppPath must end with / but the provided webAppPath was [{0}]
|
||||
|
||||
jarWarResourceSet.codingError=Coding error
|
||||
|
||||
standardRoot.checkStateNotStarted=The resources may not be accessed if they are not currently started
|
||||
standardRoot.createInvalidFile=Unable to create WebResourceSet from [{0}]
|
||||
standardRoot.createUnknownType=Unable to create WebResourceSet of unknown type [{0}]
|
||||
standardRoot.invalidPath=The resource path [{0}] is not valid
|
||||
standardRoot.invalidPathNormal=The resource path [{0}] has been normalized to [{1}] which is not valid
|
||||
standardRoot.lockedFile=The web application [{0}] failed to close the file [{1}] opened via the following stack trace
|
||||
standardRoot.noContext=A Context has not been configured for this WebResourceRoot
|
||||
standardRoot.startInvalidMain=The main resource set specified [{0}] is not valid
|
||||
standardRoot.unsupportedProtocol=The URL protocol [{0}] is not supported by this web resources implementation
|
||||
@@ -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.
|
||||
|
||||
extractingRoot.targetFailed=Konnte Verzeichnis [{0}] zum entpacken einer JAR-Datei nicht anlegen
|
||||
@@ -0,0 +1,22 @@
|
||||
# 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.
|
||||
|
||||
cache.addFail=Imposible adicionar recursos a [{0}] de la cache para la applicación web [{1}] porque no hay suficiente espacio libre luego de eliminar los datos expirados de la caché - considere incrementar el tamaño máximo de la chaché
|
||||
|
||||
dirResourceSet.notDirectory=El directorio especificado por la base y el camino interno [{0}]{1}[{2}] no existe.\n
|
||||
|
||||
extractingRoot.targetFailed=Fallo al crear directorio [{0}] para los archivos JAR extraidos
|
||||
|
||||
standardRoot.createUnknownType=Imposible crear WebResourceSet de tipo desconocido [{0}]\n
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
|
||||
abstractArchiveResourceSet.setReadOnlyFalse=Les archives basées sur WebResourceSets telles que celles des JARs sont fixées comme étant en lecture seule et ne peuvent être configurées en lecture écriture
|
||||
|
||||
abstractResource.getContentFail=Impossible de retourner [{0}] en tant que tableau d''octets
|
||||
abstractResource.getContentTooLarge=Impossible de retourner [{0}] comme tableau d''octets car la ressource a une taille de [{1}] octets qui est supérieure à la taille maximale d''un tableau d''octets
|
||||
|
||||
abstractResourceSet.checkPath=Le chemin demandé [{0}] n''est pas valide, il doit commencer par ''/''
|
||||
|
||||
cache.addFail=Incapable d''ajouter la ressource située [{0}] au cache de l''application web [{1}] parce qu''il n''y avait pas assez d''espace libre disponible après l''éviction des entrées de cache expirées - envisagez d''augmenter la taille maximale du cache
|
||||
cache.backgroundEvictFail=Le processus d''arrière plan d''éviction du cache n''a pas pu nettoyer [{0}] pourcents du cache pour le contexte [{1}], il faudrait augmenter la taille maximale du cache; après l''éviction, approximativement [{2}] KO de données restaient dans le cache
|
||||
cache.objectMaxSizeTooBig=La valeur [{0}]kB pour l''objectMaxSize est plus grade que la limite de maxSize/20 son elle a été réduite à [{1}]kB\n
|
||||
cache.objectMaxSizeTooBigBytes=La valeur de taille d''objet maximale pouvant être mis en cache de [{0}]kB est supérieure à Integer.MAX_VALUE qui est le maximum, la limite a donc été fixée à Integer.MAX_VALUE octets
|
||||
|
||||
classpathUrlStreamHandler.notFound=Impossible de charger la ressource [{0}] en utilisant le chargeur de classe de contexte du thread ou celui de la classe actuelle
|
||||
|
||||
dirResourceSet.manifestFail=Impossible de lire le manifeste depuis [{0}]
|
||||
dirResourceSet.notDirectory=Le répertoire qui a été spécifié pour la base et le chemin interne [{0}]{1}[{2}] n''existe pas
|
||||
dirResourceSet.writeNpe=Le flux d'entrée ne peut pas être null
|
||||
|
||||
extractingRoot.jarFailed=Echec de l’extraction du fichier JAR [{0}]
|
||||
extractingRoot.targetFailed=Echec de la création du répertoire [{0}] pour l''extraction des fichiers contenus dans le JAR
|
||||
|
||||
fileResource.getCanonicalPathFail=Impossible de déterminer le chemin canonique pour la ressource [{0}]
|
||||
fileResource.getCreationFail=Impossible de déterminer la date de création de la ressource [{0}]
|
||||
fileResource.getUrlFail=Impossible de déterminer l''URL pour la ressource [{0}]
|
||||
|
||||
fileResourceSet.notFile=Le fichier spécifié par ses chemins de base et internes [{0}]{1}[{2}] n''existe pas
|
||||
|
||||
jarResource.getInputStreamFail=Impossible d''obtenir une InputStream pour la ressource [{0}] située dans le JAR [{1}]
|
||||
|
||||
jarResourceRoot.invalidWebAppPath=Cette ressource se réfère toujours à un répertoire donc le webAppPath fourni doit se terminer avec ''/'' mais il était [{0}]
|
||||
|
||||
jarWarResourceSet.codingError=Erreur de programmation
|
||||
|
||||
standardRoot.checkStateNotStarted=Les resources ne peuvent pas être accédées tant qu'elles ne sont pas démarrées
|
||||
standardRoot.createInvalidFile=Impossible de créer WebResourceSet à partir de [{0}]
|
||||
standardRoot.createUnknownType=Impossible de créer un WebResourceSet pour le type inconnu [{0}]
|
||||
standardRoot.invalidPath=Le chemin de ressources [{0}] est invalide
|
||||
standardRoot.invalidPathNormal=Le chemin de ressource [{0}] a été normalisé en [{1}] ce qui est invalide
|
||||
standardRoot.lockedFile=L''application web [{0}] n''a pas fermé le fichier [{1}] ouvert à partir de la trace
|
||||
standardRoot.noContext=Un contexte n'a pas été configuré pour ce WebResourceRoot
|
||||
standardRoot.startInvalidMain=L''ensemble de ressources principal [{0}] est invalide
|
||||
standardRoot.unsupportedProtocol=Le protocole [{0}] de l''URL n''est pas supporté par cette implémentation des ressources web
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
|
||||
abstractArchiveResourceSet.setReadOnlyFalse=JARに基づくWebResourceSetなどのアーカイブベースのWebResourceSetは、読み取り専用にハードコードされており、読み取り/書き込み可能に構成されていない可能性があります。
|
||||
|
||||
abstractResource.getContentFail=[{0}]をバイト配列として返すことができません。
|
||||
abstractResource.getContentTooLarge=リソースがバイト配列の最大サイズよりも大きいサイズの[{1}]バイトであるため、[{0}]をバイト配列として返すことができません。
|
||||
|
||||
abstractResourceSet.checkPath=リクエストパス[{0}]が無効です。 "/"で始まる必要があります。
|
||||
|
||||
cache.addFail=有効期限切れの項目を破棄しても利用可能な領域が不足するため、Web アプリケーション [{1}] のキャッシュにリソース [{0}] を追加できません。最大キャッシュサイズの増加を検討してください。
|
||||
cache.backgroundEvictFail=コンテキスト [{1}] のバックグラウンドキャッシュ削除処理は全体の [{0}] % を解放できませんでした。キャッシュサイズの最大値の増加を検討してください。現在は約 [{2}] kB のデータがキャッシュに残存しています。
|
||||
cache.objectMaxSizeTooBig=objectMaxSizeの[{0}] kBの値がmaxSize / 20の制限より大きいため、[{1}] kBに減少しました。
|
||||
cache.objectMaxSizeTooBigBytes=キャッシュ可能なオブジェクトサイズの最大値に指定された [{0}]kB は Integer.MAX_VALUE バイトを越えています。最大値に Integer.MAX_VALUE を設定します。
|
||||
|
||||
classpathUrlStreamHandler.notFound=スレッドコンテキストクラスローダー、あるいは、現在のクラスのクラスローダーでリソース [{0}] を読み込みできません。
|
||||
|
||||
dirResourceSet.manifestFail=[{0}]からマニフェストを読み込めませんでした。
|
||||
dirResourceSet.notDirectory=ベースパスと内部パスで指定した [{0}][{1}][{2}] にディレクトリがありません。
|
||||
dirResourceSet.writeNpe=入力ストリームには null を指定できません。
|
||||
|
||||
extractingRoot.jarFailed=JARファイル[{0}]の抽出に失敗しました
|
||||
extractingRoot.targetFailed=JAR ファイルを展開するためのディレクトリ [{0}] を作成できません。
|
||||
|
||||
fileResource.getCanonicalPathFail=リソース [{0}] の正規化パスを取得できません。
|
||||
fileResource.getCreationFail=リソース[{0}]の作成時間を特定できません。
|
||||
fileResource.getUrlFail=リソース [{0}] の URL を取得できません。
|
||||
|
||||
fileResourceSet.notFile=基本パスおよび内部パスで指定されたファイル [{0}]{1}[{2}] がありません。
|
||||
|
||||
jarResource.getInputStreamFail=JAR ファイル [{1}] のリソース [{0}] の入力ストリームを取得できません。
|
||||
|
||||
jarResourceRoot.invalidWebAppPath=このリソースは常にディレクトリを参照するため、指定されたwebAppPathは/で終了する必要がありますが、指定されたwebAppPathは[{0}]です。
|
||||
|
||||
jarWarResourceSet.codingError=コーディングエラー
|
||||
|
||||
standardRoot.checkStateNotStarted=リソースは、現在起動されていない場合はアクセスできない場合があります
|
||||
standardRoot.createInvalidFile=[{0}]からWebResourceSetを作成できません。
|
||||
standardRoot.createUnknownType=未知のクラス [{0}] の WebResourceSet を作成できません。
|
||||
standardRoot.invalidPath=不正なリソースパス [{0}]
|
||||
standardRoot.invalidPathNormal=リソースパス[{0}]は有効ではない[{1}]に正規化されています。
|
||||
standardRoot.lockedFile=Webアプリケーション[{0}]は、次のスタックトレースによって開かれたファイル[{1}]を閉じることに失敗しました。
|
||||
standardRoot.noContext=この WebResourceRoot にはContext が構成されていません。
|
||||
standardRoot.startInvalidMain=指定された主リソースセット[{0}]は無効です。
|
||||
standardRoot.unsupportedProtocol=URLプロトコル[{0}]はこのWebリソース実装ではサポートされていません。
|
||||
@@ -0,0 +1,57 @@
|
||||
# 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.
|
||||
|
||||
abstractArchiveResourceSet.setReadOnlyFalse=JAR 파일들에 기반한 것들과 같은 아카이브 기반 WebResourceSet들은, 읽기 전용으로 하드코드되어 있으며, 읽기 및 쓰기 용으로 설정될 수 없습니다.
|
||||
|
||||
abstractResource.getContentFail=[{0}]을(를) 바이트 배열로 반환할 수 없습니다.
|
||||
abstractResource.getContentTooLarge=리소스의 크기가 [{1}] 바이트로서, 이는 바이트 배열 최대 크기보다 크기 때문에, [{0}]을(를) 바이트 배열로서 반환할 수 없습니다.
|
||||
|
||||
abstractResourceSet.checkPath=요청된 경로 [{0}]은(는) 유효하지 않습니다. 반드시 "/"로 시작해야 합니다.
|
||||
|
||||
cache.addFail=[{0}]에 위치한 리소스를 웹 애플리케이션 [{1}]을(를) 위한 캐시에 추가할 수 없습니다. 왜냐하면 만료된 캐시 엔트리들을 없애버린 이후에도 여유 공간이 충분하지 않기 때문입니다. 캐시의 최대 크기를 증가시키는 것을 고려해 보십시오.
|
||||
cache.backgroundEvictFail=백그라운드 캐시 퇴거 (cache eviction) 프로세스가, 컨텍스트 [{1}]을(를) 위한 캐시의 [{0}] 퍼센트를 해제시킬 수 없었습니다. 캐시의 최대 크기를 증가시킬 것을 고려해 보십시오. 캐시 퇴거 작업 이후, 대략 [{2}] KB의 데이터가 캐시에 남아 있습니다.
|
||||
cache.objectMaxSizeTooBig=objectMaxSize를 위한 값 [{0}]kB이, maxSize/20인 최대한계값 보다 커서, [{1}]kB로 줄여졌습니다.
|
||||
cache.objectMaxSizeTooBigBytes=[{0}]kB를 캐시하기 위해, 최대 객체 크기로서 지정된 값이 Integer.MAX_VALUE 바이트보다 큰데, Integer.MAX_VALUE는 캐시될 수 있는 최대 크기입니다. 한계 값을 Integer.MAX_VALUE 바이트로 설정하겠습니다.
|
||||
|
||||
classpathUrlStreamHandler.notFound=쓰레드 컨텍스트 클래스로더 또는 현재 클래스의 클래스로더를 사용하여, 리소스 [{0}]을(를) 로드할 수 없습니다.
|
||||
|
||||
dirResourceSet.manifestFail=[{0}](으)로부터 manifest를 읽지 못했습니다.
|
||||
dirResourceSet.notDirectory=base와 internal path [{0}]{1}[{2}](으)로 지정된 디렉토리가 존재하지 않습니다.
|
||||
dirResourceSet.writeNpe=입력 스트림이 널일 수는 없습니다.
|
||||
|
||||
extractingRoot.jarFailed=JAR 파일 [{0}]을(를) 추출하지 못했습니다.
|
||||
extractingRoot.targetFailed=JAR 파일들의 압축을 풀기 위한 디렉토리 [{0}]을(를) 생성할 수 없습니다.
|
||||
|
||||
fileResource.getCanonicalPathFail=리소스 [{0}]에 대한 canonical 경로를 결정할 수 없습니다.
|
||||
fileResource.getCreationFail=리소스 [{0}]의 생성 시간을 결정할 수 없습니다.
|
||||
fileResource.getUrlFail=리소스 [{0}]을(를) 위한 URL을 결정할 수 없습니다.
|
||||
|
||||
fileResourceSet.notFile=base와 내부 경로 [{0}]{1}[{2}]에 의해 지정된 파일이 존재하지 않습니다.
|
||||
|
||||
jarResource.getInputStreamFail=JAR [{1}] 내의 리소스 [{0}]을(를) 위한 InputStream을 얻을 수 없습니다.
|
||||
|
||||
jarResourceRoot.invalidWebAppPath=이 리소스는 언제나 디렉토리를 가리켜서, 제공된 webAppPath가 반드시 ''/'' 로 끝나야 하지만, 제공된 webAppPath는 [{0}]이었습니다.
|
||||
|
||||
jarWarResourceSet.codingError=코딩 오류
|
||||
|
||||
standardRoot.checkStateNotStarted=현재 시작되어 있는 상태가 아니라면, 리소스들은 접근될 수 없습니다.
|
||||
standardRoot.createInvalidFile=[{0}](으)로부터 WebResourceSet을 생성할 수 없습니다.
|
||||
standardRoot.createUnknownType=알 수 없는 타입 [{0}]의 WebResourceSet을 생성할 수 없습니다.
|
||||
standardRoot.invalidPath=리소스 경로 [{0}]은(는) 유효하지 않습니다.
|
||||
standardRoot.invalidPathNormal=리소스 경로 [{0}]이(가) [{1}](으)로 정규화되어 있는데, 이는 유효하지 않습니다.
|
||||
standardRoot.lockedFile=웹 애플리케이션 [{0}]이(가) 파일 [{1}]을(를) 닫지 못했습니다. 해당 파일은 다음과 같은 스택 트레이스 내에서 열렸었습니다.
|
||||
standardRoot.noContext=컨텍스트가 이 WebResourceRoot를 위해 설정되지 않았습니다.
|
||||
standardRoot.startInvalidMain=지정된 주요 리소스셋 [{0}]은(는) 유효하지 않습니다.
|
||||
standardRoot.unsupportedProtocol=URL 프로토콜 [{0}]은(는), 이 웹 리소스 구현에 의해 지원되지 않습니다.
|
||||
@@ -0,0 +1,35 @@
|
||||
# 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.
|
||||
|
||||
abstractArchiveResourceSet.setReadOnlyFalse=基于存档的WebResourceSets 如基于jar的WebResourceSets 硬编码为只读,并且不能配置为读写
|
||||
|
||||
cache.addFail=无法将位于[{0}]的资源添加到Web应用程序[{1}]的缓存中,因为在清除过期缓存条目后可用空间仍不足 - 请考虑增加缓存的最大空间。
|
||||
|
||||
dirResourceSet.notDirectory=基本和内部路径[{0}] {1} [{2}]指定的目录不存在。
|
||||
|
||||
extractingRoot.jarFailed=解压JAR文件[{0}]失败
|
||||
extractingRoot.targetFailed=无法为提取的 JAR 文件创建目录 [{0}]
|
||||
|
||||
fileResource.getCanonicalPathFail=不能判断资源的标准路径[{0}]
|
||||
fileResource.getUrlFail=不能决定一个url 为资源[{0}]
|
||||
|
||||
jarResource.getInputStreamFail=无法获取JAR[{1}]中的资源文件[{0}]的一个InputStream
|
||||
|
||||
standardRoot.checkStateNotStarted=如果当前未启动资源,则可能无法访问这些资源
|
||||
standardRoot.createUnknownType=无法为未知类型[{0}]创建WebResourceSet。
|
||||
standardRoot.invalidPathNormal=资源路径[{0}]已规范化为无效的[{1}]
|
||||
standardRoot.lockedFile=Web应用程序[{0}]无法关闭通过以下堆栈跟踪打开的文件[{1}]
|
||||
standardRoot.noContext=尚未为WebResourceRoot配置上下文
|
||||
standardRoot.startInvalidMain=指定的主资源集 [{0}] 无效
|
||||
864
java/org/apache/catalina/webresources/StandardRoot.java
Normal file
864
java/org/apache/catalina/webresources/StandardRoot.java
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarInputStream;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
/**
|
||||
* The purpose of this sub-class is to obtain references to the JarEntry objects
|
||||
* for META-INF/ and META-INF/MANIFEST.MF that are otherwise swallowed by the
|
||||
* JarInputStream implementation.
|
||||
*/
|
||||
public class TomcatJarInputStream extends JarInputStream {
|
||||
|
||||
private JarEntry metaInfEntry;
|
||||
private JarEntry manifestEntry;
|
||||
|
||||
|
||||
TomcatJarInputStream(InputStream in) throws IOException {
|
||||
super(in);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected ZipEntry createZipEntry(String name) {
|
||||
ZipEntry ze = super.createZipEntry(name);
|
||||
if (metaInfEntry == null && "META-INF/".equals(name)) {
|
||||
metaInfEntry = (JarEntry) ze;
|
||||
} else if (manifestEntry == null && "META-INF/MANIFESR.MF".equals(name)) {
|
||||
manifestEntry = (JarEntry) ze;
|
||||
}
|
||||
return ze;
|
||||
}
|
||||
|
||||
|
||||
JarEntry getMetaInfEntry() {
|
||||
return metaInfEntry;
|
||||
}
|
||||
|
||||
|
||||
JarEntry getManifestEntry() {
|
||||
return manifestEntry;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.net.URL;
|
||||
import java.net.URLStreamHandler;
|
||||
import java.net.URLStreamHandlerFactory;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.CopyOnWriteArrayList;
|
||||
|
||||
import org.apache.catalina.webresources.war.Handler;
|
||||
|
||||
public class TomcatURLStreamHandlerFactory implements URLStreamHandlerFactory {
|
||||
|
||||
private static final String WAR_PROTOCOL = "war";
|
||||
private static final String CLASSPATH_PROTOCOL = "classpath";
|
||||
|
||||
// Singleton instance
|
||||
private static volatile TomcatURLStreamHandlerFactory instance = null;
|
||||
|
||||
/**
|
||||
* Obtain a reference to the singleton instance. It is recommended that
|
||||
* callers check the value of {@link #isRegistered()} before using the
|
||||
* returned instance.
|
||||
*
|
||||
* @return A reference to the singleton instance
|
||||
*/
|
||||
public static TomcatURLStreamHandlerFactory getInstance() {
|
||||
getInstanceInternal(true);
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
private static TomcatURLStreamHandlerFactory getInstanceInternal(boolean register) {
|
||||
// Double checked locking. OK because instance is volatile.
|
||||
if (instance == null) {
|
||||
synchronized (TomcatURLStreamHandlerFactory.class) {
|
||||
if (instance == null) {
|
||||
instance = new TomcatURLStreamHandlerFactory(register);
|
||||
}
|
||||
}
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
|
||||
private final boolean registered;
|
||||
|
||||
// List of factories for application defined stream handler factories.
|
||||
private final List<URLStreamHandlerFactory> userFactories =
|
||||
new CopyOnWriteArrayList<>();
|
||||
|
||||
/**
|
||||
* Register this factory with the JVM. May be called more than once. The
|
||||
* implementation ensures that registration only occurs once.
|
||||
*
|
||||
* @return <code>true</code> if the factory is already registered with the
|
||||
* JVM or was successfully registered as a result of this call.
|
||||
* <code>false</code> if the factory was disabled prior to this
|
||||
* call.
|
||||
*/
|
||||
public static boolean register() {
|
||||
return getInstanceInternal(true).isRegistered();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prevent this this factory from registering with the JVM. May be called
|
||||
* more than once.
|
||||
*
|
||||
* @return <code>true</code> if the factory is already disabled or was
|
||||
* successfully disabled as a result of this call.
|
||||
* <code>false</code> if the factory was already registered prior
|
||||
* to this call.
|
||||
|
||||
*/
|
||||
public static boolean disable() {
|
||||
return !getInstanceInternal(false).isRegistered();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Release references to any user provided factories that have been loaded
|
||||
* using the provided class loader. Called during web application stop to
|
||||
* prevent memory leaks.
|
||||
*
|
||||
* @param classLoader The class loader to release
|
||||
*/
|
||||
public static void release(ClassLoader classLoader) {
|
||||
if (instance == null) {
|
||||
return;
|
||||
}
|
||||
List<URLStreamHandlerFactory> factories = instance.userFactories;
|
||||
for (URLStreamHandlerFactory factory : factories) {
|
||||
ClassLoader factoryLoader = factory.getClass().getClassLoader();
|
||||
while (factoryLoader != null) {
|
||||
if (classLoader.equals(factoryLoader)) {
|
||||
// Implementation note: userFactories is a
|
||||
// CopyOnWriteArrayList, so items are removed with
|
||||
// List.remove() instead of usual Iterator.remove()
|
||||
factories.remove(factory);
|
||||
break;
|
||||
}
|
||||
factoryLoader = factoryLoader.getParent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private TomcatURLStreamHandlerFactory(boolean register) {
|
||||
// Hide default constructor
|
||||
// Singleton pattern to ensure there is only one instance of this
|
||||
// factory
|
||||
this.registered = register;
|
||||
if (register) {
|
||||
URL.setURLStreamHandlerFactory(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public boolean isRegistered() {
|
||||
return registered;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Since the JVM only allows a single call to
|
||||
* {@link URL#setURLStreamHandlerFactory(URLStreamHandlerFactory)} and
|
||||
* Tomcat needs to register a handler, provide a mechanism to allow
|
||||
* applications to register their own handlers.
|
||||
*
|
||||
* @param factory The user provided factory to add to the factories Tomcat
|
||||
* has already registered
|
||||
*/
|
||||
public void addUserFactory(URLStreamHandlerFactory factory) {
|
||||
userFactories.add(factory);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public URLStreamHandler createURLStreamHandler(String protocol) {
|
||||
|
||||
// Tomcat's handler always takes priority so applications can't override
|
||||
// it.
|
||||
if (WAR_PROTOCOL.equals(protocol)) {
|
||||
return new Handler();
|
||||
} else if (CLASSPATH_PROTOCOL.equals(protocol)) {
|
||||
return new ClasspathURLStreamHandler();
|
||||
}
|
||||
|
||||
// Application handlers
|
||||
for (URLStreamHandlerFactory factory : userFactories) {
|
||||
URLStreamHandler handler =
|
||||
factory.createURLStreamHandler(protocol);
|
||||
if (handler != null) {
|
||||
return handler;
|
||||
}
|
||||
}
|
||||
|
||||
// Unknown protocol
|
||||
return null;
|
||||
}
|
||||
}
|
||||
112
java/org/apache/catalina/webresources/TrackedInputStream.java
Normal file
112
java/org/apache/catalina/webresources/TrackedInputStream.java
Normal file
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
|
||||
import org.apache.catalina.TrackedWebResource;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
|
||||
class TrackedInputStream extends InputStream implements TrackedWebResource {
|
||||
|
||||
private final WebResourceRoot root;
|
||||
private final String name;
|
||||
private final InputStream is;
|
||||
private final Exception creation;
|
||||
|
||||
TrackedInputStream(WebResourceRoot root, String name, InputStream is) {
|
||||
this.root = root;
|
||||
this.name = name;
|
||||
this.is = is;
|
||||
this.creation = new Exception();
|
||||
|
||||
root.registerTrackedResource(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read() throws IOException {
|
||||
return is.read();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b) throws IOException {
|
||||
return is.read(b);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int read(byte[] b, int off, int len) throws IOException {
|
||||
return is.read(b, off, len);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long skip(long n) throws IOException {
|
||||
return is.skip(n);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int available() throws IOException {
|
||||
return is.available();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
root.deregisterTrackedResource(this);
|
||||
is.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void mark(int readlimit) {
|
||||
is.mark(readlimit);
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized void reset() throws IOException {
|
||||
is.reset();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean markSupported() {
|
||||
return is.markSupported();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Exception getCreatedBy() {
|
||||
return creation;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringWriter sw = new StringWriter();
|
||||
PrintWriter pw = new PrintWriter(sw);
|
||||
sw.append('[');
|
||||
sw.append(name);
|
||||
sw.append(']');
|
||||
sw.append(System.lineSeparator());
|
||||
creation.printStackTrace(pw);
|
||||
pw.flush();
|
||||
|
||||
return sw.toString();
|
||||
}
|
||||
}
|
||||
45
java/org/apache/catalina/webresources/VirtualResource.java
Normal file
45
java/org/apache/catalina/webresources/VirtualResource.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
|
||||
public class VirtualResource extends EmptyResource {
|
||||
|
||||
private final String name;
|
||||
|
||||
public VirtualResource(WebResourceRoot root, String webAppPath,
|
||||
String name) {
|
||||
super(root, webAppPath);
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isVirtual() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isDirectory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
}
|
||||
45
java/org/apache/catalina/webresources/WarResource.java
Normal file
45
java/org/apache/catalina/webresources/WarResource.java
Normal file
@@ -0,0 +1,45 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.util.jar.JarEntry;
|
||||
|
||||
import org.apache.juli.logging.Log;
|
||||
import org.apache.juli.logging.LogFactory;
|
||||
import org.apache.tomcat.util.buf.UriUtil;
|
||||
|
||||
/**
|
||||
* Represents a single resource (file or directory) that is located within a
|
||||
* WAR.
|
||||
*/
|
||||
public class WarResource extends AbstractSingleArchiveResource {
|
||||
|
||||
private static final Log log = LogFactory.getLog(WarResource.class);
|
||||
|
||||
|
||||
public WarResource(AbstractArchiveResourceSet archiveResourceSet, String webAppPath,
|
||||
String baseUrl, JarEntry jarEntry) {
|
||||
super(archiveResourceSet, webAppPath, "war:" + baseUrl + UriUtil.getWarSeparator(),
|
||||
jarEntry, baseUrl);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected Log getLog() {
|
||||
return log;
|
||||
}
|
||||
}
|
||||
64
java/org/apache/catalina/webresources/WarResourceSet.java
Normal file
64
java/org/apache/catalina/webresources/WarResourceSet.java
Normal file
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.catalina.webresources;
|
||||
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.Manifest;
|
||||
|
||||
import org.apache.catalina.WebResource;
|
||||
import org.apache.catalina.WebResourceRoot;
|
||||
|
||||
/**
|
||||
* Represents a {@link org.apache.catalina.WebResourceSet} based on a WAR file.
|
||||
*/
|
||||
public class WarResourceSet extends AbstractSingleArchiveResourceSet {
|
||||
|
||||
/**
|
||||
* A no argument constructor is required for this to work with the digester.
|
||||
*/
|
||||
public WarResourceSet() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new {@link org.apache.catalina.WebResourceSet} based on a WAR
|
||||
* file.
|
||||
*
|
||||
* @param root The {@link WebResourceRoot} this new
|
||||
* {@link org.apache.catalina.WebResourceSet} will
|
||||
* be added to.
|
||||
* @param webAppMount The path within the web application at which this
|
||||
* {@link org.apache.catalina.WebResourceSet} will
|
||||
* be mounted.
|
||||
* @param base The absolute path to the WAR file on the file system
|
||||
* from which the resources will be served.
|
||||
*
|
||||
* @throws IllegalArgumentException if the webAppMount is not valid (valid
|
||||
* paths must start with '/')
|
||||
*/
|
||||
public WarResourceSet(WebResourceRoot root, String webAppMount, String base)
|
||||
throws IllegalArgumentException {
|
||||
super(root, webAppMount, base, "/");
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected WebResource createArchiveResource(JarEntry jarEntry,
|
||||
String webAppPath, Manifest manifest) {
|
||||
return new WarResource(this, webAppPath, getBaseUrlString(), jarEntry);
|
||||
}
|
||||
}
|
||||
98
java/org/apache/catalina/webresources/mbeans-descriptors.xml
Normal file
98
java/org/apache/catalina/webresources/mbeans-descriptors.xml
Normal file
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<mbeans-descriptors>
|
||||
|
||||
<mbean name="StandardRoot"
|
||||
description="Provides the resources implementation for a web application"
|
||||
domain="Catalina"
|
||||
group="WebResourceRoot"
|
||||
type="org.apache.catalina.webresources.StandardRoot">
|
||||
|
||||
<attribute name="allowLinking"
|
||||
description="Does this resources implementation allow the use of symbolic links?"
|
||||
type="boolean"
|
||||
writeable="true"/>
|
||||
|
||||
<attribute name="cachingAllowed"
|
||||
description="Is in memory caching of resource content and metadata enabled?"
|
||||
type="boolean"
|
||||
is="true"
|
||||
writeable="true"/>
|
||||
|
||||
<attribute name="stateName"
|
||||
description="The current Lifecycle state of this object"
|
||||
type="java.lang.String"
|
||||
writeable="false"/>
|
||||
|
||||
<attribute name="trackLockedFiles"
|
||||
description="Does this resources implementation track requests that lock files?"
|
||||
type="boolean"
|
||||
writeable="true"/>
|
||||
|
||||
<attribute name="trackedResources"
|
||||
description="List of resources currently being tracked for possible resource leaks"
|
||||
type="java.util.List"
|
||||
writeable="false"/>
|
||||
|
||||
</mbean>
|
||||
|
||||
<mbean name="Cache"
|
||||
description="Provides caching of resource metadata and content"
|
||||
domain="Catalina"
|
||||
group="WebResourceRoot"
|
||||
type="org.apache.catalina.webresources.Cache">
|
||||
|
||||
<attribute name="hitCount"
|
||||
description="The number of requests for resources that were served from the cache"
|
||||
type="long"
|
||||
writeable="false"/>
|
||||
|
||||
<attribute name="lookupCount"
|
||||
description="The number of requests for resources"
|
||||
type="long"
|
||||
writeable="false"/>
|
||||
|
||||
<attribute name="maxSize"
|
||||
description="The maximum permitted size of the cache in kB"
|
||||
type="long"
|
||||
writeable="true"/>
|
||||
|
||||
<attribute name="objectMaxSize"
|
||||
description="The maximum permitted size for a single object in the cache in kB"
|
||||
type="int"
|
||||
writeable="true"/>
|
||||
|
||||
<attribute name="size"
|
||||
description="The current estimate of the cache size in kB"
|
||||
type="long"
|
||||
writeable="false"/>
|
||||
|
||||
<attribute name="ttl"
|
||||
description="The time-to-live for cache entries in milliseconds"
|
||||
type="long"
|
||||
writeable="true"/>
|
||||
|
||||
<operation name="clear"
|
||||
description="Clears all cached content from the cache."
|
||||
impact="ACTION"
|
||||
returnType="void">
|
||||
</operation>
|
||||
|
||||
</mbean>
|
||||
|
||||
</mbeans-descriptors>
|
||||
46
java/org/apache/catalina/webresources/war/Handler.java
Normal file
46
java/org/apache/catalina/webresources/war/Handler.java
Normal file
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* 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.catalina.webresources.war;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.net.URLStreamHandler;
|
||||
|
||||
public class Handler extends URLStreamHandler {
|
||||
|
||||
@Override
|
||||
protected URLConnection openConnection(URL u) throws IOException {
|
||||
return new WarURLConnection(u);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setURL(URL u, String protocol, String host, int port, String authority, String userInfo, String path,
|
||||
String query, String ref) {
|
||||
if (path.startsWith("file:") && !path.startsWith("file:/")) {
|
||||
// Work around a problem with the URLs in the security policy file.
|
||||
// On Windows, the use of ${catalina.[home|base]} in the policy file
|
||||
// results in codebase URLs of the form file:C:/... when they should
|
||||
// be file:/C:/...
|
||||
// For file: and jar: URLs, the JRE compensates for this. It does not
|
||||
// compensate for this for war:file:... URLs. Therefore, we do that
|
||||
// here
|
||||
path = "file:/" + path.substring(5);
|
||||
}
|
||||
super.setURL(u, protocol, host, port, authority, userInfo, path, query, ref);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
* 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.catalina.webresources.war;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.security.Permission;
|
||||
|
||||
import org.apache.tomcat.util.buf.UriUtil;
|
||||
|
||||
|
||||
public class WarURLConnection extends URLConnection {
|
||||
|
||||
private final URLConnection wrappedJarUrlConnection;
|
||||
private boolean connected;
|
||||
|
||||
protected WarURLConnection(URL url) throws IOException {
|
||||
super(url);
|
||||
URL innerJarUrl = UriUtil.warToJar(url);
|
||||
wrappedJarUrlConnection = innerJarUrl.openConnection();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void connect() throws IOException {
|
||||
if (!connected) {
|
||||
wrappedJarUrlConnection.connect();
|
||||
connected = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public InputStream getInputStream() throws IOException {
|
||||
connect();
|
||||
return wrappedJarUrlConnection.getInputStream();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Permission getPermission() throws IOException {
|
||||
return wrappedJarUrlConnection.getPermission();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getLastModified() {
|
||||
return wrappedJarUrlConnection.getLastModified();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public int getContentLength() {
|
||||
return wrappedJarUrlConnection.getContentLength();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public long getContentLengthLong() {
|
||||
return wrappedJarUrlConnection.getContentLengthLong();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user