blob: c4d6c925bb7b73d1ab38cf23bf34152d916a1005 [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.testing.mock.caconfig.persistence;
import java.util.HashSet;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
import org.apache.jackrabbit.JcrConstants;
import org.apache.sling.api.resource.ModifiableValueMap;
import org.apache.sling.api.resource.PersistenceException;
import org.apache.sling.api.resource.Resource;
import org.apache.sling.api.resource.ResourceResolver;
import org.apache.sling.api.resource.ResourceUtil;
import org.apache.sling.api.resource.ValueMap;
import org.apache.sling.caconfig.spi.ConfigurationCollectionPersistData;
import org.apache.sling.caconfig.spi.ConfigurationPersistData;
import org.apache.sling.caconfig.spi.ConfigurationPersistenceException;
import org.apache.sling.caconfig.spi.ConfigurationPersistenceStrategy2;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import static org.junit.Assert.assertNotNull;
/**
* This is a variant of {@link org.apache.sling.caconfig.impl.def.DefaultConfigurationPersistenceStrategy}
* which reads and stores data from a sub-resources named "jcr:content".
*
* Difference to {@link CustomConfigurationPersistenceStrategy}:
* - For configuration collections jcr:content is added only for the parent, not for each item
* - For nested configuration jcr:content is not duplicated in the path
*/
public class CustomConfigurationPersistenceStrategy2 implements ConfigurationPersistenceStrategy2 {
private static final String DEFAULT_RESOURCE_TYPE = JcrConstants.NT_UNSTRUCTURED;
private static final String CHILD_NODE_NAME = JcrConstants.JCR_CONTENT;
private static final Pattern JCR_CONTENT_PATTERN =
Pattern.compile("(.*/)?" + Pattern.quote(CHILD_NODE_NAME) + "(/.*)?");
@Override
public Resource getResource(@NotNull Resource resource) {
assertNotNull(resource);
if (containsJcrContent(resource.getPath())) {
return resource;
} else {
return resource.getChild(CHILD_NODE_NAME);
}
}
@Override
public Resource getCollectionParentResource(@NotNull Resource resource) {
assertNotNull(resource);
if (containsJcrContent(resource.getPath())) {
return resource;
} else {
return resource.getChild(CHILD_NODE_NAME);
}
}
@Override
public Resource getCollectionItemResource(@NotNull Resource resource) {
assertNotNull(resource);
return resource;
}
@Override
public String getResourcePath(@NotNull String resourcePath) {
assertNotNull(resourcePath);
if (containsJcrContent(resourcePath)) {
return resourcePath;
} else {
return resourcePath + "/" + CHILD_NODE_NAME;
}
}
@Override
public String getCollectionParentResourcePath(@NotNull String resourcePath) {
assertNotNull(resourcePath);
if (containsJcrContent(resourcePath)) {
return resourcePath;
} else {
return resourcePath + "/" + CHILD_NODE_NAME;
}
}
@Override
public String getCollectionItemResourcePath(@NotNull String resourcePath) {
assertNotNull(resourcePath);
return resourcePath;
}
@Override
public String getConfigName(@NotNull String configName, @Nullable String relatedConfigPath) {
assertNotNull(configName);
if (containsJcrContent(configName)) {
return configName;
} else {
return configName + "/" + CHILD_NODE_NAME;
}
}
@Override
public String getCollectionParentConfigName(@NotNull String configName, @Nullable String relatedConfigPath) {
assertNotNull(configName);
if (containsJcrContent(configName)) {
return configName;
} else {
return configName + "/" + CHILD_NODE_NAME;
}
}
@Override
public String getCollectionItemConfigName(@NotNull String configName, @Nullable String relatedConfigPath) {
assertNotNull(configName);
return configName;
}
@Override
public boolean persistConfiguration(
@NotNull ResourceResolver resourceResolver,
@NotNull String configResourcePath,
@NotNull ConfigurationPersistData data) {
getOrCreateResource(resourceResolver, getResourcePath(configResourcePath), data.getProperties());
commit(resourceResolver);
return true;
}
@Override
public boolean persistConfigurationCollection(
@NotNull ResourceResolver resourceResolver,
@NotNull String configResourceCollectionParentPath,
@NotNull ConfigurationCollectionPersistData data) {
String parentPath = getCollectionParentResourcePath(configResourceCollectionParentPath);
Resource configResourceParent = getOrCreateResource(resourceResolver, parentPath, ValueMap.EMPTY);
// delete existing children and create new ones
deleteChildren(configResourceParent);
for (ConfigurationPersistData item : data.getItems()) {
String path =
getCollectionItemResourcePath(configResourceParent.getPath() + "/" + item.getCollectionItemName());
getOrCreateResource(resourceResolver, path, item.getProperties());
}
// if resource collection parent properties are given replace them as well
if (data.getProperties() != null) {
Resource propsResource =
getOrCreateResource(resourceResolver, parentPath + "/colPropsResource", ValueMap.EMPTY);
replaceProperties(propsResource, data.getProperties());
}
commit(resourceResolver);
return true;
}
@Override
public boolean deleteConfiguration(@NotNull ResourceResolver resourceResolver, @NotNull String configResourcePath) {
Resource resource = resourceResolver.getResource(configResourcePath);
if (resource != null) {
try {
resourceResolver.delete(resource);
} catch (PersistenceException ex) {
throw new ConfigurationPersistenceException(
"Unable to delete configuration at " + configResourcePath, ex);
}
}
commit(resourceResolver);
return true;
}
private Resource getOrCreateResource(
ResourceResolver resourceResolver, String path, Map<String, Object> properties) {
try {
Resource resource = ResourceUtil.getOrCreateResource(
resourceResolver, path, DEFAULT_RESOURCE_TYPE, DEFAULT_RESOURCE_TYPE, false);
replaceProperties(resource, properties);
return resource;
} catch (PersistenceException ex) {
throw new ConfigurationPersistenceException("Unable to persist configuration to " + path, ex);
}
}
private void deleteChildren(Resource resource) {
ResourceResolver resourceResolver = resource.getResourceResolver();
try {
for (Resource child : resource.getChildren()) {
resourceResolver.delete(child);
}
} catch (PersistenceException ex) {
throw new ConfigurationPersistenceException("Unable to remove children from " + resource.getPath(), ex);
}
}
@SuppressWarnings("null")
private void replaceProperties(Resource resource, Map<String, Object> properties) {
ModifiableValueMap modValueMap = resource.adaptTo(ModifiableValueMap.class);
// remove all existing properties that do not have jcr: namespace
for (String propertyName : new HashSet<>(modValueMap.keySet())) {
if (StringUtils.startsWith(propertyName, "jcr:")) {
continue;
}
modValueMap.remove(propertyName);
}
modValueMap.putAll(properties);
}
private void commit(ResourceResolver resourceResolver) {
try {
resourceResolver.commit();
} catch (PersistenceException ex) {
throw new ConfigurationPersistenceException("Unable to save configuration: " + ex.getMessage(), ex);
}
}
static boolean containsJcrContent(String path) {
return JCR_CONTENT_PATTERN.matcher(path).matches();
}
}