| /* |
| * 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.features.management.internal; |
| |
| // import org.apache.karaf.cellar.bundle.BundleState; |
| |
| import org.apache.karaf.cellar.core.*; |
| 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.cellar.features.ClusterFeaturesEvent; |
| import org.apache.karaf.cellar.features.Constants; |
| import org.apache.karaf.cellar.features.FeatureState; |
| import org.apache.karaf.cellar.features.ClusterRepositoryEvent; |
| import org.apache.karaf.cellar.features.management.CellarFeaturesMBean; |
| import org.apache.karaf.features.*; |
| // import org.osgi.framework.BundleEvent; |
| import org.apache.karaf.features.internal.model.Features; |
| import org.apache.karaf.features.internal.model.JaxbUtil; |
| import org.osgi.service.cm.ConfigurationAdmin; |
| |
| import javax.management.NotCompliantMBeanException; |
| import javax.management.StandardMBean; |
| import javax.management.openmbean.*; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.regex.Matcher; |
| import java.util.regex.Pattern; |
| |
| /** |
| * Implementation of the Cellar Features MBean. |
| */ |
| public class CellarFeaturesMBeanImpl extends StandardMBean implements CellarFeaturesMBean { |
| |
| private ClusterManager clusterManager; |
| private GroupManager groupManager; |
| private EventProducer eventProducer; |
| private FeaturesService featuresService; |
| private ConfigurationAdmin configurationAdmin; |
| |
| public CellarFeaturesMBeanImpl() throws NotCompliantMBeanException { |
| super(CellarFeaturesMBean.class); |
| } |
| |
| public ClusterManager getClusterManager() { |
| return this.clusterManager; |
| } |
| |
| public void setClusterManager(ClusterManager clusterManager) { |
| this.clusterManager = clusterManager; |
| } |
| |
| public GroupManager getGroupManager() { |
| return this.groupManager; |
| } |
| |
| public void setGroupManager(GroupManager groupManager) { |
| this.groupManager = groupManager; |
| } |
| |
| public EventProducer getEventProducer() { |
| return eventProducer; |
| } |
| |
| public void setEventProducer(EventProducer eventProducer) { |
| this.eventProducer = eventProducer; |
| } |
| |
| public FeaturesService getFeaturesService() { |
| return featuresService; |
| } |
| |
| public void setFeaturesService(FeaturesService featuresService) { |
| this.featuresService = featuresService; |
| } |
| |
| public ConfigurationAdmin getConfigurationAdmin() { |
| return configurationAdmin; |
| } |
| |
| public void setConfigurationAdmin(ConfigurationAdmin configurationAdmin) { |
| this.configurationAdmin = configurationAdmin; |
| } |
| |
| @Override |
| public void installFeature(String groupName, String name, String version, boolean noClean, boolean noRefresh) throws Exception { |
| this.installFeature(groupName, name, version, noClean, noRefresh, false); |
| } |
| |
| @Override |
| public void installFeature(String groupName, String name, String version, boolean noClean, boolean noRefresh, boolean noStart) throws Exception { |
| // check if the group exists |
| Group group = groupManager.findGroupByName(groupName); |
| if (group == null) { |
| throw new IllegalArgumentException("Cluster group " + groupName + " doesn't exist"); |
| } |
| |
| // check if the producer is ON |
| if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) { |
| throw new IllegalStateException("Cluster event producer is OFF"); |
| } |
| |
| // check if the feature is allowed outbound |
| CellarSupport support = new CellarSupport(); |
| support.setClusterManager(this.clusterManager); |
| support.setGroupManager(this.groupManager); |
| support.setConfigurationAdmin(this.configurationAdmin); |
| if (!support.isAllowed(group, Constants.CATEGORY, name, EventType.OUTBOUND)) { |
| throw new IllegalArgumentException("Feature " + name + " is blocked outbound for cluster group " + groupName); |
| } |
| |
| ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); |
| Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); |
| try { |
| |
| // get the features in the cluster group |
| Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName); |
| |
| // check if the feature exist |
| FeatureState feature = null; |
| String key = null; |
| for (String k : clusterFeatures.keySet()) { |
| FeatureState state = clusterFeatures.get(k); |
| key = k; |
| if (version == null) { |
| if (state.getName().equals(name)) { |
| feature = state; |
| break; |
| } |
| } else { |
| if (state.getName().equals(name) && state.getVersion().equals(version)) { |
| feature = state; |
| break; |
| } |
| } |
| } |
| |
| if (feature == null) { |
| if (version == null) |
| throw new IllegalArgumentException("Feature " + name + " doesn't exist in cluster group " + groupName); |
| else |
| throw new IllegalArgumentException("Feature " + name + "/" + version + " doesn't exist in cluster group " + groupName); |
| } |
| |
| // update the cluster group |
| feature.setInstalled(true); |
| clusterFeatures.put(key, feature); |
| } finally { |
| Thread.currentThread().setContextClassLoader(originalClassLoader); |
| } |
| |
| // broadcast the cluster event |
| ClusterFeaturesEvent event = new ClusterFeaturesEvent(name, version, noClean, noRefresh, noStart, FeatureEvent.EventType.FeatureInstalled); |
| event.setSourceGroup(group); |
| eventProducer.produce(event); |
| } |
| |
| @Override |
| public void installFeature(String groupName, String name, String version) throws Exception { |
| this.installFeature(groupName, name, version, false, false); |
| } |
| |
| @Override |
| public void installFeature(String groupName, String name) throws Exception { |
| this.installFeature(groupName, name, null); |
| } |
| |
| @Override |
| public void installFeature(String groupName, String name, boolean noClean, boolean noRefresh) throws Exception { |
| this.installFeature(groupName, name, null, noClean, noRefresh, false); |
| } |
| |
| @Override |
| public void installFeature(String groupName, String name, boolean noClean, boolean noRefresh, boolean noStart) throws Exception { |
| this.installFeature(groupName, name, null, noClean, noRefresh, noStart); |
| } |
| |
| @Override |
| public void uninstallFeature(String groupName, String name, String version) throws Exception { |
| this.uninstallFeature(groupName, name, version, false); |
| } |
| |
| @Override |
| public void uninstallFeature(String groupName, String name, String version, boolean noRefresh) throws Exception { |
| // check if the group exists |
| Group group = groupManager.findGroupByName(groupName); |
| if (group == null) { |
| throw new IllegalArgumentException("Cluster group " + groupName + " doesn't exist"); |
| } |
| |
| // check if the producer is ON |
| if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) { |
| throw new IllegalStateException("Cluster event producer is OFF"); |
| } |
| |
| // check if the feature is allowed outbound |
| CellarSupport support = new CellarSupport(); |
| support.setClusterManager(this.clusterManager); |
| support.setGroupManager(this.groupManager); |
| support.setConfigurationAdmin(this.configurationAdmin); |
| if (!support.isAllowed(group, Constants.CATEGORY, name, EventType.OUTBOUND)) { |
| throw new IllegalArgumentException("Feature " + name + " is blocked outbound for cluster group " + groupName); |
| } |
| |
| ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); |
| Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); |
| try { |
| |
| // get the features in the cluster group |
| Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName); |
| |
| // check if the feature exist |
| FeatureState feature = null; |
| String key = null; |
| for (String k : clusterFeatures.keySet()) { |
| FeatureState state = clusterFeatures.get(k); |
| key = k; |
| if (version == null) { |
| if (state.getName().equals(name)) { |
| feature = state; |
| break; |
| } |
| } else { |
| if (state.getName().equals(name) && state.getVersion().equals(version)) { |
| feature = state; |
| break; |
| } |
| } |
| } |
| |
| if (feature == null) { |
| if (version == null) |
| throw new IllegalArgumentException("Feature " + name + " doesn't exist in cluster group " + groupName); |
| else |
| throw new IllegalArgumentException("Feature " + name + "/" + version + " doesn't exist in cluster group " + groupName); |
| } |
| |
| // update the cluster group |
| feature.setInstalled(false); |
| clusterFeatures.put(key, feature); |
| } finally { |
| Thread.currentThread().setContextClassLoader(originalClassLoader); |
| } |
| |
| // broadcast the cluster event |
| ClusterFeaturesEvent event = new ClusterFeaturesEvent(name, version, false, noRefresh, false, FeatureEvent.EventType.FeatureUninstalled); |
| event.setSourceGroup(group); |
| eventProducer.produce(event); |
| } |
| |
| @Override |
| public void uninstallFeature(String groupName, String name) throws Exception { |
| this.uninstallFeature(groupName, name, null, false); |
| } |
| |
| @Override |
| public void uninstallFeature(String groupName, String name, boolean noRefresh) throws Exception { |
| this.uninstallFeature(groupName, name, null, noRefresh); |
| } |
| |
| @Override |
| public TabularData getFeatures(String groupName) throws Exception { |
| |
| Group group = groupManager.findGroupByName(groupName); |
| if (group == null) { |
| throw new IllegalArgumentException("Cluster group " + groupName + " doesn't exist"); |
| } |
| |
| CellarSupport support = new CellarSupport(); |
| support.setClusterManager(clusterManager); |
| support.setGroupManager(groupManager); |
| support.setConfigurationAdmin(configurationAdmin); |
| |
| CompositeType featuresType = new CompositeType("Feature", "Karaf Cellar feature", |
| new String[]{"name", "version", "installed", "located", "blocked"}, |
| new String[]{"Name of the feature", "Version of the feature", "Whether the feature is installed or not", |
| "Location of the feature (on the cluster or the local node)", |
| "Feature block policy"}, |
| new OpenType[]{SimpleType.STRING, SimpleType.STRING, SimpleType.BOOLEAN}); |
| |
| TabularType tabularType = new TabularType("Features", "Table of all Karaf Cellar features", |
| featuresType, new String[]{"name", "version"}); |
| TabularData table = new TabularDataSupport(tabularType); |
| |
| ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); |
| Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); |
| try { |
| Map<String, ExtendedFeatureState> features = gatherFeatures(groupName); |
| if (features != null && !features.isEmpty()) { |
| for (ExtendedFeatureState feature : features.values()) { |
| |
| String located = ""; |
| boolean cluster = feature.isCluster(); |
| boolean local = feature.isLocal(); |
| if (cluster && local) |
| located = "cluster/local"; |
| if (cluster && !local) |
| located = "cluster"; |
| if (local && !cluster) |
| located = "local"; |
| |
| String blocked = ""; |
| boolean inbound = support.isAllowed(group, Constants.CATEGORY, feature.getName(), EventType.INBOUND); |
| boolean outbound = support.isAllowed(group, Constants.CATEGORY, feature.getName(), EventType.OUTBOUND); |
| if (!inbound && !outbound) |
| blocked = "in/out"; |
| if (!inbound && outbound) |
| blocked = "in"; |
| if (!outbound && inbound) |
| blocked = "out"; |
| |
| CompositeData data = new CompositeDataSupport(featuresType, |
| new String[]{"name", "version", "installed", "located", "blocked"}, |
| new Object[]{feature.getName(), feature.getVersion(), feature.getInstalled(), located, blocked}); |
| table.put(data); |
| } |
| } |
| } finally { |
| Thread.currentThread().setContextClassLoader(originalClassLoader); |
| } |
| |
| return table; |
| } |
| |
| private Map<String, ExtendedFeatureState> gatherFeatures(String groupName) throws Exception { |
| Map<String, ExtendedFeatureState> features = new HashMap<String, ExtendedFeatureState>(); |
| |
| // get cluster features |
| Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName); |
| for (String key : clusterFeatures.keySet()) { |
| FeatureState state = clusterFeatures.get(key); |
| ExtendedFeatureState extendedState = new ExtendedFeatureState(); |
| extendedState.setName(state.getName()); |
| extendedState.setInstalled(state.getInstalled()); |
| extendedState.setVersion(state.getVersion()); |
| extendedState.setCluster(true); |
| extendedState.setLocal(true); |
| features.put(key, extendedState); |
| } |
| |
| // get local features |
| for (Feature feature : featuresService.listFeatures()) { |
| String key = feature.getName() + "/" + feature.getVersion(); |
| if (features.containsKey(key)) { |
| ExtendedFeatureState extendedState = features.get(key); |
| if (featuresService.isInstalled(feature)) |
| extendedState.setInstalled(true); |
| extendedState.setLocal(true); |
| } else { |
| ExtendedFeatureState extendedState = new ExtendedFeatureState(); |
| extendedState.setCluster(false); |
| extendedState.setLocal(true); |
| extendedState.setName(feature.getName()); |
| extendedState.setVersion(feature.getVersion()); |
| if (featuresService.isInstalled(feature)) |
| extendedState.setInstalled(true); |
| else extendedState.setInstalled(false); |
| features.put(key, extendedState); |
| } |
| } |
| |
| return features; |
| } |
| |
| @Override |
| public List<String> getRepositories(String groupName) throws Exception { |
| // check if the group exists |
| Group group = groupManager.findGroupByName(groupName); |
| if (group == null) { |
| throw new IllegalArgumentException("Cluster group " + groupName + " doesn't exist"); |
| } |
| |
| // get the features repositories in the cluster group |
| Map<String, String> clusterRepositories = clusterManager.getMap(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName); |
| |
| List<String> result = new ArrayList<String>(); |
| for (String clusterRepository : clusterRepositories.keySet()) { |
| result.add(clusterRepository); |
| } |
| |
| return result; |
| } |
| |
| @Override |
| public void addRepository(String groupName, String nameOrUrl) throws Exception { |
| this.addRepository(groupName, nameOrUrl, null, false); |
| } |
| |
| @Override |
| public void addRepository(String groupName, String nameOrUrl, String version) throws Exception { |
| this.addRepository(groupName, nameOrUrl, version, false); |
| } |
| |
| @Override |
| public void addRepository(String groupName, String nameOrUrl, String version, boolean install) throws Exception { |
| // check if the group exists |
| Group group = groupManager.findGroupByName(groupName); |
| if (group == null) { |
| throw new IllegalArgumentException("Cluster group " + groupName + " doesn't exist"); |
| } |
| |
| // check if the event producer is ON |
| if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) { |
| throw new IllegalStateException("Cluster event producer is OFF"); |
| } |
| |
| ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); |
| Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); |
| try { |
| // get the features repositories in the cluster group |
| Map<String, String> clusterRepositories = clusterManager.getMap(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName); |
| // get the features in the cluster group |
| Map<String, FeatureState> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName); |
| |
| URI uri = featuresService.getRepositoryUriFor(nameOrUrl, version); |
| if (uri == null) { |
| uri = new URI(nameOrUrl); |
| } |
| |
| // check if the URL is already registered |
| String name = null; |
| for (String repository : clusterRepositories.keySet()) { |
| if (repository.equals(uri)) { |
| name = clusterRepositories.get(repository); |
| break; |
| } |
| } |
| if (name == null) { |
| // parsing the features repository to get content |
| Features repository = JaxbUtil.unmarshal(uri.toASCIIString(), true); |
| |
| // update the cluster group |
| clusterRepositories.put(uri.toString(), name); |
| |
| // update the features in the cluster group |
| for (Feature feature : repository.getFeature()) { |
| FeatureState state = new FeatureState(); |
| state.setName(feature.getName()); |
| state.setVersion(feature.getVersion()); |
| state.setInstalled(featuresService.isInstalled(feature)); |
| clusterFeatures.put(feature.getName() + "/" + feature.getVersion(), state); |
| } |
| |
| // broadcast the cluster event |
| ClusterRepositoryEvent event = new ClusterRepositoryEvent(uri.toString(), RepositoryEvent.EventType.RepositoryAdded); |
| event.setInstall(install); |
| event.setSourceGroup(group); |
| eventProducer.produce(event); |
| } else { |
| throw new IllegalArgumentException("Features repository URL " + uri + " already registered"); |
| } |
| } finally { |
| Thread.currentThread().setContextClassLoader(originalClassLoader); |
| } |
| } |
| |
| @Override |
| public void removeRepository(String groupName, String repository) throws Exception { |
| this.removeRepository(groupName, repository, false); |
| } |
| |
| @Override |
| public void removeRepository(String groupName, String repo, boolean uninstall) throws Exception { |
| // check if the group exists |
| Group group = groupManager.findGroupByName(groupName); |
| if (group == null) { |
| throw new IllegalArgumentException("Cluster group " + groupName + " doesn't exist"); |
| } |
| |
| // check if the event producer is ON |
| if (eventProducer.getSwitch().getStatus().equals(SwitchStatus.OFF)) { |
| throw new IllegalStateException("Cluster event producer is OFF"); |
| } |
| |
| // get the features repositories in the cluster group |
| Map<String, String> clusterRepositories = clusterManager.getMap(Constants.REPOSITORIES_MAP + Configurations.SEPARATOR + groupName); |
| // get the features in the cluster group |
| Map<FeatureState, Boolean> clusterFeatures = clusterManager.getMap(Constants.FEATURES_MAP + Configurations.SEPARATOR + groupName); |
| |
| ClassLoader originalClassLoader = Thread.currentThread().getContextClassLoader(); |
| try { |
| Thread.currentThread().setContextClassLoader(getClass().getClassLoader()); |
| |
| List<String> urls = new ArrayList<String>(); |
| Pattern pattern = Pattern.compile(repo); |
| for (String repositoryUrl : clusterRepositories.keySet()) { |
| String repositoryName = clusterRepositories.get(repositoryUrl); |
| if (repositoryName != null && !repositoryName.isEmpty()) { |
| // repository has name, try regex on the name |
| Matcher nameMatcher = pattern.matcher(repositoryName); |
| if (nameMatcher.matches()) { |
| urls.add(repositoryUrl); |
| } else { |
| // the name regex doesn't match, fallback to repository URI regex |
| Matcher uriMatcher = pattern.matcher(repositoryUrl); |
| if (uriMatcher.matches()) { |
| urls.add(repositoryUrl); |
| } |
| } |
| } else { |
| // the repository name is not defined, use repository URI regex |
| Matcher uriMatcher = pattern.matcher(repositoryUrl); |
| if (uriMatcher.matches()) { |
| urls.add(repositoryUrl); |
| } |
| } |
| } |
| |
| for (String url : urls) { |
| // looking for the URL in the list |
| boolean found = false; |
| for (String repository : clusterRepositories.keySet()) { |
| if (repository.equals(repository)) { |
| found = true; |
| break; |
| } |
| } |
| if (found) { |
| Features repositoryModel = JaxbUtil.unmarshal(url, true); |
| |
| // update the features repositories in the cluster group |
| clusterRepositories.remove(url); |
| |
| // update the features in the cluster group |
| for (Feature feature : repositoryModel.getFeature()) { |
| clusterFeatures.remove(feature.getName() + "/" + feature.getVersion()); |
| } |
| |
| // broadcast a cluster event |
| ClusterRepositoryEvent event = new ClusterRepositoryEvent(url, RepositoryEvent.EventType.RepositoryRemoved); |
| event.setUninstall(uninstall); |
| event.setSourceGroup(group); |
| eventProducer.produce(event); |
| } else { |
| throw new IllegalArgumentException("Features repository URL " + url + " not found in cluster group " + groupName); |
| } |
| } |
| } finally { |
| Thread.currentThread().setContextClassLoader(originalClassLoader); |
| } |
| } |
| |
| @Override |
| public void block(String groupName, String featurePattern, boolean whitelist, boolean blacklist, boolean in, boolean out) throws Exception { |
| |
| Group group = groupManager.findGroupByName(groupName); |
| if (group == null) { |
| throw new IllegalArgumentException("Cluster group " + groupName + " doesn't exist"); |
| } |
| |
| CellarSupport support = new CellarSupport(); |
| support.setClusterManager(clusterManager); |
| support.setGroupManager(groupManager); |
| support.setConfigurationAdmin(configurationAdmin); |
| |
| if (in) { |
| if (whitelist) |
| support.switchListEntry(Configurations.WHITELIST, groupName, Constants.CATEGORY, EventType.INBOUND, featurePattern); |
| if (blacklist) |
| support.switchListEntry(Configurations.BLACKLIST, groupName, Constants.CATEGORY, EventType.INBOUND, featurePattern); |
| } |
| if (out) { |
| if (whitelist) |
| support.switchListEntry(Configurations.WHITELIST, groupName, Constants.CATEGORY, EventType.OUTBOUND, featurePattern); |
| if (blacklist) |
| support.switchListEntry(Configurations.BLACKLIST, groupName, Constants.CATEGORY, EventType.OUTBOUND, featurePattern); |
| } |
| |
| } |
| |
| class ExtendedFeatureState extends FeatureState { |
| |
| private boolean cluster; |
| private boolean local; |
| |
| public boolean isCluster() { |
| return cluster; |
| } |
| |
| public void setCluster(boolean cluster) { |
| this.cluster = cluster; |
| } |
| |
| public boolean isLocal() { |
| return local; |
| } |
| |
| public void setLocal(boolean local) { |
| this.local = local; |
| } |
| } |
| |
| } |