blob: 0666de9c36f5351ed7c4cb824a2d6d520c3628ec [file] [log] [blame]
/*
* 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.distribution.serialization.impl;
import java.io.InputStream;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.Map;
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
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.Deactivate;
import org.apache.felix.scr.annotations.Property;
import org.apache.felix.scr.annotations.PropertyOption;
import org.apache.felix.scr.annotations.PropertyUnbounded;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.Service;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceResolverFactory;
import org.apache.sling.commons.osgi.PropertiesUtil;
import org.apache.sling.commons.scheduler.Scheduler;
import org.apache.sling.distribution.DistributionRequest;
import org.apache.sling.distribution.common.DistributionException;
import org.apache.sling.distribution.component.impl.DistributionComponentConstants;
import org.apache.sling.distribution.component.impl.SettingsUtils;
import org.apache.sling.distribution.monitor.impl.MonitoringDistributionPackageBuilder;
import org.apache.sling.distribution.packaging.DistributionPackage;
import org.apache.sling.distribution.packaging.DistributionPackageBuilder;
import org.apache.sling.distribution.packaging.DistributionPackageInfo;
import org.apache.sling.distribution.packaging.impl.FileDistributionPackageBuilder;
import org.apache.sling.distribution.packaging.impl.InMemoryDistributionPackageBuilder;
import org.apache.sling.distribution.packaging.impl.ResourceDistributionPackageBuilder;
import org.apache.sling.distribution.packaging.impl.ResourceDistributionPackageCleanup;
import org.apache.sling.distribution.serialization.DistributionContentSerializer;
import org.apache.sling.distribution.util.impl.FileBackedMemoryOutputStream.MemoryUnit;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;
/**
* A factory for package builders
*/
@Component(metatype = true,
label = "Apache Sling Distribution Packaging - Package Builder Factory",
description = "OSGi configuration for package builders",
configurationFactory = true,
specVersion = "1.1",
policy = ConfigurationPolicy.REQUIRE
)
@Service(DistributionPackageBuilder.class)
@Property(name = "webconsole.configurationFactory.nameHint", value = "Builder name: {name}")
public class DistributionPackageBuilderFactory implements DistributionPackageBuilder {
/**
* name of this package builder.
*/
@Property(label = "Name", description = "The name of the package builder.")
private static final String NAME = DistributionComponentConstants.PN_NAME;
/**
* type of this package builder.
*/
@Property(options = {
@PropertyOption(name = "resource",
value = "resource packages"
),
@PropertyOption(name = "file",
value = "file packages"
),
@PropertyOption(name = "inmemory",
value = "in memory packages"
)},
value = "resource", label = "type", description = "The persistence type used by this package builder")
private static final String PERSISTENCE = DistributionComponentConstants.PN_TYPE;
@Property(name = "format.target", label = "Content Serializer", description = "The target reference for the DistributionSerializationFormat used to (de)serialize packages, " +
"e.g. use target=(name=...) to bind to services by name.", value = SettingsUtils.COMPONENT_NAME_DEFAULT)
@Reference(name = "format")
private DistributionContentSerializer contentSerializer;
@Reference
private ResourceResolverFactory resolverFactory;
/**
* Temp file folder
*/
@Property(label = "Temp Filesystem Folder", description = "The filesystem folder where the temporary files should be saved.")
private static final String TEMP_FS_FOLDER = "tempFsFolder";
// 1M
private static final int DEFAULT_FILE_THRESHOLD_VALUE = 1;
@Property(
label="File threshold",
description = "Once the data reaches the configurable size value, buffering to memory switches to file buffering.",
intValue = DEFAULT_FILE_THRESHOLD_VALUE
)
public static final String FILE_THRESHOLD = "fileThreshold";
private static final String DEFAULT_MEMORY_UNIT = "MEGA_BYTES";
@Property(
label = "The memory unit for the file threshold",
description = "The memory unit for the file threshold, Megabytes by default",
value = DEFAULT_MEMORY_UNIT,
options = {
@PropertyOption(name = "BYTES", value = "Bytes"),
@PropertyOption(name = "KILO_BYTES", value = "Kilobytes"),
@PropertyOption(name = "MEGA_BYTES", value = "Megabytes"),
@PropertyOption(name = "GIGA_BYTES", value = "Gigabytes")
}
)
private static final String MEMORY_UNIT = "memoryUnit";
private static final boolean DEFAULT_USE_OFF_HEAP_MEMORY = false;
@Property(
label="Flag to enable/disable the off-heap memory",
description = "Flag to enable/disable the off-heap memory, false by default",
boolValue = DEFAULT_USE_OFF_HEAP_MEMORY
)
public static final String USE_OFF_HEAP_MEMORY = "useOffHeapMemory";
private static final String DEFAULT_DIGEST_ALGORITHM = "NONE";
@Property(
label = "The digest algorithm to calculate the package checksum",
description = "The digest algorithm to calculate the package checksum, Megabytes by default",
value = DEFAULT_DIGEST_ALGORITHM,
options = {
@PropertyOption(name = DEFAULT_DIGEST_ALGORITHM, value = "Do not send digest"),
@PropertyOption(name = "MD2", value = "md2"),
@PropertyOption(name = "MD5", value = "md5"),
@PropertyOption(name = "SHA-1", value = "sha1"),
@PropertyOption(name = "SHA-256", value = "sha256"),
@PropertyOption(name = "SHA-384", value = "sha384"),
@PropertyOption(name = "SHA-512", value = "sha512")
}
)
private static final String DIGEST_ALGORITHM = "digestAlgorithm";
private static final int DEFAULT_MONITORING_QUEUE_SIZE = 0;
@Property(
label="The number of items for monitoring distribution packages creation/installation",
description = "The number of items for monitoring distribution packages creation/installation, 100 by default",
intValue = DEFAULT_MONITORING_QUEUE_SIZE
)
private static final String MONITORING_QUEUE_SIZE = "monitoringQueueSize";
private static final long DEFAULT_PACKAGE_CLEANUP_DELAY = 60L;
@Property(
label="The delay in seconds between two runs of the cleanup phase for resource persisted packages.",
description = "The resource persisted packages are cleaned up periodically (asynchronously) since SLING-6503." +
"The delay between two runs of the cleanup phase can be configured with this setting. 60 seconds by default",
longValue = DEFAULT_PACKAGE_CLEANUP_DELAY
)
private static final String PACKAGE_CLEANUP_DELAY = "cleanupDelay";
/**
* Package node filters
*/
@Property(label = "Package Node Filters", description = "The package node path filters. Filter format: path|+include|-exclude", cardinality = 100)
private static final String PACKAGE_FILTERS = "package.filters";
/**
* Package property filters
*/
@Property(label = "Package Property Filters", description = "The package property path filters. Filter format: path|+include|-exclude",
unbounded = PropertyUnbounded.ARRAY, value = {})
private static final String PROPERTY_FILTERS = "property.filters";
private MonitoringDistributionPackageBuilder packageBuilder;
private ServiceRegistration packageCleanup = null;
@Activate
public void activate(BundleContext context,
Map<String, Object> config) {
String[] nodeFilters = SettingsUtils.removeEmptyEntries(PropertiesUtil.toStringArray(config.get(PACKAGE_FILTERS), null));
String[] propertyFilters = SettingsUtils.removeEmptyEntries(PropertiesUtil.toStringArray(config.get(PROPERTY_FILTERS), null));
String persistenceType = PropertiesUtil.toString(config.get(PERSISTENCE), null);
String tempFsFolder = SettingsUtils.removeEmptyEntry(PropertiesUtil.toString(config.get(TEMP_FS_FOLDER), null));
String digestAlgorithm = PropertiesUtil.toString(config.get(DIGEST_ALGORITHM), DEFAULT_DIGEST_ALGORITHM);
long cleanupDelay = PropertiesUtil.toLong(config.get(PACKAGE_CLEANUP_DELAY), DEFAULT_PACKAGE_CLEANUP_DELAY);
if (DEFAULT_DIGEST_ALGORITHM.equals(digestAlgorithm)) {
digestAlgorithm = null;
}
DistributionPackageBuilder wrapped;
if ("file".equals(persistenceType)) {
wrapped = new FileDistributionPackageBuilder(contentSerializer.getName(), contentSerializer, tempFsFolder, digestAlgorithm, nodeFilters, propertyFilters);
} else if ("inmemory".equals(persistenceType)) {
wrapped = new InMemoryDistributionPackageBuilder(contentSerializer.getName(), contentSerializer, nodeFilters, propertyFilters);
} else {
final int fileThreshold = PropertiesUtil.toInteger(config.get(FILE_THRESHOLD), DEFAULT_FILE_THRESHOLD_VALUE);
String memoryUnitName = PropertiesUtil.toString(config.get(MEMORY_UNIT), DEFAULT_MEMORY_UNIT);
final MemoryUnit memoryUnit = MemoryUnit.valueOf(memoryUnitName);
final boolean useOffHeapMemory = PropertiesUtil.toBoolean(config.get(USE_OFF_HEAP_MEMORY), DEFAULT_USE_OFF_HEAP_MEMORY);
ResourceDistributionPackageBuilder resourceDistributionPackageBuilder = new ResourceDistributionPackageBuilder(contentSerializer.getName(), contentSerializer, tempFsFolder, fileThreshold, memoryUnit, useOffHeapMemory, digestAlgorithm, nodeFilters, propertyFilters);
Runnable cleanup = new ResourceDistributionPackageCleanup(resolverFactory, resourceDistributionPackageBuilder);
Dictionary<String, Object> props = new Hashtable<String, Object>();
props.put(Scheduler.PROPERTY_SCHEDULER_CONCURRENT, false);
props.put(Scheduler.PROPERTY_SCHEDULER_PERIOD, cleanupDelay);
packageCleanup = context.registerService(Runnable.class.getName(), cleanup, props);
wrapped = resourceDistributionPackageBuilder;
}
int monitoringQueueSize = PropertiesUtil.toInteger(config.get(MONITORING_QUEUE_SIZE), DEFAULT_MONITORING_QUEUE_SIZE);
packageBuilder = new MonitoringDistributionPackageBuilder(monitoringQueueSize, wrapped, context);
}
@Deactivate
public void deactivate() {
packageBuilder.clear();
if (packageCleanup != null) {
packageCleanup.unregister();
}
}
public String getType() {
return packageBuilder.getType();
}
@Nonnull
public DistributionPackage createPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull DistributionRequest request) throws DistributionException {
return packageBuilder.createPackage(resourceResolver, request);
}
@Nonnull
public DistributionPackage readPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull InputStream stream) throws DistributionException {
return packageBuilder.readPackage(resourceResolver, stream);
}
@CheckForNull
public DistributionPackage getPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull String id) throws DistributionException {
return packageBuilder.getPackage(resourceResolver, id);
}
public boolean installPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull DistributionPackage distributionPackage) throws DistributionException {
return packageBuilder.installPackage(resourceResolver, distributionPackage);
}
@Nonnull
@Override
public DistributionPackageInfo installPackage(@Nonnull ResourceResolver resourceResolver, @Nonnull InputStream stream) throws DistributionException {
return packageBuilder.installPackage(resourceResolver, stream);
}
}