blob: ea8a1c4e7a54e38fe964113bba01901ef5219d76 [file] [log] [blame]
/*
* 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.apache.karaf.cellar.bundle;
import org.apache.karaf.cellar.core.Configurations;
import org.apache.karaf.cellar.core.Group;
import org.apache.karaf.cellar.core.control.SwitchStatus;
import org.apache.karaf.cellar.core.event.EventProducer;
import org.apache.karaf.cellar.core.event.EventType;
import org.apache.karaf.features.Feature;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.SynchronousBundleListener;
import org.osgi.framework.startlevel.BundleStartLevel;
import org.osgi.service.cm.Configuration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Dictionary;
import java.util.List;
import java.util.Map;
import java.util.Set;
/**
* LocalBundleListener is listening for local bundles changes.
* When a local bundle change occurs, this listener updates the cluster and broadcasts a cluster bundle event.
*/
public class LocalBundleListener extends BundleSupport implements SynchronousBundleListener {
private static final transient Logger LOGGER = LoggerFactory.getLogger(LocalBundleListener.class);
private EventProducer eventProducer;
/**
* Callback method called when a local bundle status change.
*
* @param event the local bundle event.
*/
@Override
public void bundleChanged(BundleEvent event) {
if (event.getBundle().getBundleId() == 0 && (event.getType() == BundleEvent.STOPPING || event.getType() == BundleEvent.STOPPED)) {
LOGGER.debug("CELLAR BUNDLE: Karaf shutdown detected, removing Cellar LocalBundleListener");
bundleContext.removeBundleListener(this);
return;
}
if (!isEnabled()) {
LOGGER.trace("CELLAR BUNDLE: local listener is disabled");
return;
}
if (event.getBundle().getBundleId() == 0) {
return;
}
// check if the producer is ON
if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) {
LOGGER.debug("CELLAR BUNDLE: cluster event producer is OFF");
return;
}
if (event != null && event.getBundle() != null) {
Set<Group> groups = null;
try {
groups = groupManager.listLocalGroups();
} catch (Exception ex) {
LOGGER.warn("Failed to list local groups. Is Cellar uninstalling ?");
}
if (groups != null && !groups.isEmpty()) {
for (Group group : groups) {
// get the bundle name or location.
String name = (String) event.getBundle().getHeaders().get(org.osgi.framework.Constants.BUNDLE_NAME);
// if there is no name, then default to symbolic name.
name = (name == null) ? event.getBundle().getSymbolicName() : name;
// if there is no symbolic name, resort to location.
name = (name == null) ? event.getBundle().getLocation() : name;
String symbolicName = event.getBundle().getSymbolicName();
String version = event.getBundle().getHeaders().get(org.osgi.framework.Constants.BUNDLE_VERSION);
String bundleLocation = event.getBundle().getLocation();
int status = event.getBundle().getState();
int startLevel = event.getBundle().adapt(BundleStartLevel.class).getStartLevel();
if (isAllowed(group, Constants.CATEGORY, bundleLocation, EventType.OUTBOUND)) {
ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(getClass().getClassLoader());
try {
// update bundles in the cluster group
Map<String, BundleState> clusterBundles = clusterManager.getMap(Constants.BUNDLE_MAP + Configurations.SEPARATOR + group.getName());
if (event.getType() == BundleEvent.UNINSTALLED) {
clusterBundles.remove(symbolicName + "/" + version);
} else {
BundleState state = clusterBundles.get(symbolicName + "/" + version);
if (state == null) {
state = new BundleState();
}
state.setId(event.getBundle().getBundleId());
state.setName(name);
state.setVersion(version);
state.setSymbolicName(symbolicName);
state.setStatus(status);
state.setStartLevel(startLevel);
state.setLocation(bundleLocation);
clusterBundles.put(symbolicName + "/" + version, state);
}
// check the features first
List<Feature> matchingFeatures = retrieveFeature(bundleLocation);
for (Feature feature : matchingFeatures) {
if (!isAllowed(group, "feature", feature.getName(), EventType.OUTBOUND)) {
LOGGER.trace("CELLAR BUNDLE: bundle {} is contained in feature {} marked BLOCKED OUTBOUND for cluster group {}", bundleLocation, feature.getName(), group.getName());
return;
}
}
// broadcast the cluster event
ClusterBundleEvent clusterBundleEvent = new ClusterBundleEvent(symbolicName, version, bundleLocation, startLevel, status);
clusterBundleEvent.setSourceGroup(group);
clusterBundleEvent.setSourceNode(clusterManager.getNode());
clusterBundleEvent.setLocal(clusterManager.getNode());
eventProducer.produce(clusterBundleEvent);
} catch (Exception e) {
LOGGER.error("CELLAR BUNDLE: failed to create bundle event", e);
} finally {
Thread.currentThread().setContextClassLoader(originalClassLoader);
}
} else LOGGER.trace("CELLAR BUNDLE: bundle {} is marked BLOCKED OUTBOUND for cluster group {}", bundleLocation, group.getName());
}
}
}
}
/**
* Check if the local bundle listener is enabled in the etc/org.apache.karaf.cellar.groups.cfg.
*
* @return true if enabled, false else.
*/
private boolean isEnabled() {
try {
Configuration configuration = configurationAdmin.getConfiguration(Configurations.NODE, null);
Dictionary<String, Object> properties = configuration.getProperties();
if (properties != null) {
String value = properties.get(Constants.CATEGORY + Configurations.SEPARATOR + Configurations.LISTENER).toString();
return Boolean.parseBoolean(value);
}
} catch (Exception e) {
LOGGER.warn("CELLAR BUNDLE: can't check listener configuration", e);
}
return false;
}
public void init() {
getBundleContext().addBundleListener(this);
}
public void destroy() {
bundleContext.removeBundleListener(this);
}
public EventProducer getEventProducer() {
return eventProducer;
}
public void setEventProducer(EventProducer eventProducer) {
this.eventProducer = eventProducer;
}
}