blob: d18e88108fa3c6294ea48550a1c138d1113ec10d [file] [log] [blame]
/*
* 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.karaf.features.internal.osgi;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import org.apache.felix.resolver.ResolverImpl;
import org.apache.felix.utils.properties.Properties;
import org.apache.karaf.features.FeaturesListener;
import org.apache.karaf.features.FeaturesService;
import org.apache.karaf.features.internal.management.FeaturesServiceMBeanImpl;
import org.apache.karaf.features.internal.region.DigraphHelper;
import org.apache.karaf.features.internal.repository.AggregateRepository;
import org.apache.karaf.features.internal.repository.JsonRepository;
import org.apache.karaf.features.internal.repository.XmlRepository;
import org.apache.karaf.features.internal.resolver.Slf4jResolverLog;
import org.apache.karaf.features.internal.service.BootFeaturesInstaller;
import org.apache.karaf.features.internal.service.EventAdminListener;
import org.apache.karaf.features.internal.service.FeatureFinder;
import org.apache.karaf.features.internal.service.FeaturesServiceImpl;
import org.apache.karaf.features.internal.service.StateStorage;
import org.apache.karaf.util.tracker.BaseActivator;
import org.apache.karaf.util.tracker.annotation.ProvideService;
import org.apache.karaf.util.tracker.annotation.RequireService;
import org.apache.karaf.util.tracker.annotation.Services;
import org.eclipse.equinox.internal.region.CollisionHookHelper;
import org.eclipse.equinox.internal.region.StandardRegionDigraph;
import org.eclipse.equinox.internal.region.management.StandardManageableRegionDigraph;
import org.eclipse.equinox.region.RegionDigraph;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.hooks.bundle.CollisionHook;
import org.osgi.framework.hooks.resolver.ResolverHookFactory;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.repository.Repository;
import org.osgi.service.resolver.Resolver;
import org.osgi.service.url.URLStreamHandlerService;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;
import org.slf4j.LoggerFactory;
@Services(
requires = {
@RequireService(ConfigurationAdmin.class),
@RequireService(value = URLStreamHandlerService.class, filter = "(url.handler.protocol=mvn)")
},
provides = {
@ProvideService(FeaturesService.class),
@ProvideService(RegionDigraph.class)
}
)
public class Activator extends BaseActivator {
public static final String FEATURES_REPOS_PID = "org.apache.karaf.features.repos";
public static final String FEATURES_SERVICE_CONFIG_FILE = "org.apache.karaf.features.cfg";
private static final String STATE_FILE = "state.json";
private ServiceTracker<FeaturesListener, FeaturesListener> featuresListenerTracker;
private FeaturesServiceImpl featuresService;
private StandardRegionDigraph digraph;
private StandardManageableRegionDigraph digraphMBean;
public Activator() {
// Special case here, as we don't want the activator to wait for current job to finish,
// else it would forbid the features service to refresh itself
setSchedulerStopTimeout(0);
}
@Override
protected void doOpen() throws Exception {
super.doOpen();
Properties configuration = new Properties();
File configFile = new File(System.getProperty("karaf.etc"), FEATURES_SERVICE_CONFIG_FILE);
if (configFile.isFile() && configFile.canRead()) {
try {
configuration.load(new FileReader(configFile));
} catch (IOException e) {
logger.warn("Error reading configuration file " + configFile.toString(), e);
}
}
Dictionary<String, String> props = new Hashtable<>();
for (Map.Entry<String, String> entry : configuration.entrySet()) {
props.put(entry.getKey(), entry.getValue());
}
updated(props);
}
protected void doStart() throws Exception {
ConfigurationAdmin configurationAdmin = getTrackedService(ConfigurationAdmin.class);
Resolver resolver = new ResolverImpl(new Slf4jResolverLog(LoggerFactory.getLogger(ResolverImpl.class)));
URLStreamHandlerService mvnUrlHandler = getTrackedService(URLStreamHandlerService.class);
if (configurationAdmin == null || mvnUrlHandler == null) {
return;
}
// RegionDigraph
digraph = DigraphHelper.loadDigraph(bundleContext);
register(ResolverHookFactory.class, digraph.getResolverHookFactory());
register(CollisionHook.class, CollisionHookHelper.getCollisionHook(digraph));
register(org.osgi.framework.hooks.bundle.FindHook.class, digraph.getBundleFindHook());
register(org.osgi.framework.hooks.bundle.EventHook.class, digraph.getBundleEventHook());
register(org.osgi.framework.hooks.service.FindHook.class, digraph.getServiceFindHook());
register(org.osgi.framework.hooks.service.EventHook.class, digraph.getServiceEventHook());
register(RegionDigraph.class, digraph);
digraphMBean = new StandardManageableRegionDigraph(digraph, "org.apache.karaf", bundleContext);
digraphMBean.registerMBean();
FeatureFinder featureFinder = new FeatureFinder();
Hashtable<String, Object> props = new Hashtable<>();
props.put(Constants.SERVICE_PID, FEATURES_REPOS_PID);
register(ManagedService.class, featureFinder, props);
List<Repository> repositories = new ArrayList<>();
String[] resourceRepositories = getString("resourceRepositories", "").split(",");
long repositoryExpiration = getLong("repositoryExpiration", FeaturesService.DEFAULT_REPOSITORY_EXPIRATION);
boolean repositoryIgnoreFailures = getBoolean("repositoryIgnoreFailures", true);
for (String url : resourceRepositories) {
url = url.trim();
if (!url.isEmpty()) {
if (url.startsWith("json:")) {
repositories.add(new JsonRepository(url.substring("json:".length()), repositoryExpiration, repositoryIgnoreFailures));
} else if (url.startsWith("xml:")) {
repositories.add(new XmlRepository(url.substring("xml:".length()), repositoryExpiration, repositoryIgnoreFailures));
} else {
logger.warn("Unrecognized resource repository: " + url);
}
}
}
Repository globalRepository;
switch (repositories.size()) {
case 0:
globalRepository = null;
break;
case 1:
globalRepository = repositories.get(0);
break;
default:
globalRepository = new AggregateRepository(repositories);
break;
}
String overrides = getString("overrides", new File(System.getProperty("karaf.etc"), "overrides.properties").toURI().toString());
String featureResolutionRange = getString("featureResolutionRange", FeaturesService.DEFAULT_FEATURE_RESOLUTION_RANGE);
String bundleUpdateRange = getString("bundleUpdateRange", FeaturesService.DEFAULT_BUNDLE_UPDATE_RANGE);
String updateSnapshots = getString("updateSnapshots", FeaturesService.DEFAULT_UPDATE_SNAPSHOTS);
int downloadThreads = getInt("downloadThreads", FeaturesService.DEFAULT_DOWNLOAD_THREADS);
long scheduleDelay = getLong("scheduleDelay", FeaturesService.DEFAULT_SCHEDULE_DELAY);
int scheduleMaxRun = getInt("scheduleMaxRun", FeaturesService.DEFAULT_SCHEDULE_MAX_RUN);
String blacklisted = getString("blacklisted", new File(System.getProperty("karaf.etc"), "blacklisted.properties").toURI().toString());
String serviceRequirements = getString("serviceRequirements", FeaturesService.SERVICE_REQUIREMENTS_DEFAULT);
boolean configCfgStore = getBoolean("configCfgStore", FeaturesService.DEFAULT_CONFIG_CFG_STORE);
StateStorage stateStorage = new StateStorage() {
@Override
protected InputStream getInputStream() throws IOException {
File file = bundleContext.getDataFile(STATE_FILE);
if (file.exists()) {
return new FileInputStream(file);
} else {
return null;
}
}
@Override
protected OutputStream getOutputStream() throws IOException {
File file = bundleContext.getDataFile(STATE_FILE);
return new FileOutputStream(file);
}
};
EventAdminListener eventAdminListener;
try {
eventAdminListener = new EventAdminListener(bundleContext);
} catch (Throwable t) {
eventAdminListener = null;
}
featuresService = new FeaturesServiceImpl(
bundleContext.getBundle(),
bundleContext.getBundle(0).getBundleContext(),
stateStorage,
featureFinder,
eventAdminListener,
configurationAdmin,
resolver,
digraph,
overrides,
featureResolutionRange,
bundleUpdateRange,
updateSnapshots,
serviceRequirements,
globalRepository,
downloadThreads,
scheduleDelay,
scheduleMaxRun,
blacklisted,
configCfgStore);
register(FeaturesService.class, featuresService);
featuresListenerTracker = new ServiceTracker<>(
bundleContext,
FeaturesListener.class,
new ServiceTrackerCustomizer<FeaturesListener, FeaturesListener>() {
@Override
public FeaturesListener addingService(ServiceReference<FeaturesListener> reference) {
FeaturesListener service = bundleContext.getService(reference);
featuresService.registerListener(service);
return service;
}
@Override
public void modifiedService(ServiceReference<FeaturesListener> reference, FeaturesListener service) {
}
@Override
public void removedService(ServiceReference<FeaturesListener> reference, FeaturesListener service) {
featuresService.unregisterListener(service);
bundleContext.ungetService(reference);
}
}
);
featuresListenerTracker.open();
String featuresRepositories = getString("featuresRepositories", "");
String featuresBoot = getString("featuresBoot", "");
boolean featuresBootAsynchronous = getBoolean("featuresBootAsynchronous", false);
BootFeaturesInstaller bootFeaturesInstaller = new BootFeaturesInstaller(
bundleContext, featuresService,
featuresRepositories, featuresBoot, featuresBootAsynchronous);
bootFeaturesInstaller.start();
FeaturesServiceMBeanImpl featuresServiceMBean = new FeaturesServiceMBeanImpl();
featuresServiceMBean.setBundleContext(bundleContext);
featuresServiceMBean.setFeaturesService(featuresService);
registerMBean(featuresServiceMBean, "type=feature");
}
protected void doStop() {
if (digraphMBean != null) {
digraphMBean.unregisterMbean();
digraphMBean = null;
}
if (featuresListenerTracker != null) {
featuresListenerTracker.close();
featuresListenerTracker = null;
}
super.doStop();
if (featuresService != null) {
featuresService.stop();
featuresService = null;
}
if (digraph != null) {
try {
DigraphHelper.saveDigraph(bundleContext, digraph);
} catch (Exception e) {
// Ignore
}
digraph = null;
}
}
}