blob: a41b7c23eb5a34f494278eb4368a83c79f22d07c [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.auth.form.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.cm.ConfigurationAdminOptions.factoryConfiguration;
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 AuthFormTestSupport extends TestSupport {
private static final String BUNDLE_SYMBOLICNAME = "TEST-CONTENT-BUNDLE";
private static final String SLING_BUNDLE_RESOURCES_HEADER = "Sling-Bundle-Resources";
protected static final String FORM_AUTH_VERIFY_USER = "form-auth-user";
protected static final String FORM_AUTH_VERIFY_PWD = "testing";
protected final Logger logger = LoggerFactory.getLogger(getClass());
@Inject
private HealthCheckExecutor hcExecutor;
@Configuration
public Option[] configuration() throws IOException {
return options(
composite(
super.baseConfiguration(),
vmOption(System.getProperty("pax.vm.options")),
optionalRemoteDebug(),
slingQuickstart(),
testBundle("bundle.filename"),
// testing - add a user to use to login and verify the content loading has happened
factoryConfiguration("org.apache.sling.jcr.repoinit.RepositoryInitializer")
.put("scripts", new String[] {
"create user " + FORM_AUTH_VERIFY_USER + " with password " + FORM_AUTH_VERIFY_PWD +"\n"
})
.asOption(),
junitBundles(),
awaitility()
).add(
additionalOptions()
).remove(
// remove our bundle under test to avoid duplication
mavenBundle().groupId("org.apache.sling").artifactId("org.apache.sling.auth.form").version(versionResolver)
)
);
}
protected Option[] additionalOptions() throws IOException {
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);
}
}
/**
* 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();
}
}