FragmentJarScannerCallback.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.tomcat.util.descriptor.web;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
import org.apache.tomcat.Jar;
import org.apache.tomcat.JarScannerCallback;
import org.xml.sax.InputSource;
/**
* Callback handling a web-fragment.xml descriptor.
*/
public class FragmentJarScannerCallback implements JarScannerCallback {
private static final String FRAGMENT_LOCATION =
"META-INF/web-fragment.xml";
private final WebXmlParser webXmlParser;
private final boolean delegate;
private final boolean parseRequired;
private final Map<String,WebXml> fragments = new HashMap<>();
private boolean ok = true;
public FragmentJarScannerCallback(WebXmlParser webXmlParser, boolean delegate,
boolean parseRequired) {
this.webXmlParser = webXmlParser;
this.delegate = delegate;
this.parseRequired = parseRequired;
}
@Override
public void scan(Jar jar, String webappPath, boolean isWebapp) throws IOException {
InputStream is = null;
WebXml fragment = new WebXml();
fragment.setWebappJar(isWebapp);
fragment.setDelegate(delegate);
try {
// Only web application JARs are checked for web-fragment.xml
// files.
// web-fragment.xml files don't need to be parsed if they are never
// going to be used.
if (isWebapp && parseRequired) {
is = jar.getInputStream(FRAGMENT_LOCATION);
}
if (is == null) {
// If there is no web.xml, normal JAR no impact on
// distributable
fragment.setDistributable(true);
} else {
String fragmentUrl = jar.getURL(FRAGMENT_LOCATION);
InputSource source = new InputSource(fragmentUrl);
source.setByteStream(is);
if (!webXmlParser.parseWebXml(source, fragment, true)) {
ok = false;
}
}
} finally {
addFragment(fragment, jar.getJarFileURL());
}
}
private String extractJarFileName(URL input) {
String url = input.toString();
if (url.endsWith("!/")) {
// Remove it
url = url.substring(0, url.length() - 2);
}
// File name will now be whatever is after the final /
return url.substring(url.lastIndexOf('/') + 1);
}
@Override
public void scan(File file, String webappPath, boolean isWebapp) throws IOException {
WebXml fragment = new WebXml();
fragment.setWebappJar(isWebapp);
fragment.setDelegate(delegate);
File fragmentFile = new File(file, FRAGMENT_LOCATION);
try {
if (fragmentFile.isFile()) {
try (InputStream stream = new FileInputStream(fragmentFile)) {
InputSource source =
new InputSource(fragmentFile.toURI().toURL().toString());
source.setByteStream(stream);
if (!webXmlParser.parseWebXml(source, fragment, true)) {
ok = false;
}
}
} else {
// If there is no web.xml, normal folder no impact on
// distributable
fragment.setDistributable(true);
}
} finally {
addFragment(fragment, file.toURI().toURL());
}
}
private void addFragment(WebXml fragment, URL url) {
fragment.setURL(url);
if (fragment.getName() == null) {
fragment.setName(url.toString());
}
fragment.setJarName(extractJarFileName(url));
if (fragments.containsKey(fragment.getName())) {
// Duplicate. Mark the fragment that has already been found with
// this name as having a duplicate so Tomcat can handle it
// correctly when the fragments are being ordered.
String duplicateName = fragment.getName();
fragments.get(duplicateName).addDuplicate(url.toString());
// Rename the current fragment so it doesn't clash
fragment.setName(url.toString());
}
fragments.put(fragment.getName(), fragment);
}
@Override
public void scanWebInfClasses() {
// NO-OP. Fragments unpacked in WEB-INF classes are not handled,
// mainly because if there are multiple fragments there is no way to
// handle multiple web-fragment.xml files.
}
public boolean isOk() {
return ok;
}
public Map<String,WebXml> getFragments() {
return fragments;
}
}