/*
* #%L
* ACS AEM Commons Bundle
* %%
* Copyright (C) 2015 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.oak.impl;
import com.adobe.acs.commons.analysis.jcrchecksum.ChecksumGenerator;
import com.adobe.acs.commons.util.AemCapabilityHelper;
import org.apache.commons.lang.StringUtils;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.ConfigurationPolicy;
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.resource.ResourceResolverFactory;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.commons.scheduler.ScheduleOptions;
import org.apache.sling.commons.scheduler.Scheduler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.jcr.RepositoryException;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
//@formatter:off
@Component(
label = "ACS AEM Commons - Ensure Oak Index",
description = "Component Factory to manage Oak indexes.",
configurationFactory = true,
policy = ConfigurationPolicy.REQUIRE,
metatype = true
)
@Properties({
@Property(
name = "webconsole.configurationFactory.nameHint",
value = "Definitions: {ensure-definitions.path}, Indexes: {oak-indexes.path}",
propertyPrivate = true
)
})
@Service
//@formatter:on
public class EnsureOakIndex implements AppliableEnsureOakIndex {
static final Logger log = LoggerFactory.getLogger(EnsureOakIndex.class);
//@formatter:off
private static final String DEFAULT_ENSURE_DEFINITIONS_PATH = StringUtils.EMPTY;
@Property(label = "Ensure Definitions Path",
description = "The absolute path to the resource containing the "
+ "ACS AEM Commons ensure definitions",
value = DEFAULT_ENSURE_DEFINITIONS_PATH)
public static final String PROP_ENSURE_DEFINITIONS_PATH = "ensure-definitions.path";
private static final String DEFAULT_OAK_INDEXES_PATH = "/oak:index";
@Property(label = "Oak Indexes Path",
description = "The absolute path to the oak:index to update; Defaults to [ /oak:index ]",
value = DEFAULT_OAK_INDEXES_PATH)
public static final String PROP_OAK_INDEXES_PATH = "oak-indexes.path";
private static final boolean DEFAULT_IMMEDIATE = true;
@Property(
label = "Immediate",
description = "Apply the indexes on startup of service. Defaults to [ true ]",
boolValue = DEFAULT_IMMEDIATE
)
public static final String PROP_IMMEDIATE = "immediate";
@Reference
private AemCapabilityHelper capabilityHelper;
@Reference
private ChecksumGenerator checksumGenerator;
@Reference
private ResourceResolverFactory resourceResolverFactory;
@Reference
private Scheduler scheduler;
private String ensureDefinitionsPath;
private String oakIndexesPath;
private boolean immediate = DEFAULT_IMMEDIATE;
private boolean applied = false;
private CopyOnWriteArrayList<String> ignoreProperties = new CopyOnWriteArrayList<String>();
//@formatter:on
@Activate
protected final void activate(Map<String, Object> config) throws RepositoryException {
if (!capabilityHelper.isOak()) {
log.info("Cowardly refusing to create indexes on non-Oak instance.");
return;
}
ensureDefinitionsPath = PropertiesUtil.toString(config.get(PROP_ENSURE_DEFINITIONS_PATH),
DEFAULT_ENSURE_DEFINITIONS_PATH);
oakIndexesPath = PropertiesUtil.toString(config.get(PROP_OAK_INDEXES_PATH),
DEFAULT_OAK_INDEXES_PATH);
if (StringUtils.isBlank(ensureDefinitionsPath)) {
throw new IllegalArgumentException("OSGi Configuration Property `"
+ PROP_ENSURE_DEFINITIONS_PATH + "` " + "cannot be blank.");
} else if (StringUtils.isBlank(oakIndexesPath)) {
throw new IllegalArgumentException("OSGi Configuration Property `"
+ PROP_OAK_INDEXES_PATH + "` " + "cannot be blank.");
}
this.immediate = PropertiesUtil.toBoolean(config.get(PROP_IMMEDIATE), DEFAULT_IMMEDIATE);
if (this.immediate) {
apply(false);
}
}
/**
* {@inheritDoc}
**/
@Override
public final void apply(boolean force) {
if (!force && this.applied) {
return;
}
log.info("Ensuring Oak Indexes [ {} ~> {} ]", ensureDefinitionsPath, oakIndexesPath);
// Start the indexing process asynchronously, so the activate won't get blocked
// by rebuilding a synchronous index
EnsureOakIndexJobHandler jobHandler =
new EnsureOakIndexJobHandler(this, oakIndexesPath, ensureDefinitionsPath);
ScheduleOptions options = scheduler.NOW();
options.name(toString());
options.canRunConcurrently(false);
scheduler.schedule(jobHandler, options);
applied = true;
log.info("Job scheduled for ensuring Oak Indexes [ {} ~> {} ]", ensureDefinitionsPath, oakIndexesPath);
}
@Override
public final boolean isApplied() {
return this.applied;
}
@Override
public boolean isImmediate() {
return this.immediate;
}
@Override
public List<String> getIgnoreProperties() {
return this.ignoreProperties;
}
@Override
public void setIgnoreProperties(String[] ignoreProperties) {
this.ignoreProperties = new CopyOnWriteArrayList<String>(ignoreProperties);
}
@Override
public final String getEnsureDefinitionsPath() {
return StringUtils.trim(this.ensureDefinitionsPath);
}
@Override
public String getOakIndexesPath() {
return StringUtils.trim(this.oakIndexesPath);
}
public final String toString() {
return String.format("EnsureOakIndex( %s => %s )",
new Object[]{ensureDefinitionsPath, oakIndexesPath});
}
ChecksumGenerator getChecksumGenerator() {
return checksumGenerator;
}
final ResourceResolverFactory getResourceResolverFactory() {
return resourceResolverFactory;
}
static class OakIndexDefinitionException extends Exception {
OakIndexDefinitionException(String message) {
super(message);
}
}
}