/*
* 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.sling.discovery.impl.cluster;
import java.util.Dictionary;
import java.util.Hashtable;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.sling.api.SlingConstants;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.discovery.impl.Config;
import org.apache.sling.discovery.impl.DiscoveryServiceImpl;
import org.apache.sling.settings.SlingSettingsService;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Constants;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* osgi event handler which takes note when the established view changes in the
* repository - or when an announcement changed in one of the instances
*/
@Component(immediate = true)
public class ClusterViewChangeListener implements EventHandler {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Reference
private SlingSettingsService slingSettingsService;
@Reference
private ResourceResolverFactory resolverFactory;
@Reference
private DiscoveryServiceImpl discoveryService;
@Reference
private Config config;
/** the sling id of the local instance **/
private String slingId;
private ComponentContext context;
private ServiceRegistration eventHandlerRegistration;
@Activate
protected void activate(final ComponentContext context) {
this.slingId = slingSettingsService.getSlingId();
this.context = context;
if (logger.isDebugEnabled()) {
logger.debug("activated. slingid=" + slingId + ", discoveryservice="
+ discoveryService);
}
registerEventHandler();
}
private void registerEventHandler() {
BundleContext bundleContext = context == null ? null : context.getBundleContext();
if (bundleContext == null) {
logger.info("registerEventHandler: context or bundleContext is null - cannot register");
return;
}
Dictionary<String,Object> properties = new Hashtable<String,Object>();
properties.put(Constants.SERVICE_DESCRIPTION, "Cluster View Change Listener");
String[] topics = new String[] {
SlingConstants.TOPIC_RESOURCE_ADDED,
SlingConstants.TOPIC_RESOURCE_CHANGED,
SlingConstants.TOPIC_RESOURCE_REMOVED };
properties.put(EventConstants.EVENT_TOPIC, topics);
String path = config.getDiscoveryResourcePath();
if (path.endsWith("/")) {
path = path.substring(0, path.length()-1);
}
path = path + "/*";
properties.put(EventConstants.EVENT_FILTER, "(&(path="+path+"))");
eventHandlerRegistration = bundleContext.registerService(
EventHandler.class.getName(), this, properties);
logger.info("registerEventHandler: ClusterViewChangeHandler registered as EventHandler");
}
@Deactivate
protected void deactivate() {
if (eventHandlerRegistration != null) {
eventHandlerRegistration.unregister();
logger.info("deactivate: ClusterViewChangeHandler unregistered as EventHandler");
eventHandlerRegistration = null;
}
logger.info("deactivate: deactivated slingId: {}, this: {}", slingId, this);
}
/**
* Handle osgi events from the repository and take note when
* the established view, properties or announcements change - and
* inform the DiscoveryServiceImpl in those cases.
*/
public void handleEvent(final Event event) {
final String resourcePath = (String) event.getProperty("path");
if (config==null) {
return;
}
final String establishedViewPath = config.getEstablishedViewPath();
final String clusterInstancesPath = config.getClusterInstancesPath();
if (resourcePath == null) {
// not of my business
return;
}
// properties: path, resourceChangedAttributes, resourceType,
// event.topics
if (resourcePath.startsWith(establishedViewPath)) {
if (logger.isDebugEnabled()) {
logger.debug("handleEvent: establishedViewPath resourcePath="
+ resourcePath + ", event=" + event);
}
handleTopologyChanged();
} else if (resourcePath.startsWith(clusterInstancesPath)) {
final Object resourceChangedAttributes = event
.getProperty("resourceChangedAttributes");
if (resourceChangedAttributes != null
&& resourceChangedAttributes instanceof String[]) {
String[] resourceChangedAttributesStrings = (String[]) resourceChangedAttributes;
if (resourceChangedAttributesStrings.length == 1
&& resourceChangedAttributesStrings[0]
.equals("lastHeartbeat")) {
// then ignore this one
return;
}
}
if (logger.isDebugEnabled()) {
logger.debug("handleEvent: clusterInstancesPath (announcement or properties) resourcePath="
+ resourcePath + ", event=" + event);
}
handleTopologyChanged();
} else {
// not of my business
return;
}
}
/** Inform the DiscoveryServiceImpl that the topology (might) have changed **/
private void handleTopologyChanged() {
logger.info("handleTopologyChanged: detected a change in the established views, invoking checkForTopologyChange.");
discoveryService.checkForTopologyChange();
}
}