blob: 34c86c236b9527767842d0425a13a8d7119dc66c [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.jcr.jackrabbit.accessmanager.it;
import static org.apache.felix.hc.api.FormattingResultLog.msHumanReadable;
import static org.apache.sling.testing.paxexam.SlingOptions.awaitility;
import static org.apache.sling.testing.paxexam.SlingOptions.slingQuickstartOakTar;
import static org.apache.sling.testing.paxexam.SlingOptions.versionResolver;
import static org.awaitility.Awaitility.await;
import static org.junit.Assert.assertNotNull;
import static org.ops4j.pax.exam.CoreOptions.composite;
import static org.ops4j.pax.exam.CoreOptions.junitBundles;
import static org.ops4j.pax.exam.CoreOptions.mavenBundle;
import static org.ops4j.pax.exam.CoreOptions.options;
import static org.ops4j.pax.exam.CoreOptions.streamBundle;
import static org.ops4j.pax.exam.CoreOptions.vmOption;
import static org.ops4j.pax.exam.CoreOptions.when;
import static org.ops4j.pax.tinybundles.core.TinyBundles.withBnd;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.text.SimpleDateFormat;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Inject;
import org.apache.felix.hc.api.Result;
import org.apache.felix.hc.api.ResultLog;
import org.apache.felix.hc.api.execution.HealthCheckExecutionResult;
import org.apache.felix.hc.api.execution.HealthCheckExecutor;
import org.apache.felix.hc.api.execution.HealthCheckSelector;
import org.apache.sling.testing.paxexam.TestSupport;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.options.ModifiableCompositeOption;
import org.ops4j.pax.exam.options.extra.VMOption;
import org.ops4j.pax.tinybundles.core.TinyBundle;
import org.ops4j.pax.tinybundles.core.TinyBundles;
import org.osgi.framework.Constants;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public abstract class AccessManagerTestSupport extends TestSupport {
private static final String BUNDLE_SYMBOLICNAME = "TEST-CONTENT-BUNDLE";
private static final String SLING_BUNDLE_RESOURCES_HEADER = "Sling-Bundle-Resources";
protected final Logger logger = LoggerFactory.getLogger(getClass());
@Inject
private HealthCheckExecutor hcExecutor;
@Configuration
public Option[] configuration() throws IOException {
final String vmOpt = System.getProperty("pax.vm.options");
VMOption vmOption = null;
if (vmOpt != null && !vmOpt.isEmpty()) {
vmOption = new VMOption(vmOpt);
}
final String jacocoOpt = System.getProperty("jacoco.command");
VMOption jacocoCommand = null;
if (jacocoOpt != null && !jacocoOpt.isEmpty()) {
jacocoCommand = new VMOption(jacocoOpt);
}
// switch to the minimum oak version that supports principalbased access control
versionResolver.setVersion("org.apache.jackrabbit", "oak-api", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-blob", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-blob-plugins", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-commons", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-core", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-core-spi", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-jcr", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-lucene", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-query-spi", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-security-spi", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-segment-tar", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-store-composite", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-store-document", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-store-spi", "1.16.0");
versionResolver.setVersion("org.apache.jackrabbit", "oak-jackrabbit-api", "1.16.0");
// newer version of sling.api and dependencies for SLING-10034
// may remove at a later date if the superclass includes these versions or later
versionResolver.setVersionFromProject("org.apache.sling", "org.apache.sling.api");
versionResolver.setVersion("org.apache.sling", "org.apache.sling.resourceresolver", "1.7.0"); // to be compatible with current o.a.sling.api
versionResolver.setVersion("org.apache.sling", "org.apache.sling.scripting.core", "2.3.4"); // to be compatible with current o.a.sling.api
versionResolver.setVersion("org.apache.sling", "org.apache.sling.scripting.api", "2.2.0"); // to be compatible with current o.a.sling.api
versionResolver.setVersion("org.apache.sling", "org.apache.sling.servlets.resolver", "2.7.12"); // to be compatible with current o.a.sling.api
versionResolver.setVersion("org.apache.sling", "org.apache.sling.commons.compiler", "2.4.0"); // to be compatible with current o.a.sling.scripting.core
return options(
composite(
super.baseConfiguration(),
when(vmOption != null).useOptions(vmOption),
when(jacocoCommand != null).useOptions(jacocoCommand),
optionalRemoteDebug(),
slingQuickstart(),
testBundle("bundle.filename"),
junitBundles(),
awaitility()
).add(
// needed by latest version of org.apache.sling.api
mavenBundle().groupId("org.apache.felix").artifactId("org.apache.felix.converter").version("1.0.14"),
// switch to the oak variation of jackrabbit-api
mavenBundle().groupId("org.apache.jackrabbit").artifactId("oak-jackrabbit-api").version(versionResolver)
).add(
additionalOptions()
).remove(
// remove our bundle under test to avoid duplication
mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.jcr.jackrabbit.accessmanager").version(versionResolver),
// switch to the oak variation of jackrabbit-api
mavenBundle().groupId("org.apache.jackrabbit").artifactId("jackrabbit-api").version(versionResolver)
)
);
}
protected Option[] additionalOptions() throws IOException { // NOSONAR
return new Option[]{};
}
protected Option slingQuickstart() {
final String workingDirectory = workingDirectory();
final int httpPort = findFreePort();
return composite(
slingQuickstartOakTar(workingDirectory, httpPort)
);
}
public String getTestFileUrl(String path) {
return getClass().getResource(path).toExternalForm();
}
/**
* Optionally configure remote debugging on the port supplied by the "debugPort"
* system property.
*/
protected ModifiableCompositeOption optionalRemoteDebug() {
VMOption option = null;
String property = System.getProperty("debugPort");
if (property != null) {
option = vmOption(String.format("-Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=%s", property));
}
return composite(option);
}
/**
* Wait for the health check to be ok
*
* @param timeoutMsec the max time to wait for the health check to be ok
* @param nextIterationDelay the sleep time between the check attempts
*/
protected void waitForServerReady(long timeoutMsec, long nextIterationDelay) {
// retry until the exec call returns true and doesn't throw any exception
await().atMost(timeoutMsec, TimeUnit.MILLISECONDS)
.pollInterval(nextIterationDelay, TimeUnit.MILLISECONDS)
.until(this::doHealthCheck);
}
/**
* @return true if health checks are ok
*/
protected boolean doHealthCheck() throws IOException {
boolean isOk = true;
logger.info("Performing health check");
HealthCheckSelector hcs = HealthCheckSelector.tags("systemalive");
List<HealthCheckExecutionResult> results = hcExecutor.execute(hcs);
logger.info("systemalive health check got {} results", results.size());
isOk &= !results.isEmpty();
for (final HealthCheckExecutionResult exR : results) {
final Result r = exR.getHealthCheckResult();
if (logger.isInfoEnabled()) {
logger.info("systemalive health check: {}", toHealthCheckResultInfo(exR, false));
}
isOk &= r.isOk();
if (!isOk) {
break; // found a failure so stop checking further
}
}
if (isOk) {
hcs = HealthCheckSelector.tags("bundles");
results = hcExecutor.execute(hcs);
logger.info("bundles health check got {} results", results.size());
isOk &= !results.isEmpty();
for (final HealthCheckExecutionResult exR : results) {
final Result r = exR.getHealthCheckResult();
if (logger.isInfoEnabled()) {
logger.info("bundles health check: {}", toHealthCheckResultInfo(exR, false));
}
isOk &= r.isOk();
if (!isOk) {
break; // found a failure so stop checking further
}
}
}
return isOk;
}
/**
* Produce a human readable report of the health check results that is suitable for
* debugging or writing to a log
*/
protected String toHealthCheckResultInfo(final HealthCheckExecutionResult exResult, final boolean debug) throws IOException {
String value = null;
try (StringWriter resultWriter = new StringWriter(); BufferedWriter writer = new BufferedWriter(resultWriter)) {
final Result result = exResult.getHealthCheckResult();
writer.append('"').append(exResult.getHealthCheckMetadata().getTitle()).append('"');
writer.append(" result is: ").append(result.getStatus().toString());
writer.newLine();
writer.append(" Finished: ").append(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(exResult.getFinishedAt()) + " after "
+ msHumanReadable(exResult.getElapsedTimeInMs()));
for (final ResultLog.Entry e : result) {
if (!debug && e.isDebug()) {
continue;
}
writer.newLine();
writer.append(" ");
writer.append(e.getStatus().toString());
writer.append(' ');
writer.append(e.getMessage());
if (e.getException() != null) {
writer.append(" ");
writer.append(e.getException().toString());
}
}
writer.flush();
value = resultWriter.toString();
}
return value;
}
/**
* Add content to our test bundle
*/
protected void addContent(final TinyBundle bundle, String resourcePath) throws IOException {
String pathInBundle = resourcePath;
resourcePath = "/content" + resourcePath;
try (final InputStream is = getClass().getResourceAsStream(resourcePath)) {
assertNotNull("Expecting resource to be found:" + resourcePath, is);
logger.info("Adding resource to bundle, path={}, resource={}", pathInBundle, resourcePath);
bundle.add(pathInBundle, is);
}
}
/**
* Override to provide the option for your test
*
* @return the tinybundle Option or null if none
*/
protected Option buildBundleResourcesBundle() throws IOException {
return null;
}
/**
* Build a test bundle containing the specified bundle resources
*
* @param header the value for the {@link #SLING_BUNDLE_RESOURCES_HEADER} header
* @param content the collection of files to embed in the tinybundle
* @return the tinybundle Option
*/
protected Option buildBundleResourcesBundle(final String header, final Collection<String> content) throws IOException {
final TinyBundle bundle = TinyBundles.bundle();
bundle.set(Constants.BUNDLE_SYMBOLICNAME, BUNDLE_SYMBOLICNAME);
bundle.set(SLING_BUNDLE_RESOURCES_HEADER, header);
bundle.set("Require-Capability", "osgi.extender;filter:=\"(&(osgi.extender=org.apache.sling.bundleresource)(version<=1.1.0)(!(version>=2.0.0)))\"");
for (final String entry : content) {
addContent(bundle, entry);
}
return streamBundle(
bundle.build(withBnd())
).start();
}
}