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

View File

@@ -0,0 +1,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();
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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);
}

View 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();
}

View 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();
}
}

View File

@@ -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;
}
}
}

View File

@@ -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);
}
}
}

View 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;
}
}
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -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();
}
}

View 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);
}
}
}
}
}

View 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;
}
}

View 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
}
}

View 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);
}
}
}

View 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;
}
}

View 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()));
}
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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;
}
}

View 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;
}
}
}

View File

@@ -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

View File

@@ -0,0 +1,16 @@
# Licensed to the Apache Software Foundation (ASF) under one or more
# contributor license agreements. See the NOTICE file distributed with
# this work for additional information regarding copyright ownership.
# The ASF licenses this file to You under the Apache License, Version 2.0
# (the "License"); you may not use this file except in compliance with
# the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
extractingRoot.targetFailed=Konnte Verzeichnis [{0}] zum entpacken einer JAR-Datei nicht anlegen

View File

@@ -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

View File

@@ -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 lextraction 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

View File

@@ -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リソース実装ではサポートされていません。

View File

@@ -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}]은(는), 이 웹 리소스 구현에 의해 지원되지 않습니다.

View File

@@ -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}] 无效

File diff suppressed because it is too large Load Diff

View File

@@ -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;
}
}

View File

@@ -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;
}
}

View 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();
}
}

View 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;
}
}

View 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;
}
}

View 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);
}
}

View 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>

View 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);
}
}

View File

@@ -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();
}
}