/**
* Copyright 2011-2012 Universite Joseph Fourier, LIG, ADELE team
* 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 fr.imag.adele.obrMan.internal;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.HashSet;
import java.util.Properties;
import java.util.Set;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;
import org.osgi.framework.BundleContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import fr.imag.adele.apam.CompositeType;
import fr.imag.adele.apam.ManagerModel;
import fr.imag.adele.apam.impl.CompositeTypeImpl;
/**
* This class represents the OBR model associated with a given composite.
*
* The model specifies the bundle repositories available when a resolution is requested
* in the specified context.
*
* @author vega
*
*/
public class Model {
private static Logger logger = LoggerFactory.getLogger(Model.class);
/**
* Global framework properties used to configure OBR
*/
private static final String OSGI_OBR_REPOSITORY_URL = "obr.repository.url";
private static final String ROOT_MODEL_URL = "apam.root.model.url";
/**
* Properties available in the OBRMan model associated with a composite
*/
private static final String LOCAL_MAVEN_REPOSITORY = "LocalMavenRepository";
private static final String DEFAULT_OSGI_REPOSITORIES = "DefaultOSGiRepositories";
private static final String REPOSITORIES = "Repositories";
private static final String COMPOSITES = "Composites";
/**
* Loads the OBRMan model of the specified context, and return the list of configured repositories specified
* in the model.
*
* Returns NULL if the model is not specified, or can not be loaded.
*
*/
public static Model loadModel(OBRMan obrManager, CompositeType context, BundleContext osgiContext) {
/*
* Get the model, if specified
*/
ManagerModel model = context.getModel(obrManager);
if (model == null || model.getURL() == null)
return null;
/*
* Try to load the model from the specified location, as a map of properties
*/
Properties configuration = null;
try {
configuration = new Properties();
configuration.load(model.getURL().openStream());
return new Model(obrManager, context, configuration, osgiContext);
} catch (IOException e) {
logger.error("Invalid OBRMAN Model. Cannot read stream " + model.getURL(), e.getCause());
return null;
}
}
/**
* Loads the OBR model associated with the root composite context
*/
public static Model loadDefaultRootModel(OBRMan obrManager, BundleContext osgiContext) {
Properties configuration = new Properties();
/*
* Try loading from a globally specified platform location
*/
String rootModelLocation = osgiContext.getProperty(ROOT_MODEL_URL);
if (configuration.isEmpty() && rootModelLocation != null) {
try {
configuration.load(new URL(rootModelLocation).openStream());
} catch (IOException e) {
logger.error("Invalid OBRMAN Model. Cannot read global root model at " + rootModelLocation, e.getCause());
}
}
/*
* If no configuration was specified, initialize the default
*/
if (configuration.isEmpty()) {
configuration.put(Model.LOCAL_MAVEN_REPOSITORY, "true");
configuration.put(Model.DEFAULT_OSGI_REPOSITORIES, "true");
}
return new Model(obrManager, CompositeTypeImpl.getRootCompositeType(), configuration, osgiContext);
}
/**
* Loads the OBR model associated with the root composite context from a given location.
*
* This is useful to force at runtime to use a given repository for the root context. We do not try
* to use the default behavior, but fail with a error if the location is invalid
*/
public static Model loadRootModel(OBRMan obrManager, BundleContext osgiContext, URL location) throws IOException {
Properties configuration = new Properties();
configuration.load(location.openStream());
return new Model(obrManager, CompositeTypeImpl.getRootCompositeType(), configuration, osgiContext);
}
/**
* The list of configured repositories
*/
private final Set<URI> repositories;
/**
* Builds a new model from the specified configuration
*/
private Model(OBRMan obrManager, CompositeType context, Properties configuration, BundleContext osgiContext) {
this.repositories = new HashSet<URI>();
/*
* Get the repository list, specified in the model
*/
for (Object key : configuration.keySet()) {
if (LOCAL_MAVEN_REPOSITORY.equals(key)) {
// Add the obr repository located in the local maven repository
boolean useMavenBundleRepository = Boolean.valueOf(configuration.getProperty(LOCAL_MAVEN_REPOSITORY));
if (useMavenBundleRepository) {
try {
repositories.add(getLocalMavenBundleRepository());
} catch (Exception e) {
logger.error("Error when adding default local repository to obr manager", e.getCause());
}
}
}
else if (REPOSITORIES.equals(key)) {
// Add obr repositories declared in the composite
for (String repositoryLocation : configuration.getProperty(REPOSITORIES).split("\\s+")) {
try {
repositories.add(new URI(repositoryLocation));
} catch (Exception e) {
logger.error("Error when adding default local repository to obr manager :" + repositoryLocation, e.getCause());
}
}
}
else if (Model.DEFAULT_OSGI_REPOSITORIES.equals(key)) {
// Add obr repositories declared in the osgi configuration file
boolean useGlobalOSGiRepository = Boolean.valueOf(configuration.getProperty(DEFAULT_OSGI_REPOSITORIES));
if (useGlobalOSGiRepository) {
String osgiRepositoryLocations = osgiContext.getProperty(OSGI_OBR_REPOSITORY_URL);
if (osgiRepositoryLocations != null) {
for (String osgiRepositoryLocation : osgiRepositoryLocations.split("\\s+")) {
try {
repositories.add(new URI(osgiRepositoryLocation));
} catch (Exception e) {
logger.error("Error when adding default local repository to obr manager :" + osgiRepositoryLocations, e.getCause());
}
}
}
}
}
else if (Model.COMPOSITES.equals(key)) {
// look for obr repositories in other composites
String[] otherCompositesRepositories = configuration.getProperty(COMPOSITES).split("\\s+");
for (String composite : otherCompositesRepositories) {
CompositeType importedContext = obrManager.getApam().getCompositeType(composite);
Model importedModel = importedContext != null ? obrManager.getModel(importedContext) : null;
if (importedContext != null && importedModel != null) {
repositories.addAll(importedModel.getRepositoryLocations());
} else {
// If the compositeType is not present, do nothing
logger.error("The composite " + context.getName() + " reference a missing composite " + composite);
}
}
}
}
}
/**
* The list of repositories configured in this model
*/
public Set<URI> getRepositoryLocations() {
return repositories;
};
public Set<String> getRepositories() {
Set<String> result = new HashSet<String>();
for (URI repositoryLocation :repositories) {
result.add(repositoryLocation.toString());
}
return result;
}
/**
* The OSGi Bundle Repository associated with the local maven installation.
*/
private static URI getLocalMavenBundleRepository() {
// try to find the maven settings.xml file
File globalSettings = getGlobalMavenSettings();
File userSettings = getUserMavenSettings();
/*
* Extract localRepository from settings.xml
*
* If a global and a user settings are both specified, the
* user preferences override the global
*/
URI userDefinedRepository = null;
URI globalDefinedRepository = null;
URI defaultRepository = null;
if (userSettings != null) {
userDefinedRepository = getMavenBundleRepository(userSettings);
}
if (globalSettings != null) {
globalDefinedRepository = getMavenBundleRepository(globalSettings);
}
defaultRepository = getDefaultBundleRepository();
if (userDefinedRepository != null) {
return userDefinedRepository;
}
if (globalDefinedRepository != null) {
return globalDefinedRepository;
}
if (defaultRepository != null) {
return defaultRepository;
}
throw new IllegalArgumentException(new NullPointerException(
"Could not find local repository location in : "+userSettings+" "+globalSettings+" "+defaultRepository)
);
}
/**
* Get the OSGi Bundle Repository associated with the maven repository location specified in
* the maven settings file.
*
*/
private static URI getMavenBundleRepository(File settingsFile) {
try {
SAXParserFactory factory = SAXParserFactory.newInstance();
SAXParser saxParser = factory.newSAXParser();
MavenSettingsParser handler = new MavenSettingsParser();
saxParser.parse(settingsFile, handler);
return handler.getMavenBundleRepository();
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
public static class MavenSettingsParser extends DefaultHandler {
/*
* Whether we are parsing the local repository tag
*/
private boolean parsingRepositoryPath = false;
/*
* Temporary buffering of path, only used if currently parsing location path
*/
StringBuilder repositoryPath = new StringBuilder();
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
if (qName.equalsIgnoreCase("localRepository")) {
parsingRepositoryPath = true;
}
}
@Override
public void characters(char ch[], int start, int length) throws SAXException {
if (parsingRepositoryPath && ch != null) {
repositoryPath.append(ch, start, length);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
if (parsingRepositoryPath) {
parsingRepositoryPath = false;
}
}
public URI getMavenBundleRepository() throws MalformedURLException {
String repositoryDirectoryName = repositoryPath.toString().trim();
if (repositoryDirectoryName.isEmpty()) {
return null;
}
File repositoryDirectory = new File(repositoryDirectoryName);
File repository = new File(repositoryDirectory, "repository.xml");
if (!repository.exists()) {
return null;
}
return repository.toURI();
}
}
/**
* Get the repository at the default maven location, when there is no specific configuration
*/
private static URI getDefaultBundleRepository() {
File m2Folder = getUserMavenConfiguration();
if (m2Folder == null) {
return null;
}
File repositoryFile = new File(new File(m2Folder, "repository"), "repository.xml");
if (repositoryFile.exists()) {
URI repo = repositoryFile.toURI();
logger.info("Default Linux repository :" + repo);
return repo;
}
return null;
}
/**
* Get the user defined maven configuration directory
*/
private static File getUserMavenConfiguration() {
String user_home = System.getProperty("user.home");
if (user_home == null) {
user_home = System.getProperty("HOME");
if (user_home == null) {
return null;
}
}
File user_home_file = new File(user_home);
File m2Folder = new File(user_home_file, ".m2");
return m2Folder;
}
/**
* Get the global maven settings file
*/
private static File getGlobalMavenSettings() {
String m2_home = System.getenv().get("M2_HOME");
if (m2_home == null) {
return null;
}
File m2_Home_file = new File(m2_home);
File settings = new File(new File(m2_Home_file, "conf"), "settings.xml");
if (settings.exists()) {
return settings;
}
return null;
}
/**
* Get the user defined maven settings file
*/
private static File getUserMavenSettings() {
File m2Folder = getUserMavenConfiguration();
if (m2Folder == null) {
return null;
}
File settings = new File(m2Folder, "settings.xml");
if (settings.exists()) {
return settings;
}
return null;
}
}