/*
* JBoss, Home of Professional Open Source.
* Copyright 2012, Red Hat, Inc., and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.osgi.service;
import static org.jboss.as.osgi.OSGiMessages.MESSAGES;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Properties;
import org.jboss.as.server.ServerEnvironment;
/**
* Provides a precedence-ordered list of locations from which bundles can be loaded based on the given
* {@link ServerEnvironment}.
*
* <p>
* <strong>Note:</strong> This is an adaptation of the {@code org.jboss.modules.LayeredModulePathFactory}
* class in jboss-modules, which performs a similar function for module loading.
* </p>
*
* @author Brian Stansberry (c) 2012 Red Hat Inc.
*/
public final class LayeredBundlePathFactory {
/**
* Provides a precedence-ordered list of locations from which bundles can be loaded based on the given
* {@link ServerEnvironment}.
*/
public static List<File> resolveLayeredBundlePath(final ServerEnvironment serverEnvironment) {
File bundlesDir = serverEnvironment.getBundlesDir();
if (!bundlesDir.isDirectory())
throw MESSAGES.illegalStateCannotFindBundleDir(bundlesDir);
return resolveLayeredBundlePath(bundlesDir);
}
/**
* Inspects each element in the given {@code bundlePath} to see if it includes a {@code layers.conf} file
* and/or a standard directory structure with child directories {@code system/layers} and, optionally,
* {@code system/add-ons}. If so, the layers identified in {@code layers.conf} are added to the module path
*
* @param bundlePath the filesystem locations that make up the standard bundle path, each of which is to be
* checked for the presence of layers and add-ons
*
* @return a new bundle path, including any layers and add-ons, if found
*/
public static List<File> resolveLayeredBundlePath(final File... bundlePath) {
List<File> layeredPath = new ArrayList<File>();
for (File file : bundlePath) {
// Always add the root, as the user may place modules directly in it
layeredPath.add(file);
LayersConfig layersConfig = getLayersConfig(file);
File layersDir = new File(file, layersConfig.getLayersPath());
if (!layersDir.exists()) {
if (layersConfig.isConfigured()) {
// Bad config from user
throw MESSAGES.illegalStateNoLayersDirectoryFound(layersDir);
}
// else this isn't a root that has layers and add-ons
continue;
}
boolean validLayers = true;
List<File> layerFiles = new ArrayList<File>();
for (String layerName : layersConfig.getLayers()) {
File layer = new File(layersDir, layerName);
if (!layer.exists()) {
if (layersConfig.isConfigured()) {
// Bad config from user
throw MESSAGES.illegalStateCannotFindLayer(layerName, layersDir);
}
// else this isn't a standard layers and add-ons structure
validLayers = false;
break;
}
layerFiles.add(layer);
}
if (validLayers) {
layeredPath.addAll(layerFiles);
// Now add-ons
File[] addOns = new File(file, layersConfig.getAddOnsPath()).listFiles();
if (addOns != null) {
for (File addOn : addOns) {
if (addOn.isDirectory()) {
layeredPath.add(addOn);
}
}
}
}
}
return layeredPath;
}
private static LayersConfig getLayersConfig(File repoRoot) {
File layersList = new File(repoRoot, "layers.conf");
if (!layersList.exists()) {
return new LayersConfig();
}
Reader reader = null;
try {
reader = new InputStreamReader(new FileInputStream(layersList), "UTF-8");
Properties props = new Properties();
props.load(reader);
return new LayersConfig(props);
} catch (IOException e) {
throw new RuntimeException(e);
} finally {
safeClose(reader);
}
}
private static void safeClose(Closeable c) {
if (c != null) try {
c.close();
} catch (Throwable ignored) {}
}
private static class LayersConfig {
private static final String DEFAULT_LAYERS_PATH = "system/layers";
private static final String DEFAULT_ADD_ONS_PATH = "system/add-ons";
private final boolean configured;
private final String layersPath;
private final String addOnsPath;
private final List<String> layers;
private LayersConfig() {
configured = false;
layersPath = DEFAULT_LAYERS_PATH;
addOnsPath = DEFAULT_ADD_ONS_PATH;
layers = Collections.singletonList("base");
}
private LayersConfig(Properties properties) {
configured = true;
// Possible future enhancement; probably better to use an xml file. This should evolve consistently
// with what is done in jboss-modules
// layersPath = properties.getProperty("layers.path", DEFAULT_LAYERS_PATH);
// addOnsPath = properties.getProperty("add-ons.path", DEFAULT_ADD_ONS_PATH);
// boolean excludeBase = Boolean.valueOf(properties.getProperty("exclude.base.layer", "false"));
layersPath = DEFAULT_LAYERS_PATH;
addOnsPath = DEFAULT_ADD_ONS_PATH;
boolean excludeBase = false;
String layersProp = (String) properties.get("layers");
if (layersProp == null || (layersProp = layersProp.trim()).length() == 0) {
if (excludeBase) {
layers = Collections.emptyList();
} else {
layers = Collections.singletonList("base");
}
} else {
String[] layerNames = layersProp.split(",");
layers = new ArrayList<String>();
boolean hasBase = false;
for (String layerName : layerNames) {
if ("base".equals(layerName)) {
hasBase = true;
}
layers.add(layerName);
}
if (!hasBase && !excludeBase) {
layers.add("base");
}
}
}
boolean isConfigured() {
return configured;
}
String getLayersPath() {
return layersPath;
}
String getAddOnsPath() {
return addOnsPath;
}
List<String> getLayers() {
return layers;
}
}
}