blob: 464ef74118c00a2877ddc29e0ba424a8d7286b8e [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.event.it;
import static org.ops4j.pax.exam.CoreOptions.frameworkProperty;
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.systemProperty;
import static org.ops4j.pax.exam.CoreOptions.when;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Dictionary;
import java.util.Hashtable;
import java.util.List;
import javax.inject.Inject;
import org.apache.sling.api.resource.LoginException;
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.ResourceResolverFactory;
import org.apache.sling.discovery.PropertyProvider;
import org.apache.sling.event.impl.jobs.config.JobManagerConfiguration;
import org.apache.sling.event.jobs.JobManager;
import org.apache.sling.event.jobs.consumer.JobConsumer;
import org.apache.sling.event.jobs.consumer.JobExecutor;
import org.ops4j.pax.exam.Configuration;
import org.ops4j.pax.exam.CoreOptions;
import org.ops4j.pax.exam.Option;
import org.ops4j.pax.exam.cm.ConfigurationAdminOptions;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleException;
import org.osgi.framework.InvalidSyntaxException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.event.EventAdmin;
import org.osgi.service.event.EventConstants;
import org.osgi.service.event.EventHandler;
import org.slf4j.LoggerFactory;
public abstract class AbstractJobHandlingTest {
private static final String BUNDLE_JAR_SYS_PROP = "project.bundle.file";
/** The property containing the build directory. */
private static final String SYS_PROP_BUILD_DIR = "bundle.build.dir";
private static final String DEFAULT_BUILD_DIR = "target";
private static final String PORT_CONFIG = "org.osgi.service.http.port";
protected static final int DEFAULT_TEST_TIMEOUT = 1000*60*5;
@Inject
protected EventAdmin eventAdmin;
@Inject
protected ConfigurationAdmin configAdmin;
@Inject
protected BundleContext bc;
protected List<ServiceRegistration<?>> registrations = new ArrayList<>();
@Configuration
public Option[] config() {
final String buildDir = System.getProperty(SYS_PROP_BUILD_DIR, DEFAULT_BUILD_DIR);
final String bundleFileName = System.getProperty( BUNDLE_JAR_SYS_PROP );
final File bundleFile = new File( bundleFileName );
if ( !bundleFile.canRead() ) {
throw new IllegalArgumentException( "Cannot read from bundle file " + bundleFileName + " specified in the "
+ BUNDLE_JAR_SYS_PROP + " system property" );
}
String localRepo = System.getProperty("maven.repo.local", "");
final String jackrabbitVersion = "2.13.1";
final String oakVersion = "1.5.7";
final String slingHome = new File(buildDir + File.separatorChar + "sling_" + System.currentTimeMillis()).getAbsolutePath();
return options(
frameworkProperty("sling.home").value(slingHome),
frameworkProperty("repository.home").value(slingHome + File.separatorChar + "repository"),
when( localRepo.length() > 0 ).useOptions(
systemProperty("org.ops4j.pax.url.mvn.localRepository").value(localRepo)
),
when( System.getProperty(PORT_CONFIG) != null ).useOptions(
systemProperty(PORT_CONFIG).value(System.getProperty(PORT_CONFIG))),
systemProperty("pax.exam.osgi.unresolved.fail").value("true"),
ConfigurationAdminOptions.newConfiguration("org.apache.felix.jaas.ConfigurationSpi")
.create(true)
.put("jaas.defaultRealmName", "jackrabbit.oak")
.put("jaas.configProviderName", "FelixJaasProvider")
.asOption(),
ConfigurationAdminOptions.factoryConfiguration("org.apache.felix.jaas.Configuration.factory")
.create(true)
.put("jaas.controlFlag", "optional")
.put("jaas.classname", "org.apache.jackrabbit.oak.spi.security.authentication.GuestLoginModule")
.put("jaas.ranking", 300)
.asOption(),
ConfigurationAdminOptions.factoryConfiguration("org.apache.felix.jaas.Configuration.factory")
.create(true)
.put("jaas.controlFlag", "required")
.put("jaas.classname", "org.apache.jackrabbit.oak.security.authentication.user.LoginModuleImpl")
.asOption(),
ConfigurationAdminOptions.factoryConfiguration("org.apache.felix.jaas.Configuration.factory")
.create(true)
.put("jaas.controlFlag", "sufficient")
.put("jaas.classname", "org.apache.jackrabbit.oak.security.authentication.token.TokenLoginModule")
.put("jaas.ranking", 200)
.asOption(),
ConfigurationAdminOptions.newConfiguration("org.apache.jackrabbit.oak.security.authentication.AuthenticationConfigurationImpl")
.create(true)
.put("org.apache.jackrabbit.oak.authentication.configSpiName", "FelixJaasProvider")
.asOption(),
ConfigurationAdminOptions.newConfiguration("org.apache.jackrabbit.oak.security.user.UserConfigurationImpl")
.create(true)
.put("groupsPath", "/home/groups")
.put("usersPath", "/home/users")
.put("defaultPath", "1")
.put("importBehavior", "besteffort")
.asOption(),
ConfigurationAdminOptions.newConfiguration("org.apache.jackrabbit.oak.security.user.RandomAuthorizableNodeName")
.create(true)
.put("enabledActions", new String[] {"org.apache.jackrabbit.oak.spi.security.user.action.AccessControlAction"})
.put("userPrivilegeNames", new String[] {"jcr:all"})
.put("groupPrivilegeNames", new String[] {"jcr:read"})
.asOption(),
ConfigurationAdminOptions.newConfiguration("org.apache.jackrabbit.oak.spi.security.user.action.DefaultAuthorizableActionProvider")
.create(true)
.put("length", 21)
.asOption(),
ConfigurationAdminOptions.newConfiguration("org.apache.jackrabbit.oak.plugins.segment.SegmentNodeStoreService")
.create(true)
.put("name", "Default NodeStore")
.asOption(),
ConfigurationAdminOptions.factoryConfiguration("org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended")
.create(true)
.put("user.mapping", "org.apache.sling.event=admin")
.asOption(),
ConfigurationAdminOptions.newConfiguration("org.apache.sling.jcr.resource.internal.JcrSystemUserValidator")
.create(true)
.put("allow.only.system.user", "false")
.asOption(),
// logging
systemProperty("pax.exam.logging").value("none"),
mavenBundle("org.apache.sling", "org.apache.sling.commons.log", "4.0.6"),
mavenBundle("org.apache.sling", "org.apache.sling.commons.logservice", "1.0.6"),
mavenBundle("org.slf4j", "slf4j-api", "1.7.13"),
mavenBundle("org.slf4j", "jcl-over-slf4j", "1.7.13"),
mavenBundle("org.slf4j", "log4j-over-slf4j", "1.7.13"),
mavenBundle("commons-io", "commons-io", "2.4"),
mavenBundle("commons-fileupload", "commons-fileupload", "1.3.1"),
mavenBundle("commons-collections", "commons-collections", "3.2.2"),
mavenBundle("commons-codec", "commons-codec", "1.10"),
mavenBundle("commons-lang", "commons-lang", "2.6"),
mavenBundle("org.apache.commons", "commons-lang3", "3.5"),
mavenBundle("commons-pool", "commons-pool", "1.6"),
mavenBundle("io.dropwizard.metrics", "metrics-core", "3.2.4"),
mavenBundle("org.apache.servicemix.bundles", "org.apache.servicemix.bundles.concurrent", "1.3.4_1"),
mavenBundle("org.apache.geronimo.bundles", "commons-httpclient", "3.1_1"),
mavenBundle("org.apache.tika", "tika-core", "1.9"),
mavenBundle("org.apache.tika", "tika-bundle", "1.9"),
// infrastructure
mavenBundle("org.apache.felix", "org.apache.felix.http.servlet-api", "1.1.2"),
mavenBundle("org.apache.felix", "org.apache.felix.http.jetty", "3.1.6"),
mavenBundle("org.apache.felix", "org.apache.felix.eventadmin", "1.4.8"),
mavenBundle("org.apache.felix", "org.apache.felix.scr", "2.0.6"),
mavenBundle("org.apache.felix", "org.apache.felix.configadmin", "1.8.10"),
mavenBundle("org.apache.felix", "org.apache.felix.inventory", "1.0.4"),
mavenBundle("org.apache.felix", "org.apache.felix.metatype", "1.1.2"),
// sling
mavenBundle("org.apache.sling", "org.apache.sling.settings", "1.3.8"),
mavenBundle("org.apache.sling", "org.apache.sling.commons.osgi", "2.3.0"),
mavenBundle("org.apache.sling", "org.apache.sling.commons.mime", "2.1.8"),
mavenBundle("org.apache.sling", "org.apache.sling.commons.classloader", "1.3.2"),
mavenBundle("org.apache.sling", "org.apache.sling.commons.johnzon", "1.0.0"),
mavenBundle("org.apache.sling", "org.apache.sling.commons.scheduler", "2.4.14"),
mavenBundle("org.apache.sling", "org.apache.sling.commons.threads", "3.2.4"),
mavenBundle("org.apache.sling", "org.apache.sling.commons.metrics", "1.2.6"),
mavenBundle("org.apache.sling", "org.apache.sling.auth.core", "1.3.12"),
mavenBundle("org.apache.sling", "org.apache.sling.discovery.api", "1.0.2"),
mavenBundle("org.apache.sling", "org.apache.sling.discovery.commons", "1.0.20"),
mavenBundle("org.apache.sling", "org.apache.sling.discovery.standalone", "1.0.2"),
mavenBundle("org.apache.sling", "org.apache.sling.api", "2.14.2"),
mavenBundle("org.apache.sling", "org.apache.sling.resourceresolver", "1.4.18"),
mavenBundle("org.apache.sling", "org.apache.sling.adapter", "2.1.10"),
mavenBundle("org.apache.sling", "org.apache.sling.jcr.resource", "2.8.0"),
mavenBundle("org.apache.sling", "org.apache.sling.jcr.classloader", "3.2.2"),
mavenBundle("org.apache.sling", "org.apache.sling.jcr.contentloader", "2.2.4"),
mavenBundle("org.apache.sling", "org.apache.sling.engine", "2.6.2"),
mavenBundle("org.apache.sling", "org.apache.sling.serviceusermapper", "1.3.2"),
mavenBundle("org.apache.sling", "org.apache.sling.jcr.jcr-wrapper", "2.0.0"),
mavenBundle("org.apache.sling", "org.apache.sling.jcr.api", "2.4.0"),
mavenBundle("org.apache.sling", "org.apache.sling.jcr.base", "2.4.0"),
mavenBundle("com.google.guava", "guava", "15.0"),
mavenBundle("org.apache.jackrabbit", "jackrabbit-api", jackrabbitVersion),
mavenBundle("org.apache.jackrabbit", "jackrabbit-jcr-commons", jackrabbitVersion),
mavenBundle("org.apache.jackrabbit", "jackrabbit-spi", jackrabbitVersion),
mavenBundle("org.apache.jackrabbit", "jackrabbit-spi-commons", jackrabbitVersion),
mavenBundle("org.apache.jackrabbit", "jackrabbit-jcr-rmi", jackrabbitVersion),
mavenBundle("org.apache.felix", "org.apache.felix.jaas", "0.0.4"),
mavenBundle("org.apache.jackrabbit", "oak-core", oakVersion),
mavenBundle("org.apache.jackrabbit", "oak-commons", oakVersion),
mavenBundle("org.apache.jackrabbit", "oak-lucene", oakVersion),
mavenBundle("org.apache.jackrabbit", "oak-blob", oakVersion),
mavenBundle("org.apache.jackrabbit", "oak-jcr", oakVersion),
mavenBundle("org.apache.jackrabbit", "oak-segment", oakVersion),
mavenBundle("org.apache.sling", "org.apache.sling.jcr.oak.server", "1.1.0"),
mavenBundle("org.apache.sling", "org.apache.sling.testing.tools", "1.0.16"),
mavenBundle("org.apache.httpcomponents", "httpcore-osgi", "4.1.2"),
mavenBundle("org.apache.httpcomponents", "httpclient-osgi", "4.1.2"),
// SLING-5560: delaying start of the sling.event bundle to
// ensure the parameter 'startup.delay' is properly set to 1sec
// for these ITs - as otherwise, the default of 30sec applies -
// which will cause the tests to fail
// @see setup() where the bundle is finally started - after reconfig
CoreOptions.bundle( bundleFile.toURI().toString() ).start(false),
junitBundles()
);
}
protected JobManager getJobManager() {
JobManager result = null;
int count = 0;
do {
final ServiceReference<JobManager> sr = this.bc.getServiceReference(JobManager.class);
if ( sr != null ) {
result = this.bc.getService(sr);
} else {
count++;
if ( count == 10 ) {
break;
}
sleep(500);
}
} while ( result == null );
return result;
}
protected void sleep(final long time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// ignore
}
}
public void setup() throws IOException {
// set load delay to 3 sec
final org.osgi.service.cm.Configuration c2 = this.configAdmin.getConfiguration("org.apache.sling.event.impl.jobs.jcr.PersistenceHandler", null);
Dictionary<String, Object> p2 = new Hashtable<>();
p2.put(JobManagerConfiguration.PROPERTY_BACKGROUND_LOAD_DELAY, 3L);
// and startup.delay to 1sec - otherwise default of 30sec breaks tests!
p2.put("startup.delay", 1L);
c2.update(p2);
// SLING-5560 : since the above (re)config is now applied, we're safe
// to go ahead and start the sling.event bundle.
// this time, the JobManagerConfiguration will be activated
// with the 'startup.delay' set to 1sec - so that ITs actually succeed
try {
Bundle[] bundles = bc.getBundles();
for (Bundle bundle : bundles) {
if (bundle.getSymbolicName().contains("sling.event")) {
// assuming we only have 1 bundle that contains 'sling.event'
LoggerFactory.getLogger(getClass()).info("starting bundle... "+bundle);
bundle.start();
break;
}
}
} catch (BundleException e) {
LoggerFactory.getLogger(getClass()).error("could not start sling.event bundle: "+e, e);
throw new RuntimeException(e);
}
}
private int deleteCount;
private void delete(final Resource rsrc )
throws PersistenceException {
final ResourceResolver resolver = rsrc.getResourceResolver();
for(final Resource child : rsrc.getChildren()) {
delete(child);
}
resolver.delete(rsrc);
deleteCount++;
if ( deleteCount >= 20 ) {
resolver.commit();
deleteCount = 0;
}
}
public void cleanup() {
// clean job area
final ServiceReference<ResourceResolverFactory> ref = this.bc.getServiceReference(ResourceResolverFactory.class);
final ResourceResolverFactory factory = this.bc.getService(ref);
ResourceResolver resolver = null;
try {
resolver = factory.getAdministrativeResourceResolver(null);
final Resource rsrc = resolver.getResource("/var/eventing");
if ( rsrc != null ) {
delete(rsrc);
resolver.commit();
}
} catch ( final LoginException le ) {
// ignore
} catch (final PersistenceException e) {
// ignore
} catch ( final Exception e ) {
// sometimes an NPE is thrown from the repository, as we
// are in the cleanup, we can ignore this
} finally {
if ( resolver != null ) {
resolver.close();
}
}
// unregister all services
for(final ServiceRegistration<?> reg : this.registrations) {
reg.unregister();
}
this.registrations.clear();
// remove all configurations
try {
final org.osgi.service.cm.Configuration[] cfgs = this.configAdmin.listConfigurations(null);
if ( cfgs != null ) {
for(final org.osgi.service.cm.Configuration c : cfgs) {
try {
c.delete();
} catch (final IOException io) {
// ignore
}
}
}
} catch (final IOException io) {
// ignore
} catch (final InvalidSyntaxException e) {
// ignore
}
this.sleep(1000);
}
/**
* Helper method to register an event handler
*/
protected ServiceRegistration<EventHandler> registerEventHandler(final String topic,
final EventHandler handler) {
final Dictionary<String, Object> props = new Hashtable<>();
props.put(EventConstants.EVENT_TOPIC, topic);
final ServiceRegistration<EventHandler> reg = this.bc.registerService(EventHandler.class,
handler, props);
this.registrations.add(reg);
return reg;
}
protected long getConsumerChangeCount() {
long result = -1;
try {
final Collection<ServiceReference<PropertyProvider>> refs = this.bc.getServiceReferences(PropertyProvider.class, "(changeCount=*)");
if ( !refs.isEmpty() ) {
result = (Long)refs.iterator().next().getProperty("changeCount");
}
} catch ( final InvalidSyntaxException ignore ) {
// ignore
}
return result;
}
protected void waitConsumerChangeCount(final long minimum) {
do {
final long cc = getConsumerChangeCount();
if ( cc >= minimum ) {
// we need to wait for the topology events (TODO)
sleep(200);
return;
}
sleep(50);
} while ( true );
}
/**
* Helper method to register a job consumer
*/
protected ServiceRegistration<JobConsumer> registerJobConsumer(final String topic,
final JobConsumer handler) {
long cc = this.getConsumerChangeCount();
final Dictionary<String, Object> props = new Hashtable<>();
props.put(JobConsumer.PROPERTY_TOPICS, topic);
final ServiceRegistration<JobConsumer> reg = this.bc.registerService(JobConsumer.class,
handler, props);
this.registrations.add(reg);
this.waitConsumerChangeCount(cc + 1);
return reg;
}
/**
* Helper method to register a job executor
*/
protected ServiceRegistration<JobExecutor> registerJobExecutor(final String topic,
final JobExecutor handler) {
long cc = this.getConsumerChangeCount();
final Dictionary<String, Object> props = new Hashtable<>();
props.put(JobConsumer.PROPERTY_TOPICS, topic);
final ServiceRegistration<JobExecutor> reg = this.bc.registerService(JobExecutor.class,
handler, props);
this.registrations.add(reg);
this.waitConsumerChangeCount(cc + 1);
return reg;
}
protected void unregister(final ServiceRegistration<?> reg) {
if ( reg != null ) {
this.registrations.remove(reg);
reg.unregister();
}
}
}