865 lines
27 KiB
Java
865 lines
27 KiB
Java
/*
|
|
* 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.net.URISyntaxException;
|
|
import java.net.URL;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.HashSet;
|
|
import java.util.LinkedHashSet;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Set;
|
|
import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
import javax.management.ObjectName;
|
|
|
|
import org.apache.catalina.Context;
|
|
import org.apache.catalina.Host;
|
|
import org.apache.catalina.LifecycleException;
|
|
import org.apache.catalina.LifecycleState;
|
|
import org.apache.catalina.TrackedWebResource;
|
|
import org.apache.catalina.WebResource;
|
|
import org.apache.catalina.WebResourceRoot;
|
|
import org.apache.catalina.WebResourceSet;
|
|
import org.apache.catalina.util.LifecycleMBeanBase;
|
|
import org.apache.juli.logging.Log;
|
|
import org.apache.juli.logging.LogFactory;
|
|
import org.apache.tomcat.util.buf.UriUtil;
|
|
import org.apache.tomcat.util.http.RequestUtil;
|
|
import org.apache.tomcat.util.res.StringManager;
|
|
|
|
/**
|
|
* <p>
|
|
* Provides the resources implementation for a web application. The
|
|
* {@link org.apache.catalina.Lifecycle} of this class should be aligned with
|
|
* that of the associated {@link Context}.
|
|
* </p><p>
|
|
* This implementation assumes that the base attribute supplied to {@link
|
|
* StandardRoot#createWebResourceSet(
|
|
* org.apache.catalina.WebResourceRoot.ResourceSetType, String, String, String,
|
|
* String)} represents the absolute path to a file.
|
|
* </p>
|
|
*/
|
|
public class StandardRoot extends LifecycleMBeanBase implements WebResourceRoot {
|
|
|
|
private static final Log log = LogFactory.getLog(StandardRoot.class);
|
|
protected static final StringManager sm = StringManager.getManager(StandardRoot.class);
|
|
|
|
private Context context;
|
|
private boolean allowLinking = false;
|
|
private final List<WebResourceSet> preResources = new ArrayList<>();
|
|
private WebResourceSet main;
|
|
private final List<WebResourceSet> classResources = new ArrayList<>();
|
|
private final List<WebResourceSet> jarResources = new ArrayList<>();
|
|
private final List<WebResourceSet> postResources = new ArrayList<>();
|
|
|
|
private final Cache cache = new Cache(this);
|
|
private boolean cachingAllowed = true;
|
|
private ObjectName cacheJmxName = null;
|
|
|
|
private boolean trackLockedFiles = false;
|
|
private final Set<TrackedWebResource> trackedResources =
|
|
Collections.newSetFromMap(new ConcurrentHashMap<TrackedWebResource,Boolean>());
|
|
|
|
// Constructs to make iteration over all WebResourceSets simpler
|
|
private final List<WebResourceSet> mainResources = new ArrayList<>();
|
|
private final List<List<WebResourceSet>> allResources =
|
|
new ArrayList<>();
|
|
{
|
|
allResources.add(preResources);
|
|
allResources.add(mainResources);
|
|
allResources.add(classResources);
|
|
allResources.add(jarResources);
|
|
allResources.add(postResources);
|
|
}
|
|
|
|
|
|
/**
|
|
* Creates a new standard implementation of {@link WebResourceRoot}. A no
|
|
* argument constructor is required for this to work with the digester.
|
|
* {@link #setContext(Context)} must be called before this component is
|
|
* initialized.
|
|
*/
|
|
public StandardRoot() {
|
|
// NO-OP
|
|
}
|
|
|
|
public StandardRoot(Context context) {
|
|
this.context = context;
|
|
}
|
|
|
|
@Override
|
|
public String[] list(String path) {
|
|
return list(path, true);
|
|
}
|
|
|
|
private String[] list(String path, boolean validate) {
|
|
if (validate) {
|
|
path = validate(path);
|
|
}
|
|
|
|
// Set because we don't want duplicates
|
|
// LinkedHashSet to retain the order. It is the order of the
|
|
// WebResourceSet that matters but it is simpler to retain the order
|
|
// over all of the JARs.
|
|
HashSet<String> result = new LinkedHashSet<>();
|
|
for (List<WebResourceSet> list : allResources) {
|
|
for (WebResourceSet webResourceSet : list) {
|
|
if (!webResourceSet.getClassLoaderOnly()) {
|
|
String[] entries = webResourceSet.list(path);
|
|
for (String entry : entries) {
|
|
result.add(entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result.toArray(new String[result.size()]);
|
|
}
|
|
|
|
|
|
@Override
|
|
public Set<String> listWebAppPaths(String path) {
|
|
path = validate(path);
|
|
|
|
// Set because we don't want duplicates
|
|
HashSet<String> result = new HashSet<>();
|
|
for (List<WebResourceSet> list : allResources) {
|
|
for (WebResourceSet webResourceSet : list) {
|
|
if (!webResourceSet.getClassLoaderOnly()) {
|
|
result.addAll(webResourceSet.listWebAppPaths(path));
|
|
}
|
|
}
|
|
}
|
|
if (result.size() == 0) {
|
|
return null;
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public boolean mkdir(String path) {
|
|
path = validate(path);
|
|
|
|
if (preResourceExists(path)) {
|
|
return false;
|
|
}
|
|
|
|
boolean mkdirResult = main.mkdir(path);
|
|
|
|
if (mkdirResult && isCachingAllowed()) {
|
|
// Remove the entry from the cache so the new directory is visible
|
|
cache.removeCacheEntry(path);
|
|
}
|
|
return mkdirResult;
|
|
}
|
|
|
|
@Override
|
|
public boolean write(String path, InputStream is, boolean overwrite) {
|
|
path = validate(path);
|
|
|
|
if (!overwrite && preResourceExists(path)) {
|
|
return false;
|
|
}
|
|
|
|
boolean writeResult = main.write(path, is, overwrite);
|
|
|
|
if (writeResult && isCachingAllowed()) {
|
|
// Remove the entry from the cache so the new resource is visible
|
|
cache.removeCacheEntry(path);
|
|
}
|
|
|
|
return writeResult;
|
|
}
|
|
|
|
private boolean preResourceExists(String path) {
|
|
for (WebResourceSet webResourceSet : preResources) {
|
|
WebResource webResource = webResourceSet.getResource(path);
|
|
if (webResource.exists()) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override
|
|
public WebResource getResource(String path) {
|
|
return getResource(path, true, false);
|
|
}
|
|
|
|
protected WebResource getResource(String path, boolean validate,
|
|
boolean useClassLoaderResources) {
|
|
if (validate) {
|
|
path = validate(path);
|
|
}
|
|
|
|
if (isCachingAllowed()) {
|
|
return cache.getResource(path, useClassLoaderResources);
|
|
} else {
|
|
return getResourceInternal(path, useClassLoaderResources);
|
|
}
|
|
}
|
|
|
|
|
|
@Override
|
|
public WebResource getClassLoaderResource(String path) {
|
|
return getResource("/WEB-INF/classes" + path, true, true);
|
|
}
|
|
|
|
|
|
@Override
|
|
public WebResource[] getClassLoaderResources(String path) {
|
|
return getResources("/WEB-INF/classes" + path, true);
|
|
}
|
|
|
|
|
|
/**
|
|
* Ensures that this object is in a valid state to serve resources, checks
|
|
* that the path is a String that starts with '/' and checks that the path
|
|
* can be normalized without stepping outside of the root.
|
|
*
|
|
* @param path
|
|
* @return the normalized path
|
|
*/
|
|
private String validate(String path) {
|
|
if (!getState().isAvailable()) {
|
|
throw new IllegalStateException(
|
|
sm.getString("standardRoot.checkStateNotStarted"));
|
|
}
|
|
|
|
if (path == null || path.length() == 0 || !path.startsWith("/")) {
|
|
throw new IllegalArgumentException(
|
|
sm.getString("standardRoot.invalidPath", path));
|
|
}
|
|
|
|
String result;
|
|
if (File.separatorChar == '\\') {
|
|
// On Windows '\\' is a separator so in case a Windows style
|
|
// separator has managed to make it into the path, replace it.
|
|
result = RequestUtil.normalize(path, true);
|
|
} else {
|
|
// On UNIX and similar systems, '\\' is a valid file name so do not
|
|
// convert it to '/'
|
|
result = RequestUtil.normalize(path, false);
|
|
}
|
|
if (result == null || result.length() == 0 || !result.startsWith("/")) {
|
|
throw new IllegalArgumentException(
|
|
sm.getString("standardRoot.invalidPathNormal", path, result));
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
protected final WebResource getResourceInternal(String path,
|
|
boolean useClassLoaderResources) {
|
|
WebResource result = null;
|
|
WebResource virtual = null;
|
|
WebResource mainEmpty = null;
|
|
for (List<WebResourceSet> list : allResources) {
|
|
for (WebResourceSet webResourceSet : list) {
|
|
if (!useClassLoaderResources && !webResourceSet.getClassLoaderOnly() ||
|
|
useClassLoaderResources && !webResourceSet.getStaticOnly()) {
|
|
result = webResourceSet.getResource(path);
|
|
if (result.exists()) {
|
|
return result;
|
|
}
|
|
if (virtual == null) {
|
|
if (result.isVirtual()) {
|
|
virtual = result;
|
|
} else if (main.equals(webResourceSet)) {
|
|
mainEmpty = result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Use the first virtual result if no real result was found
|
|
if (virtual != null) {
|
|
return virtual;
|
|
}
|
|
|
|
// Default is empty resource in main resources
|
|
return mainEmpty;
|
|
}
|
|
|
|
@Override
|
|
public WebResource[] getResources(String path) {
|
|
return getResources(path, false);
|
|
}
|
|
|
|
private WebResource[] getResources(String path,
|
|
boolean useClassLoaderResources) {
|
|
path = validate(path);
|
|
|
|
if (isCachingAllowed()) {
|
|
return cache.getResources(path, useClassLoaderResources);
|
|
} else {
|
|
return getResourcesInternal(path, useClassLoaderResources);
|
|
}
|
|
}
|
|
|
|
protected WebResource[] getResourcesInternal(String path,
|
|
boolean useClassLoaderResources) {
|
|
List<WebResource> result = new ArrayList<>();
|
|
for (List<WebResourceSet> list : allResources) {
|
|
for (WebResourceSet webResourceSet : list) {
|
|
if (useClassLoaderResources || !webResourceSet.getClassLoaderOnly()) {
|
|
WebResource webResource = webResourceSet.getResource(path);
|
|
if (webResource.exists()) {
|
|
result.add(webResource);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (result.size() == 0) {
|
|
result.add(main.getResource(path));
|
|
}
|
|
|
|
return result.toArray(new WebResource[result.size()]);
|
|
}
|
|
|
|
@Override
|
|
public WebResource[] listResources(String path) {
|
|
return listResources(path, true);
|
|
}
|
|
|
|
protected WebResource[] listResources(String path, boolean validate) {
|
|
if (validate) {
|
|
path = validate(path);
|
|
}
|
|
|
|
String[] resources = list(path, false);
|
|
WebResource[] result = new WebResource[resources.length];
|
|
for (int i = 0; i < resources.length; i++) {
|
|
if (path.charAt(path.length() - 1) == '/') {
|
|
result[i] = getResource(path + resources[i], false, false);
|
|
} else {
|
|
result[i] = getResource(path + '/' + resources[i], false, false);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
// TODO: Should the createWebResourceSet() methods be removed to some
|
|
// utility class for file system based resource sets?
|
|
|
|
@Override
|
|
public void createWebResourceSet(ResourceSetType type, String webAppMount,
|
|
URL url, String internalPath) {
|
|
BaseLocation baseLocation = new BaseLocation(url);
|
|
createWebResourceSet(type, webAppMount, baseLocation.getBasePath(),
|
|
baseLocation.getArchivePath(), internalPath);
|
|
}
|
|
|
|
@Override
|
|
public void createWebResourceSet(ResourceSetType type, String webAppMount,
|
|
String base, String archivePath, String internalPath) {
|
|
List<WebResourceSet> resourceList;
|
|
WebResourceSet resourceSet;
|
|
|
|
switch (type) {
|
|
case PRE:
|
|
resourceList = preResources;
|
|
break;
|
|
case CLASSES_JAR:
|
|
resourceList = classResources;
|
|
break;
|
|
case RESOURCE_JAR:
|
|
resourceList = jarResources;
|
|
break;
|
|
case POST:
|
|
resourceList = postResources;
|
|
break;
|
|
default:
|
|
throw new IllegalArgumentException(
|
|
sm.getString("standardRoot.createUnknownType", type));
|
|
}
|
|
|
|
// This implementation assumes that the base for all resources will be a
|
|
// file.
|
|
File file = new File(base);
|
|
|
|
if (file.isFile()) {
|
|
if (archivePath != null) {
|
|
// Must be a JAR nested inside a WAR if archivePath is non-null
|
|
resourceSet = new JarWarResourceSet(this, webAppMount, base,
|
|
archivePath, internalPath);
|
|
} else if (file.getName().toLowerCase(Locale.ENGLISH).endsWith(".jar")) {
|
|
resourceSet = new JarResourceSet(this, webAppMount, base,
|
|
internalPath);
|
|
} else {
|
|
resourceSet = new FileResourceSet(this, webAppMount, base,
|
|
internalPath);
|
|
}
|
|
} else if (file.isDirectory()) {
|
|
resourceSet =
|
|
new DirResourceSet(this, webAppMount, base, internalPath);
|
|
} else {
|
|
throw new IllegalArgumentException(
|
|
sm.getString("standardRoot.createInvalidFile", file));
|
|
}
|
|
|
|
if (type.equals(ResourceSetType.CLASSES_JAR)) {
|
|
resourceSet.setClassLoaderOnly(true);
|
|
} else if (type.equals(ResourceSetType.RESOURCE_JAR)) {
|
|
resourceSet.setStaticOnly(true);
|
|
}
|
|
|
|
resourceList.add(resourceSet);
|
|
}
|
|
|
|
@Override
|
|
public void addPreResources(WebResourceSet webResourceSet) {
|
|
webResourceSet.setRoot(this);
|
|
preResources.add(webResourceSet);
|
|
}
|
|
|
|
@Override
|
|
public WebResourceSet[] getPreResources() {
|
|
return preResources.toArray(new WebResourceSet[preResources.size()]);
|
|
}
|
|
|
|
@Override
|
|
public void addJarResources(WebResourceSet webResourceSet) {
|
|
webResourceSet.setRoot(this);
|
|
jarResources.add(webResourceSet);
|
|
}
|
|
|
|
@Override
|
|
public WebResourceSet[] getJarResources() {
|
|
return jarResources.toArray(new WebResourceSet[jarResources.size()]);
|
|
}
|
|
|
|
@Override
|
|
public void addPostResources(WebResourceSet webResourceSet) {
|
|
webResourceSet.setRoot(this);
|
|
postResources.add(webResourceSet);
|
|
}
|
|
|
|
@Override
|
|
public WebResourceSet[] getPostResources() {
|
|
return postResources.toArray(new WebResourceSet[postResources.size()]);
|
|
}
|
|
|
|
protected WebResourceSet[] getClassResources() {
|
|
return classResources.toArray(new WebResourceSet[classResources.size()]);
|
|
}
|
|
|
|
protected void addClassResources(WebResourceSet webResourceSet) {
|
|
webResourceSet.setRoot(this);
|
|
classResources.add(webResourceSet);
|
|
}
|
|
|
|
@Override
|
|
public void setAllowLinking(boolean allowLinking) {
|
|
if (this.allowLinking != allowLinking && cachingAllowed) {
|
|
// If allow linking changes, invalidate the cache.
|
|
cache.clear();
|
|
}
|
|
this.allowLinking = allowLinking;
|
|
}
|
|
|
|
@Override
|
|
public boolean getAllowLinking() {
|
|
return allowLinking;
|
|
}
|
|
|
|
@Override
|
|
public void setCachingAllowed(boolean cachingAllowed) {
|
|
this.cachingAllowed = cachingAllowed;
|
|
if (!cachingAllowed) {
|
|
cache.clear();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean isCachingAllowed() {
|
|
return cachingAllowed;
|
|
}
|
|
|
|
@Override
|
|
public long getCacheTtl() {
|
|
return cache.getTtl();
|
|
}
|
|
|
|
@Override
|
|
public void setCacheTtl(long cacheTtl) {
|
|
cache.setTtl(cacheTtl);
|
|
}
|
|
|
|
@Override
|
|
public long getCacheMaxSize() {
|
|
return cache.getMaxSize();
|
|
}
|
|
|
|
@Override
|
|
public void setCacheMaxSize(long cacheMaxSize) {
|
|
cache.setMaxSize(cacheMaxSize);
|
|
}
|
|
|
|
@Override
|
|
public void setCacheObjectMaxSize(int cacheObjectMaxSize) {
|
|
cache.setObjectMaxSize(cacheObjectMaxSize);
|
|
// Don't enforce the limit when not running as attributes may get set in
|
|
// any order.
|
|
if (getState().isAvailable()) {
|
|
cache.enforceObjectMaxSizeLimit();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public int getCacheObjectMaxSize() {
|
|
return cache.getObjectMaxSize();
|
|
}
|
|
|
|
@Override
|
|
public void setTrackLockedFiles(boolean trackLockedFiles) {
|
|
this.trackLockedFiles = trackLockedFiles;
|
|
if (!trackLockedFiles) {
|
|
trackedResources.clear();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean getTrackLockedFiles() {
|
|
return trackLockedFiles;
|
|
}
|
|
|
|
public List<String> getTrackedResources() {
|
|
List<String> result = new ArrayList<>(trackedResources.size());
|
|
for (TrackedWebResource resource : trackedResources) {
|
|
result.add(resource.toString());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@Override
|
|
public Context getContext() {
|
|
return context;
|
|
}
|
|
|
|
@Override
|
|
public void setContext(Context context) {
|
|
this.context = context;
|
|
}
|
|
|
|
/**
|
|
* Class loader resources are handled by treating JARs in WEB-INF/lib as
|
|
* resource JARs (without the internal META-INF/resources/ prefix) mounted
|
|
* at WEB-INF/classes (rather than the web app root). This enables reuse
|
|
* of the resource handling plumbing.
|
|
*
|
|
* These resources are marked as class loader only so they are only used in
|
|
* the methods that are explicitly defined to return class loader resources.
|
|
* This prevents calls to getResource("/WEB-INF/classes") returning from one
|
|
* or more of the JAR files.
|
|
*
|
|
* @throws LifecycleException If an error occurs that should stop the web
|
|
* application from starting
|
|
*/
|
|
protected void processWebInfLib() throws LifecycleException {
|
|
WebResource[] possibleJars = listResources("/WEB-INF/lib", false);
|
|
|
|
for (WebResource possibleJar : possibleJars) {
|
|
if (possibleJar.isFile() && possibleJar.getName().endsWith(".jar")) {
|
|
createWebResourceSet(ResourceSetType.CLASSES_JAR,
|
|
"/WEB-INF/classes", possibleJar.getURL(), "/");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* For unit testing.
|
|
* @param main The main resources
|
|
*/
|
|
protected final void setMainResources(WebResourceSet main) {
|
|
this.main = main;
|
|
mainResources.clear();
|
|
mainResources.add(main);
|
|
}
|
|
|
|
|
|
@Override
|
|
public void backgroundProcess() {
|
|
cache.backgroundProcess();
|
|
gc();
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
public void gc() {
|
|
for (List<WebResourceSet> list : allResources) {
|
|
for (WebResourceSet webResourceSet : list) {
|
|
webResourceSet.gc();
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void registerTrackedResource(TrackedWebResource trackedResource) {
|
|
trackedResources.add(trackedResource);
|
|
}
|
|
|
|
|
|
@Override
|
|
public void deregisterTrackedResource(TrackedWebResource trackedResource) {
|
|
trackedResources.remove(trackedResource);
|
|
}
|
|
|
|
|
|
@Override
|
|
public List<URL> getBaseUrls() {
|
|
List<URL> result = new ArrayList<>();
|
|
for (List<WebResourceSet> list : allResources) {
|
|
for (WebResourceSet webResourceSet : list) {
|
|
if (!webResourceSet.getClassLoaderOnly()) {
|
|
URL url = webResourceSet.getBaseUrl();
|
|
if (url != null) {
|
|
result.add(url);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* Returns true if and only if all the resources for this web application
|
|
* are provided via a packed WAR file. It is used to optimise cache
|
|
* validation in this case on the basis that the WAR file will not change.
|
|
*/
|
|
protected boolean isPackedWarFile() {
|
|
return main instanceof WarResourceSet && preResources.isEmpty() && postResources.isEmpty();
|
|
}
|
|
|
|
|
|
// ----------------------------------------------------------- JMX Lifecycle
|
|
@Override
|
|
protected String getDomainInternal() {
|
|
return context.getDomain();
|
|
}
|
|
|
|
@Override
|
|
protected String getObjectNameKeyProperties() {
|
|
StringBuilder keyProperties = new StringBuilder("type=WebResourceRoot");
|
|
keyProperties.append(context.getMBeanKeyProperties());
|
|
|
|
return keyProperties.toString();
|
|
}
|
|
|
|
// --------------------------------------------------------------- Lifecycle
|
|
|
|
@Override
|
|
protected void initInternal() throws LifecycleException {
|
|
super.initInternal();
|
|
|
|
cacheJmxName = register(cache, getObjectNameKeyProperties() + ",name=Cache");
|
|
|
|
registerURLStreamHandlerFactory();
|
|
|
|
if (context == null) {
|
|
throw new IllegalStateException(
|
|
sm.getString("standardRoot.noContext"));
|
|
}
|
|
|
|
for (List<WebResourceSet> list : allResources) {
|
|
for (WebResourceSet webResourceSet : list) {
|
|
webResourceSet.init();
|
|
}
|
|
}
|
|
}
|
|
|
|
protected void registerURLStreamHandlerFactory() {
|
|
// Ensure support for jar:war:file:/ URLs will be available (required
|
|
// for resource JARs in packed WAR files).
|
|
TomcatURLStreamHandlerFactory.register();
|
|
}
|
|
|
|
@Override
|
|
protected void startInternal() throws LifecycleException {
|
|
mainResources.clear();
|
|
|
|
main = createMainResourceSet();
|
|
|
|
mainResources.add(main);
|
|
|
|
for (List<WebResourceSet> list : allResources) {
|
|
// Skip class resources since they are started below
|
|
if (list != classResources) {
|
|
for (WebResourceSet webResourceSet : list) {
|
|
webResourceSet.start();
|
|
}
|
|
}
|
|
}
|
|
|
|
// This has to be called after the other resources have been started
|
|
// else it won't find all the matching resources
|
|
processWebInfLib();
|
|
// Need to start the newly found resources
|
|
for (WebResourceSet classResource : classResources) {
|
|
classResource.start();
|
|
}
|
|
|
|
cache.enforceObjectMaxSizeLimit();
|
|
|
|
setState(LifecycleState.STARTING);
|
|
}
|
|
|
|
protected WebResourceSet createMainResourceSet() {
|
|
String docBase = context.getDocBase();
|
|
|
|
WebResourceSet mainResourceSet;
|
|
if (docBase == null) {
|
|
mainResourceSet = new EmptyResourceSet(this);
|
|
} else {
|
|
File f = new File(docBase);
|
|
if (!f.isAbsolute()) {
|
|
f = new File(((Host)context.getParent()).getAppBaseFile(), f.getPath());
|
|
}
|
|
if (f.isDirectory()) {
|
|
mainResourceSet = new DirResourceSet(this, "/", f.getAbsolutePath(), "/");
|
|
} else if(f.isFile() && docBase.endsWith(".war")) {
|
|
mainResourceSet = new WarResourceSet(this, "/", f.getAbsolutePath());
|
|
} else {
|
|
throw new IllegalArgumentException(
|
|
sm.getString("standardRoot.startInvalidMain",
|
|
f.getAbsolutePath()));
|
|
}
|
|
}
|
|
|
|
return mainResourceSet;
|
|
}
|
|
|
|
@Override
|
|
protected void stopInternal() throws LifecycleException {
|
|
for (List<WebResourceSet> list : allResources) {
|
|
for (WebResourceSet webResourceSet : list) {
|
|
webResourceSet.stop();
|
|
}
|
|
}
|
|
|
|
if (main != null) {
|
|
main.destroy();
|
|
}
|
|
mainResources.clear();
|
|
|
|
for (WebResourceSet webResourceSet : jarResources) {
|
|
webResourceSet.destroy();
|
|
}
|
|
jarResources.clear();
|
|
|
|
for (WebResourceSet webResourceSet : classResources) {
|
|
webResourceSet.destroy();
|
|
}
|
|
classResources.clear();
|
|
|
|
for (TrackedWebResource trackedResource : trackedResources) {
|
|
log.error(sm.getString("standardRoot.lockedFile",
|
|
context.getName(),
|
|
trackedResource.getName()),
|
|
trackedResource.getCreatedBy());
|
|
try {
|
|
trackedResource.close();
|
|
} catch (IOException e) {
|
|
// Ignore
|
|
}
|
|
}
|
|
cache.clear();
|
|
|
|
setState(LifecycleState.STOPPING);
|
|
}
|
|
|
|
@Override
|
|
protected void destroyInternal() throws LifecycleException {
|
|
for (List<WebResourceSet> list : allResources) {
|
|
for (WebResourceSet webResourceSet : list) {
|
|
webResourceSet.destroy();
|
|
}
|
|
}
|
|
|
|
unregister(cacheJmxName);
|
|
|
|
super.destroyInternal();
|
|
}
|
|
|
|
|
|
// Unit tests need to access this class
|
|
static class BaseLocation {
|
|
|
|
private final String basePath;
|
|
private final String archivePath;
|
|
|
|
BaseLocation(URL url) {
|
|
File f = null;
|
|
|
|
if ("jar".equals(url.getProtocol()) || "war".equals(url.getProtocol())) {
|
|
String jarUrl = url.toString();
|
|
int endOfFileUrl = -1;
|
|
if ("jar".equals(url.getProtocol())) {
|
|
endOfFileUrl = jarUrl.indexOf("!/");
|
|
} else {
|
|
endOfFileUrl = jarUrl.indexOf(UriUtil.getWarSeparator());
|
|
}
|
|
String fileUrl = jarUrl.substring(4, endOfFileUrl);
|
|
try {
|
|
f = new File(new URL(fileUrl).toURI());
|
|
} catch (MalformedURLException | URISyntaxException e) {
|
|
throw new IllegalArgumentException(e);
|
|
}
|
|
int startOfArchivePath = endOfFileUrl + 2;
|
|
if (jarUrl.length() > startOfArchivePath) {
|
|
archivePath = jarUrl.substring(startOfArchivePath);
|
|
} else {
|
|
archivePath = null;
|
|
}
|
|
} else if ("file".equals(url.getProtocol())){
|
|
try {
|
|
f = new File(url.toURI());
|
|
} catch (URISyntaxException e) {
|
|
throw new IllegalArgumentException(e);
|
|
}
|
|
archivePath = null;
|
|
} else {
|
|
throw new IllegalArgumentException(sm.getString(
|
|
"standardRoot.unsupportedProtocol", url.getProtocol()));
|
|
}
|
|
|
|
basePath = f.getAbsolutePath();
|
|
}
|
|
|
|
|
|
String getBasePath() {
|
|
return basePath;
|
|
}
|
|
|
|
|
|
String getArchivePath() {
|
|
return archivePath;
|
|
}
|
|
}
|
|
}
|