/*
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2017 - Adobe
* %%
* 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.
* #L%
*/
package com.adobe.acs.commons.replication.packages.automatic.impl;
import java.util.Hashtable;
import javax.management.NotCompliantMBeanException;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Properties;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.SlingConstants;
import org.apache.sling.api.resource.LoginException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.commons.scheduler.Scheduler;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.adobe.acs.commons.util.ResourceServiceManager;
import com.adobe.acs.commons.replication.packages.automatic.AutomaticPackageReplicatorMBean;
import com.adobe.acs.commons.replication.packages.automatic.model.AutomaticPackageReplicatorModel;
import com.adobe.acs.commons.replication.packages.automatic.model.AutomaticPackageReplicatorModel.TRIGGER;
import com.day.cq.replication.Replicator;
/**
* Listens to changes under /etc/acs-commons/automatic-package-replication and
* manages the Automatic Package Replicator jobs based on the updates.
*/
@Component(immediate = true)
@Service(value = { EventHandler.class, AutomaticPackageReplicatorMBean.class })
@Properties({
@Property(name = EventConstants.EVENT_TOPIC, value = { SlingConstants.TOPIC_RESOURCE_ADDED,
SlingConstants.TOPIC_RESOURCE_CHANGED, SlingConstants.TOPIC_RESOURCE_REMOVED }),
@Property(name = "jmx.objectname", value = "com.adobe.acs.commons:type=Automatic Package Replicator"),
@Property(name = EventConstants.EVENT_FILTER, value = "(path=/etc/acs-commons/automatic-package-replication/*/jcr:content)") })
public class ConfigurationUpdateListener extends ResourceServiceManager
implements EventHandler, AutomaticPackageReplicatorMBean {
private static final Logger log = LoggerFactory.getLogger(ConfigurationUpdateListener.class);
private static final String ROOT_PATH = "/etc/acs-commons/automatic-package-replication";
private static final String TRIGGER_KEY = "trigger.name";
/**
* Creating this as a separate method to make migrating to service users
* easier. Callers of this method must ensure the resource resolver is
* closed.
*
* @param factory
* the resource resolver factory to use for getting the resource
* resolver
* @return the resource resolver or null if there is an exception allocating
* the resource resolver
*/
@SuppressWarnings("deprecation")
final static ResourceResolver getResourceResolver(ResourceResolverFactory factory) {
ResourceResolver resolver = null;
try {
resolver = factory.getAdministrativeResourceResolver(null);
} catch (LoginException e) {
log.error("Exception allocating resource resolver", e);
}
return resolver;
}
@Reference
private ResourceResolverFactory resourceResolverFactory;
@Reference
private Scheduler scheduler;
@Reference
private Replicator replicator;
@Reference
private EventAdmin eventAdmin;
public ConfigurationUpdateListener() throws NotCompliantMBeanException {
super(AutomaticPackageReplicatorMBean.class);
}
protected ConfigurationUpdateListener(Class<?> mbeanInterface) throws NotCompliantMBeanException {
super(mbeanInterface);
}
@Override
public void execute(String id) {
AutomaticPackageReplicatorJob job = (AutomaticPackageReplicatorJob) getBundleContext()
.getService(super.getRegisteredServices().get(id).getReference());
job.run();
}
@Override
protected boolean isServiceUpdated(Resource config, ServiceReference reference) {
boolean updated = false;
AutomaticPackageReplicatorModel model = new AutomaticPackageReplicatorModel(config);
String triggerStr = (String) reference.getProperty(TRIGGER_KEY);
if (model.getTrigger() == TRIGGER.cron && model.getTrigger() == TRIGGER.valueOf(triggerStr) && ObjectUtils
.equals(reference.getProperty(Scheduler.PROPERTY_SCHEDULER_EXPRESSION), model.getCronTrigger())) {
updated = true;
} else if (model.getTrigger() == TRIGGER.event && model.getTrigger() == TRIGGER.valueOf(triggerStr)
&& ObjectUtils.equals(reference.getProperty(EventConstants.EVENT_TOPIC), model.getEventTopic())
&& ObjectUtils.equals(reference.getProperty(EventConstants.EVENT_FILTER), model.getEventFilter())) {
updated = true;
}
return updated;
}
@Override
public String getRootPath() {
return ROOT_PATH;
}
@Override
protected ResourceResolver getResourceResolver() {
return getResourceResolver(resourceResolverFactory);
}
@Override
protected ServiceRegistration registerServiceObject(Resource config, Hashtable<String, Object> props) {
AutomaticPackageReplicatorModel model = new AutomaticPackageReplicatorModel(config);
AutomaticPackageReplicatorJob job = new AutomaticPackageReplicatorJob(resourceResolverFactory, replicator,
eventAdmin, model.getPackagePath());
ServiceRegistration serviceRegistration = null;
props.put(TRIGGER_KEY, model.getTrigger().name());
if (AutomaticPackageReplicatorModel.TRIGGER.cron == model.getTrigger()) {
if(StringUtils.isEmpty(model.getCronTrigger())){
throw new IllegalArgumentException("No cron trigger specified");
}
props.put(Scheduler.PROPERTY_SCHEDULER_EXPRESSION, model.getCronTrigger());
log.debug("Registering cron runner with: {}", props);
serviceRegistration = super.getBundleContext().registerService(Runnable.class.getCanonicalName(), job,
props);
} else {
if(StringUtils.isEmpty(model.getEventTopic())){
throw new IllegalArgumentException("No event topic specified");
}
props.put(EventConstants.EVENT_TOPIC, new String[] { model.getEventTopic() });
if (StringUtils.isNotEmpty(model.getEventFilter())) {
props.put(EventConstants.EVENT_FILTER, model.getEventFilter());
}
log.debug("Registering event handler runner with: {}", props);
serviceRegistration = super.getBundleContext().registerService(EventHandler.class.getCanonicalName(), job,
props);
}
return serviceRegistration;
}
}