/* * 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.RegionDigraphPersistence; 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), @ProvideService(RegionDigraphPersistence.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 StandardRegionDigraph dg = digraph = DigraphHelper.loadDigraph(bundleContext); register(ResolverHookFactory.class, dg.getResolverHookFactory()); register(CollisionHook.class, CollisionHookHelper.getCollisionHook(dg)); register(org.osgi.framework.hooks.bundle.FindHook.class, dg.getBundleFindHook()); register(org.osgi.framework.hooks.bundle.EventHook.class, dg.getBundleEventHook()); register(org.osgi.framework.hooks.service.FindHook.class, dg.getServiceFindHook()); register(org.osgi.framework.hooks.service.EventHook.class, dg.getServiceEventHook()); register(RegionDigraph.class, dg); register(RegionDigraphPersistence.class, this::doPersistRegionDigraph); if (getBoolean("digraphMBean", FeaturesService.DEFAULT_DIGRAPH_MBEAN)) { StandardManageableRegionDigraph dgmb = digraphMBean = new StandardManageableRegionDigraph(dg, "org.apache.karaf", bundleContext); dgmb.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 = getStringArray("resourceRepositories", ""); 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, bundleContext.getBundle(0).getBundleContext(), stateStorage, featureFinder, eventAdminListener, configurationAdmin, resolver, dg, 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(); FeaturesServiceMBeanImpl featuresServiceMBean = new FeaturesServiceMBeanImpl(); featuresServiceMBean.setBundleContext(bundleContext); featuresServiceMBean.setFeaturesService(featuresService); registerMBean(featuresServiceMBean, "type=feature"); String[] featuresRepositories = getStringArray("featuresRepositories", ""); String featuresBoot = getString("featuresBoot", ""); boolean featuresBootAsynchronous = getBoolean("featuresBootAsynchronous", false); BootFeaturesInstaller bootFeaturesInstaller = new BootFeaturesInstaller( bundleContext, featuresService, featuresRepositories, featuresBoot, featuresBootAsynchronous); bootFeaturesInstaller.start(); } 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) { doPersistRegionDigraph(); digraph = null; } } private void doPersistRegionDigraph() { if (digraph != null) { try { DigraphHelper.saveDigraph(bundleContext, digraph); } catch (Exception e) { // Ignore } } } }