org.jboss.modules.xml.ModuleXmlParser Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2014 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed 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.jboss.modules.xml;
import java.io.BufferedInputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.AllPermission;
import java.security.Permissions;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import java.util.jar.JarFile;
import java.util.zip.ZipEntry;
import java.util.zip.ZipFile;
import org.jboss.modules.AssertionSetting;
import org.jboss.modules.DependencySpec;
import org.jboss.modules.maven.ArtifactCoordinates;
import org.jboss.modules.maven.MavenArtifactUtil;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoadException;
import org.jboss.modules.ModuleLoader;
import org.jboss.modules.ModuleSpec;
import org.jboss.modules.NativeLibraryResourceLoader;
import org.jboss.modules.PathUtils;
import org.jboss.modules.ResourceLoader;
import org.jboss.modules.ResourceLoaderSpec;
import org.jboss.modules.ResourceLoaders;
import org.jboss.modules.filter.MultiplePathFilterBuilder;
import org.jboss.modules.filter.PathFilter;
import org.jboss.modules.filter.PathFilters;
import org.jboss.modules.maven.MavenResolver;
import org.jboss.modules.security.FactoryPermissionCollection;
import org.jboss.modules.security.ModularPermissionFactory;
import org.jboss.modules.security.PermissionFactory;
import static org.jboss.modules.xml.XmlPullParser.CDSECT;
import static org.jboss.modules.xml.XmlPullParser.COMMENT;
import static org.jboss.modules.xml.XmlPullParser.DOCDECL;
import static org.jboss.modules.xml.XmlPullParser.END_DOCUMENT;
import static org.jboss.modules.xml.XmlPullParser.END_TAG;
import static org.jboss.modules.xml.XmlPullParser.ENTITY_REF;
import static org.jboss.modules.xml.XmlPullParser.FEATURE_PROCESS_NAMESPACES;
import static org.jboss.modules.xml.XmlPullParser.IGNORABLE_WHITESPACE;
import static org.jboss.modules.xml.XmlPullParser.PROCESSING_INSTRUCTION;
import static org.jboss.modules.xml.XmlPullParser.START_DOCUMENT;
import static org.jboss.modules.xml.XmlPullParser.START_TAG;
import static org.jboss.modules.xml.XmlPullParser.TEXT;
/**
* A fast, validating {@code module.xml} parser.
*
* @author David M. Lloyd
* @author [email protected]
*/
public final class ModuleXmlParser {
/**
* A factory for resource roots, based on a root path, loader path, and loader name. Normally it is sufficient to
* accept the default.
*/
public interface ResourceRootFactory {
ResourceLoader createResourceLoader(final String rootPath, final String loaderPath, final String loaderName) throws IOException;
}
private ModuleXmlParser() {
}
private static final String MODULE_1_0 = "urn:jboss:module:1.0";
private static final String MODULE_1_1 = "urn:jboss:module:1.1";
private static final String MODULE_1_2 = "urn:jboss:module:1.2";
private static final String MODULE_1_3 = "urn:jboss:module:1.3";
// there is no 1.4
private static final String MODULE_1_5 = "urn:jboss:module:1.5";
private static final String E_MODULE = "module";
private static final String E_ARTIFACT = "artifact";
private static final String E_NATIVE_ARTIFACT = "native-artifact";
private static final String E_DEPENDENCIES = "dependencies";
private static final String E_RESOURCES = "resources";
private static final String E_MAIN_CLASS = "main-class";
private static final String E_RESOURCE_ROOT = "resource-root";
private static final String E_PATH = "path";
private static final String E_EXPORTS = "exports";
private static final String E_IMPORTS = "imports";
private static final String E_INCLUDE = "include";
private static final String E_EXCLUDE = "exclude";
private static final String E_INCLUDE_SET = "include-set";
private static final String E_EXCLUDE_SET = "exclude-set";
private static final String E_FILTER = "filter";
private static final String E_SYSTEM = "system";
private static final String E_PATHS = "paths";
private static final String E_MODULE_ALIAS = "module-alias";
private static final String E_MODULE_ABSENT = "module-absent";
private static final String E_PROPERTIES = "properties";
private static final String E_PROPERTY = "property";
private static final String E_PERMISSIONS = "permissions";
private static final String E_GRANT = "grant";
private static final String A_NAME = "name";
private static final String A_SLOT = "slot";
private static final String A_EXPORT = "export";
private static final String A_SERVICES = "services";
private static final String A_PATH = "path";
private static final String A_OPTIONAL = "optional";
private static final String A_TARGET_NAME = "target-name";
private static final String A_TARGET_SLOT = "target-slot";
private static final String A_VALUE = "value";
private static final String A_PERMISSION = "permission";
private static final String A_ACTIONS = "actions";
private static final String D_NONE = "none";
private static final String D_IMPORT = "import";
private static final String D_EXPORT = "export";
private static final List LIST_A_NAME = Collections.singletonList(A_NAME);
private static final List LIST_A_PATH = Collections.singletonList(A_PATH);
private static final List LIST_A_NAME_A_SLOT = Arrays.asList(A_NAME, A_SLOT);
private static final List LIST_A_NAME_A_TARGET_NAME = Arrays.asList(A_NAME, A_TARGET_NAME);
private static final List LIST_A_PERMISSION_A_NAME = Arrays.asList(A_PERMISSION, A_NAME);
/**
* Parse a {@code module.xml} file.
*
* @param moduleLoader the module loader to use for dependency specifications
* @param moduleIdentifier the module identifier of the module to load
* @param root the module path root
* @param moduleInfoFile the {@code File} of the {@code module.xml} content
* @return a module specification
* @throws ModuleLoadException if a dependency could not be established or another error occurs
* @throws IOException if I/O fails
*/
public static ModuleSpec parseModuleXml(final ModuleLoader moduleLoader, final ModuleIdentifier moduleIdentifier, final File root, final File moduleInfoFile) throws ModuleLoadException, IOException {
final FileInputStream fis;
try {
fis = new FileInputStream(moduleInfoFile);
} catch (FileNotFoundException e) {
throw new ModuleLoadException("No module.xml file found at " + moduleInfoFile);
}
try {
return parseModuleXml((rootPath, loaderPath, loaderName) -> {
final File file;
final File loaderFile = new File(loaderPath);
if (loaderFile.isAbsolute()) {
file = loaderFile;
} else {
file = new File(rootPath, loaderPath);
}
if (file.isDirectory()) {
return ResourceLoaders.createFileResourceLoader(loaderName, file);
} else {
final JarFile jarFile = new JarFile(file, true);
return ResourceLoaders.createJarResourceLoader(loaderName, jarFile);
}
}, root.getPath(), new BufferedInputStream(fis), moduleInfoFile.getPath(), moduleLoader, moduleIdentifier);
} finally {
safeClose(fis);
}
}
/**
* Parse a {@code module.xml} file.
*
* @param factory the resource root factory to use (must not be {@code null})
* @param rootPath the root path to send in to the resource root factory (must not be {@code null})
* @param source a stream of the {@code module.xml} content (must not be {@code null})
* @param moduleInfoFile the {@code File} of the {@code module.xml} content (must not be {@code null})
* @param moduleLoader the module loader to use for dependency specifications (must not be {@code null})
* @param moduleIdentifier the module identifier of the module to load
* @return a module specification
* @throws ModuleLoadException if a dependency could not be established or another error occurs
* @throws IOException if I/O fails
*/
public static ModuleSpec parseModuleXml(final ResourceRootFactory factory, final String rootPath, InputStream source, final String moduleInfoFile, final ModuleLoader moduleLoader, final ModuleIdentifier moduleIdentifier) throws ModuleLoadException, IOException {
return parseModuleXml(factory, MavenResolver.createDefaultResolver(), rootPath, source, moduleInfoFile, moduleLoader, moduleIdentifier);
}
/**
* Parse a {@code module.xml} file.
*
* @param factory the resource root factory to use (must not be {@code null})
* @param mavenResolver the Maven artifact resolver to use (must not be {@code null})
* @param rootPath the root path to send in to the resource root factory (must not be {@code null})
* @param source a stream of the {@code module.xml} content (must not be {@code null})
* @param moduleInfoFile the {@code File} of the {@code module.xml} content (must not be {@code null})
* @param moduleLoader the module loader to use for dependency specifications (must not be {@code null})
* @param moduleIdentifier the module identifier of the module to load
* @return a module specification
* @throws ModuleLoadException if a dependency could not be established or another error occurs
* @throws IOException if I/O fails
*/
public static ModuleSpec parseModuleXml(final ResourceRootFactory factory, final MavenResolver mavenResolver, final String rootPath, InputStream source, final String moduleInfoFile, final ModuleLoader moduleLoader, final ModuleIdentifier moduleIdentifier) throws ModuleLoadException, IOException {
try {
final MXParser parser = new MXParser();
parser.setFeature(FEATURE_PROCESS_NAMESPACES, true);
parser.setInput(source, null);
return parseDocument(mavenResolver, factory, rootPath, parser, moduleLoader, moduleIdentifier);
} catch (XmlPullParserException e) {
throw new ModuleLoadException("Error loading module from " + moduleInfoFile, e);
} finally {
safeClose(source);
}
}
public static XmlPullParserException unexpectedContent(final XmlPullParser reader) {
final String kind;
switch (reader.getEventType()) {
case CDSECT: kind = "cdata"; break;
case COMMENT: kind = "comment"; break;
case DOCDECL: kind = "document decl"; break;
case END_DOCUMENT: kind = "document end"; break;
case END_TAG: kind = "element end"; break;
case ENTITY_REF: kind = "entity ref"; break;
case PROCESSING_INSTRUCTION: kind = "processing instruction"; break;
case IGNORABLE_WHITESPACE: kind = "whitespace"; break;
case START_DOCUMENT: kind = "document start"; break;
case START_TAG: kind = "element start"; break;
case TEXT: kind = "text"; break;
default: kind = "unknown"; break;
}
final StringBuilder b = new StringBuilder("Unexpected content of type '").append(kind).append('\'');
if (reader.getName() != null) {
b.append(" named '").append(reader.getName()).append('\'');
}
if (reader.getText() != null) {
b.append(", text is: '").append(reader.getText()).append('\'');
}
return new XmlPullParserException(b.toString(), reader, null);
}
public static XmlPullParserException endOfDocument(final XmlPullParser reader) {
return new XmlPullParserException("Unexpected end of document", reader, null);
}
private static XmlPullParserException invalidModuleName(final XmlPullParser reader, final ModuleIdentifier expected) {
return new XmlPullParserException("Invalid/mismatched module name (expected " + expected + ")", reader, null);
}
private static XmlPullParserException missingAttributes(final XmlPullParser reader, final Set required) {
final StringBuilder b = new StringBuilder("Missing one or more required attributes:");
for (String attribute : required) {
b.append(' ').append(attribute);
}
return new XmlPullParserException(b.toString(), reader, null);
}
private static XmlPullParserException unknownAttribute(final XmlPullParser parser, final int index) {
final String namespace = parser.getAttributeNamespace(index);
final String prefix = parser.getAttributePrefix(index);
final String name = parser.getAttributeName(index);
final StringBuilder eb = new StringBuilder("Unknown attribute \"");
if (prefix != null) eb.append(prefix).append(':');
eb.append(name);
if (namespace != null) eb.append("\" from namespace \"").append(namespace);
eb.append('"');
return new XmlPullParserException(eb.toString(), parser, null);
}
private static XmlPullParserException unknownAttributeValue(final XmlPullParser parser, final int index) {
final String namespace = parser.getAttributeNamespace(index);
final String prefix = parser.getAttributePrefix(index);
final String name = parser.getAttributeName(index);
final StringBuilder eb = new StringBuilder("Unknown value \"");
eb.append(parser.getAttributeValue(index));
eb.append("\" for attribute \"");
if (prefix != null && ! prefix.isEmpty()) eb.append(prefix).append(':');
eb.append(name);
if (namespace != null && ! namespace.isEmpty()) eb.append("\" from namespace \"").append(namespace);
eb.append('"');
return new XmlPullParserException(eb.toString(), parser, null);
}
private static void validateNamespace(final XmlPullParser reader) throws XmlPullParserException {
switch (reader.getNamespace()) {
case MODULE_1_0:
case MODULE_1_1:
case MODULE_1_2:
case MODULE_1_3:
case MODULE_1_5:
break;
default: throw unexpectedContent(reader);
}
}
private static void assertNoAttributes(final XmlPullParser reader) throws XmlPullParserException {
final int attributeCount = reader.getAttributeCount();
if (attributeCount > 0) {
throw unknownAttribute(reader, 0);
}
}
private static void validateAttributeNamespace(final XmlPullParser reader, final int index) throws XmlPullParserException {
if (! reader.getAttributeNamespace(index).isEmpty()) {
throw unknownAttribute(reader, index);
}
}
private static ModuleSpec parseDocument(final MavenResolver mavenResolver, final ResourceRootFactory factory, final String rootPath, XmlPullParser reader, final ModuleLoader moduleLoader, final ModuleIdentifier moduleIdentifier) throws XmlPullParserException, IOException {
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case START_DOCUMENT: {
return parseRootElement(mavenResolver, factory, rootPath, reader, moduleLoader, moduleIdentifier);
}
case START_TAG: {
final String element = reader.getName();
switch (element) {
case E_MODULE: {
final ModuleSpec.Builder specBuilder = ModuleSpec.build(moduleIdentifier);
parseModuleContents(mavenResolver, reader, factory, moduleLoader, moduleIdentifier, specBuilder, rootPath);
parseEndDocument(reader);
return specBuilder.create();
}
case E_MODULE_ALIAS: {
final ModuleSpec moduleSpec = parseModuleAliasContents(reader, moduleIdentifier);
parseEndDocument(reader);
return moduleSpec;
}
case E_MODULE_ABSENT: {
parseModuleAbsentContents(reader, moduleIdentifier);
return null;
}
default: {
throw unexpectedContent(reader);
}
}
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static ModuleSpec parseRootElement(final MavenResolver mavenResolver, final ResourceRootFactory factory, final String rootPath, final XmlPullParser reader, final ModuleLoader moduleLoader, final ModuleIdentifier moduleIdentifier) throws XmlPullParserException, IOException {
assertNoAttributes(reader);
int eventType;
while ((eventType = reader.nextTag()) != END_DOCUMENT) {
switch (eventType) {
case START_TAG: {
validateNamespace(reader);
final String element = reader.getName();
switch (element) {
case E_MODULE: {
final ModuleSpec.Builder specBuilder = ModuleSpec.build(moduleIdentifier);
parseModuleContents(mavenResolver, reader, factory, moduleLoader, moduleIdentifier, specBuilder, rootPath);
parseEndDocument(reader);
return specBuilder.create();
}
case E_MODULE_ALIAS: {
final ModuleSpec moduleSpec = parseModuleAliasContents(reader, moduleIdentifier);
parseEndDocument(reader);
return moduleSpec;
}
case E_MODULE_ABSENT: {
parseModuleAbsentContents(reader, moduleIdentifier);
return null;
}
default: {
throw unexpectedContent(reader);
}
}
}
default: {
throw unexpectedContent(reader);
}
}
}
throw endOfDocument(reader);
}
private static ModuleSpec parseModuleAliasContents(final XmlPullParser reader, final ModuleIdentifier moduleIdentifier) throws XmlPullParserException, IOException {
final int count = reader.getAttributeCount();
String name = null;
String slot = null;
String targetName = null;
String targetSlot = null;
final Set required = new HashSet<>(LIST_A_NAME_A_TARGET_NAME);
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_NAME: name = reader.getAttributeValue(i); break;
case A_SLOT: slot = reader.getAttributeValue(i); break;
case A_TARGET_NAME: targetName = reader.getAttributeValue(i); break;
case A_TARGET_SLOT: targetSlot = reader.getAttributeValue(i); break;
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
if (! moduleIdentifier.equals(ModuleIdentifier.create(name, slot))) {
throw invalidModuleName(reader, moduleIdentifier);
}
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
return ModuleSpec.buildAlias(moduleIdentifier, ModuleIdentifier.create(targetName, targetSlot)).create();
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parseModuleAbsentContents(final XmlPullParser reader, final ModuleIdentifier moduleIdentifier) throws XmlPullParserException, IOException {
final int count = reader.getAttributeCount();
String name = null;
String slot = null;
final Set required = new HashSet<>(LIST_A_NAME_A_SLOT);
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_NAME: name = reader.getAttributeValue(i); break;
case A_SLOT: slot = reader.getAttributeValue(i); break;
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
if (! moduleIdentifier.equals(ModuleIdentifier.create(name, slot))) {
throw invalidModuleName(reader, moduleIdentifier);
}
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
return;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static final AllPermission ALL_PERMISSION = new AllPermission();
static final Permissions DEFAULT_PERMISSION_COLLECTION = getAllPermission();
private static Permissions getAllPermission() {
final Permissions permissions = new Permissions();
permissions.add(ALL_PERMISSION);
return permissions;
}
private static void parseModuleContents(final MavenResolver mavenResolver, final XmlPullParser reader, final ResourceRootFactory factory, final ModuleLoader moduleLoader, final ModuleIdentifier moduleIdentifier, final ModuleSpec.Builder specBuilder, final String rootPath) throws XmlPullParserException, IOException {
final int count = reader.getAttributeCount();
String name = null;
String slot = null;
final Set required = new HashSet<>(LIST_A_NAME);
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_NAME: name = reader.getAttributeValue(i); break;
case A_SLOT: slot = reader.getAttributeValue(i); break;
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
if (! specBuilder.getIdentifier().equals(ModuleIdentifier.create(name, slot))) {
throw invalidModuleName(reader, specBuilder.getIdentifier());
}
// xsd:all
MultiplePathFilterBuilder exportsBuilder = PathFilters.multiplePathFilterBuilder(true);
ArrayList dependencies = new ArrayList<>();
Set visited = new HashSet<>();
int eventType;
boolean gotPerms = false;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
specBuilder.addDependency(DependencySpec.createLocalDependencySpec(PathFilters.acceptAll(), exportsBuilder.create()));
for (DependencySpec dependency : dependencies) {
specBuilder.addDependency(dependency);
}
if (! gotPerms) specBuilder.setPermissionCollection(DEFAULT_PERMISSION_COLLECTION);
return;
}
case START_TAG: {
validateNamespace(reader);
final String element = reader.getName();
if (visited.contains(element)) {
throw unexpectedContent(reader);
}
visited.add(element);
switch (element) {
case E_EXPORTS: parseFilterList(reader, exportsBuilder); break;
case E_DEPENDENCIES: parseDependencies(reader, dependencies); break;
case E_MAIN_CLASS: parseMainClass(reader, specBuilder); break;
case E_RESOURCES: parseResources(mavenResolver, factory, rootPath, reader, specBuilder); break;
case E_PROPERTIES: parseProperties(reader, specBuilder); break;
case E_PERMISSIONS: parsePermissions(reader, moduleLoader, moduleIdentifier, specBuilder); gotPerms = true; break;
default: throw unexpectedContent(reader);
}
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parseDependencies(final XmlPullParser reader, final ArrayList dependencies) throws XmlPullParserException, IOException {
assertNoAttributes(reader);
// xsd:choice
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
return;
}
case START_TAG: {
validateNamespace(reader);
switch (reader.getName()) {
case E_MODULE: parseModuleDependency(reader, dependencies); break;
case E_SYSTEM: parseSystemDependency(reader, dependencies); break;
default: throw unexpectedContent(reader);
}
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parseModuleDependency(final XmlPullParser reader, final ArrayList dependencies) throws XmlPullParserException, IOException {
String name = null;
String slot = null;
boolean export = false;
boolean optional = false;
String services = D_NONE;
final Set required = new HashSet<>(LIST_A_NAME);
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_NAME: name = reader.getAttributeValue(i); break;
case A_SLOT: slot = reader.getAttributeValue(i); break;
case A_EXPORT: export = Boolean.parseBoolean(reader.getAttributeValue(i)); break;
case A_OPTIONAL: optional = Boolean.parseBoolean(reader.getAttributeValue(i)); break;
case A_SERVICES: {
services = reader.getAttributeValue(i);
switch (services) {
case D_NONE:
case D_IMPORT:
case D_EXPORT:
break;
default: throw unknownAttributeValue(reader, i);
}
break;
}
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
final MultiplePathFilterBuilder importBuilder = PathFilters.multiplePathFilterBuilder(true);
final MultiplePathFilterBuilder exportBuilder = PathFilters.multiplePathFilterBuilder(export);
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
assert services.equals(D_NONE) || services.equals(D_EXPORT) || services.equals(D_IMPORT);
if (services.equals(D_EXPORT)) {
// If services are to be re-exported, add META-INF/services -> true near the end of the list
exportBuilder.addFilter(PathFilters.getMetaInfServicesFilter(), true);
}
if (export) {
// If re-exported, add META-INF/** -> false at the end of the list (require explicit override)
exportBuilder.addFilter(PathFilters.getMetaInfSubdirectoriesFilter(), false);
exportBuilder.addFilter(PathFilters.getMetaInfFilter(), false);
}
final PathFilter exportFilter = exportBuilder.create();
final PathFilter importFilter;
if (importBuilder.isEmpty()) {
importFilter = services.equals(D_NONE) ? PathFilters.getDefaultImportFilter() : PathFilters.getDefaultImportFilterWithServices();
} else {
if (! services.equals(D_NONE)) {
importBuilder.addFilter(PathFilters.getMetaInfServicesFilter(), true);
}
importBuilder.addFilter(PathFilters.getMetaInfSubdirectoriesFilter(), false);
importBuilder.addFilter(PathFilters.getMetaInfFilter(), false);
importFilter = importBuilder.create();
}
dependencies.add(DependencySpec.createModuleDependencySpec(importFilter, exportFilter, null, ModuleIdentifier.create(name, slot), optional));
return;
}
case START_TAG: {
validateNamespace(reader);
switch (reader.getName()) {
case E_EXPORTS: parseFilterList(reader, exportBuilder); break;
case E_IMPORTS: parseFilterList(reader, importBuilder); break;
default: throw unexpectedContent(reader);
}
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parseSystemDependency(final XmlPullParser reader, final ArrayList dependencies) throws XmlPullParserException, IOException {
boolean export = false;
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
switch (attribute) {
case A_EXPORT: export = Boolean.parseBoolean(reader.getAttributeValue(i)); break;
default: throw unexpectedContent(reader);
}
}
Set paths = Collections.emptySet();
final MultiplePathFilterBuilder exportBuilder = PathFilters.multiplePathFilterBuilder(export);
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
final PathFilter exportFilter = exportBuilder.create();
dependencies.add(DependencySpec.createSystemDependencySpec(PathFilters.acceptAll(), exportFilter, paths));
return;
}
case START_TAG: {
validateNamespace(reader);
switch (reader.getName()) {
case E_PATHS: {
paths = parseSet(reader);
break;
}
case E_EXPORTS: {
parseFilterList(reader, exportBuilder);
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
}
}
private static void parseMainClass(final XmlPullParser reader, final ModuleSpec.Builder specBuilder) throws XmlPullParserException, IOException {
String name = null;
final Set required = new HashSet<>(LIST_A_NAME);
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_NAME: name = reader.getAttributeValue(i); break;
default: throw unexpectedContent(reader);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
specBuilder.setMainClass(name);
// consume remainder of element
parseNoContent(reader);
}
private static void parseResources(final MavenResolver mavenResolver, final ResourceRootFactory factory, final String rootPath, final XmlPullParser reader, final ModuleSpec.Builder specBuilder) throws XmlPullParserException, IOException {
assertNoAttributes(reader);
// xsd:choice
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
specBuilder.addResourceRoot(ResourceLoaderSpec.createResourceLoaderSpec(new NativeLibraryResourceLoader(new File(rootPath, "lib")), PathFilters.rejectAll()));
return;
}
case START_TAG: {
validateNamespace(reader);
switch (reader.getName()) {
case E_RESOURCE_ROOT: {
parseResourceRoot(factory, rootPath, reader, specBuilder);
break;
}
case E_ARTIFACT: {
parseArtifact(mavenResolver, reader, specBuilder);
break;
}
case E_NATIVE_ARTIFACT: {
parseNativeArtifact(mavenResolver, reader, specBuilder);
break;
}
default: throw unexpectedContent(reader);
}
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void createMavenNativeArtifactLoader(final MavenResolver mavenResolver, final String name, final XmlPullParser reader, final ModuleSpec.Builder specBuilder) throws IOException, XmlPullParserException {
File fp = mavenResolver.resolveJarArtifact(ArtifactCoordinates.fromString(name));
if (fp == null) throw new XmlPullParserException(String.format("Failed to resolve native artifact '%s'", name), reader, null);
File lib = new File(fp.getParentFile(), "lib");
if (!lib.exists()) {
if (!fp.getParentFile().canWrite()) throw new XmlPullParserException(String.format("Native artifact '%s' cannot be unpacked", name), reader, null);
unzip(fp, fp.getParentFile());
}
specBuilder.addResourceRoot(ResourceLoaderSpec.createResourceLoaderSpec(new NativeLibraryResourceLoader(lib), PathFilters.rejectAll()));
}
private static void parseNativeArtifact(final MavenResolver mavenResolver, final XmlPullParser reader, final ModuleSpec.Builder specBuilder) throws XmlPullParserException, IOException {
String name = null;
final Set required = new HashSet<>(LIST_A_NAME);
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_NAME: name = reader.getAttributeValue(i); break;
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
try {
createMavenNativeArtifactLoader(mavenResolver, name, reader, specBuilder);
} catch (IOException e) {
throw new XmlPullParserException(String.format("Failed to add artifact '%s'", name), reader, e);
}
return;
}
case START_TAG: {
throw unexpectedContent(reader);
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parseArtifact(final MavenResolver mavenResolver, final XmlPullParser reader, final ModuleSpec.Builder specBuilder) throws XmlPullParserException, IOException {
String name = null;
final Set required = new HashSet<>(LIST_A_NAME);
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_NAME: name = reader.getAttributeValue(i); break;
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
final MultiplePathFilterBuilder filterBuilder = PathFilters.multiplePathFilterBuilder(true);
final ResourceLoader resourceLoader;
final Set encountered = new HashSet<>();
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
try {
resourceLoader = MavenArtifactUtil.createMavenArtifactLoader(mavenResolver, name);
} catch (IOException e) {
throw new XmlPullParserException(String.format("Failed to add artifact '%s'", name), reader, e);
}
if (resourceLoader == null) throw new XmlPullParserException(String.format("Failed to resolve artifact '%s'", name), reader, null);
specBuilder.addResourceRoot(ResourceLoaderSpec.createResourceLoaderSpec(resourceLoader, filterBuilder.create()));
return;
}
case START_TAG: {
validateNamespace(reader);
final String element = reader.getName();
if (! encountered.add(element)) throw unexpectedContent(reader);
switch (element) {
case E_FILTER: parseFilterList(reader, filterBuilder); break;
default: throw unexpectedContent(reader);
}
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parseResourceRoot(final ResourceRootFactory factory, final String rootPath, final XmlPullParser reader, final ModuleSpec.Builder specBuilder) throws XmlPullParserException, IOException {
String name = null;
String path = null;
final Set required = new HashSet<>(LIST_A_PATH);
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_NAME: name = reader.getAttributeValue(i); break;
case A_PATH: path = reader.getAttributeValue(i); break;
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
if (name == null) name = path;
final MultiplePathFilterBuilder filterBuilder = PathFilters.multiplePathFilterBuilder(true);
final ResourceLoader resourceLoader;
final Set encountered = new HashSet<>();
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
try {
resourceLoader = factory.createResourceLoader(rootPath, path, name);
} catch (IOException e) {
throw new XmlPullParserException(String.format("Failed to add resource root '%s' at path '%s'", name, path), reader, e);
}
specBuilder.addResourceRoot(ResourceLoaderSpec.createResourceLoaderSpec(resourceLoader, filterBuilder.create()));
return;
}
case START_TAG: {
validateNamespace(reader);
final String element = reader.getName();
if (! encountered.add(element)) throw unexpectedContent(reader);
switch (element) {
case E_FILTER: parseFilterList(reader, filterBuilder); break;
default: throw unexpectedContent(reader);
}
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parseFilterList(final XmlPullParser reader, final MultiplePathFilterBuilder builder) throws XmlPullParserException, IOException {
assertNoAttributes(reader);
// xsd:choice
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
return;
}
case START_TAG: {
validateNamespace(reader);
switch (reader.getName()) {
case E_INCLUDE: parsePath(reader, true, builder); break;
case E_EXCLUDE: parsePath(reader, false, builder); break;
case E_INCLUDE_SET: parseSet(reader, true, builder); break;
case E_EXCLUDE_SET: parseSet(reader, false, builder); break;
default: throw unexpectedContent(reader);
}
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parsePath(final XmlPullParser reader, final boolean include, final MultiplePathFilterBuilder builder) throws XmlPullParserException, IOException {
String path = null;
final Set required = new HashSet<>(LIST_A_PATH);
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_PATH: path = reader.getAttributeValue(i); break;
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
final boolean literal = path.indexOf('*') == -1 && path.indexOf('?') == -1;
if (literal) {
if (path.charAt(path.length() - 1) == '/') {
builder.addFilter(PathFilters.isChildOf(path), include);
} else {
builder.addFilter(PathFilters.is(path), include);
}
} else {
builder.addFilter(PathFilters.match(path), include);
}
// consume remainder of element
parseNoContent(reader);
}
private static Set parseSet(final XmlPullParser reader) throws XmlPullParserException, IOException {
assertNoAttributes(reader);
final Set set = new HashSet<>();
// xsd:choice
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
return set;
}
case START_TAG: {
validateNamespace(reader);
switch (reader.getName()) {
case E_PATH: parsePathName(reader, set); break;
default: throw unexpectedContent(reader);
}
}
}
}
}
private static void parseSet(final XmlPullParser reader, final boolean include, final MultiplePathFilterBuilder builder) throws XmlPullParserException, IOException {
builder.addFilter(PathFilters.in(parseSet(reader)), include);
}
private static void parsePathName(final XmlPullParser reader, final Set set) throws XmlPullParserException, IOException {
String name = null;
final Set required = new HashSet<>(LIST_A_NAME);
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_NAME: name = reader.getAttributeValue(i); break;
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
set.add(name);
// consume remainder of element
parseNoContent(reader);
}
private static void parseProperties(final XmlPullParser reader, final ModuleSpec.Builder specBuilder) throws XmlPullParserException, IOException {
assertNoAttributes(reader);
// xsd:choice
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
return;
}
case START_TAG: {
validateNamespace(reader);
switch (reader.getName()) {
case E_PROPERTY: {
parseProperty(reader, specBuilder);
break;
}
default: throw unexpectedContent(reader);
}
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parseProperty(final XmlPullParser reader, final ModuleSpec.Builder specBuilder) throws XmlPullParserException, IOException {
String name = null;
String value = null;
final Set required = new HashSet<>(LIST_A_NAME);
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_NAME: name = reader.getAttributeValue(i); break;
case A_VALUE: value = reader.getAttributeValue(i); break;
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
specBuilder.addProperty(name, value == null ? "true" : value);
if ("jboss.assertions".equals(name)) try {
specBuilder.setAssertionSetting(AssertionSetting.valueOf(value.toUpperCase(Locale.US)));
} catch (IllegalArgumentException ignored) {}
// consume remainder of element
parseNoContent(reader);
}
private static void parsePermissions(final XmlPullParser reader, final ModuleLoader moduleLoader, final ModuleIdentifier moduleIdentifier, final ModuleSpec.Builder specBuilder) throws XmlPullParserException, IOException {
assertNoAttributes(reader);
// xsd:choice
ArrayList list = new ArrayList<>();
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
specBuilder.setPermissionCollection(new FactoryPermissionCollection(list.toArray(new PermissionFactory[list.size()])));
return;
}
case START_TAG: {
validateNamespace(reader);
switch (reader.getName()) {
case E_GRANT: {
parseGrant(reader, moduleLoader, moduleIdentifier, list);
break;
}
default: throw unexpectedContent(reader);
}
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parseGrant(final XmlPullParser reader, final ModuleLoader moduleLoader, final ModuleIdentifier moduleIdentifier, final ArrayList list) throws XmlPullParserException, IOException {
String permission = null;
String name = null;
String actions = null;
final Set required = new HashSet<>(LIST_A_PERMISSION_A_NAME);
final int count = reader.getAttributeCount();
for (int i = 0; i < count; i ++) {
validateAttributeNamespace(reader, i);
final String attribute = reader.getAttributeName(i);
required.remove(attribute);
switch (attribute) {
case A_PERMISSION: permission = reader.getAttributeValue(i); break;
case A_NAME: name = reader.getAttributeValue(i); break;
case A_ACTIONS: actions = reader.getAttributeValue(i); break;
default: throw unknownAttribute(reader, i);
}
}
if (! required.isEmpty()) {
throw missingAttributes(reader, required);
}
list.add(new ModularPermissionFactory(moduleLoader, moduleIdentifier, permission, name, actions));
// consume remainder of element
parseNoContent(reader);
}
private static void parseNoContent(final XmlPullParser reader) throws XmlPullParserException, IOException {
int eventType;
for (;;) {
eventType = reader.nextTag();
switch (eventType) {
case END_TAG: {
return;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void parseEndDocument(final XmlPullParser reader) throws XmlPullParserException, IOException {
int eventType;
for (;;) {
eventType = reader.nextToken();
switch (eventType) {
case END_DOCUMENT: {
return;
}
case TEXT:
case CDSECT: {
if (! reader.isWhitespace()) {
throw unexpectedContent(reader);
}
// ignore
break;
}
case IGNORABLE_WHITESPACE:
case COMMENT: {
// ignore
break;
}
default: {
throw unexpectedContent(reader);
}
}
}
}
private static void unzip(File src, File destDir) throws IOException {
final String absolutePath = destDir.getAbsolutePath();
final ZipFile zip = new ZipFile(src);
try {
final Enumeration extends ZipEntry> entries = zip.entries();
while (entries.hasMoreElements()) {
final ZipEntry entry = entries.nextElement();
if (entry.isDirectory()) {
continue;
}
final File fp = new File(absolutePath, PathUtils.canonicalize(PathUtils.relativize(entry.getName())));
final File parent = fp.getParentFile();
if (! parent.exists()) {
parent.mkdirs();
}
final InputStream is = zip.getInputStream(entry);
try {
final FileOutputStream os = new FileOutputStream(fp);
try {
copy(is, os);
} finally {
safeClose(os);
}
} finally {
safeClose(is);
}
}
} finally {
safeClose(zip);
}
}
private static void copy(InputStream in, OutputStream out) throws IOException {
byte[] buf = new byte[16384];
int len;
while ((len = in.read(buf)) > 0) {
out.write(buf, 0, len);
}
out.flush();
}
private static void safeClose(Closeable closeable) {
if (closeable != null) try {
closeable.close();
} catch (Throwable ignored) {}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy