This is an automated email from the ASF dual-hosted git repository. andysch pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/sling-org-apache-sling-kickstart.git
commit f38674383ea57c1b585beb22f011779774d0ca45 Author: Andreas Schaefer <[email protected]> AuthorDate: Thu Feb 6 13:55:37 2020 -0800 Renamed Quickstart to Kickstart --- Readme.md | 27 + pom.xml | 308 ++++++ .../apache/sling/kickstart/app/SlingStarter.java | 322 ++++++ .../sling/kickstart/control/ControlAction.java | 53 + .../sling/kickstart/control/ControlListener.java | 628 +++++++++++ .../sling/kickstart/control/ControlTarget.java | 32 + .../org.apache.sling.feature.launcher.spi.Launcher | 1 + ...eature.launcher.spi.extensions.ExtensionHandler | 2 + src/main/resources/feature-sling12.json | 1139 ++++++++++++++++++++ .../feature/starter/it/LaunchpadReadyRule.java | 121 +++ .../apache/sling/feature/starter/it/SmokeIT.java | 195 ++++ .../sling/feature/starter/it/package-info.java | 30 + 12 files changed, 2858 insertions(+) diff --git a/Readme.md b/Readme.md new file mode 100644 index 0000000..2fc61a3 --- /dev/null +++ b/Readme.md @@ -0,0 +1,27 @@ +# Sling Feature Starter + +This project is the Feature Model based version of the **sling-org-apache-sling-starter** +module and creates an executable JAR file for now. +It is also a test case for the Slingstart Feature Maven Plugin as it uses it +to launch a Launchpad Ready Rule and Smoke tests. + +## Build + +This plugin depends on the **Sling Start Feature Maven Plugin** (also in the Sling +Whiteboard) which is then used to run the IT tests: + +1. Go to **sling-slingstart-feature-maven-plugin** module in Sling Whiteboard +2. Build with: `mvn clean install` +3. Go back to **sling-org-apache-sling-feature-starter** +4. Build and Launch it with: `mvn clean install` +5. Sling will come up and run the IT tests and then shut down. Sling can be + kept running after the end of the IT tests by providing the property + **block.sling.at.the.end** with the value **true** + +## Usage + +After the resulting jar file **org.apache.sling.feature.starter-<version>.jar** +can be executed with: +``` +java -jar org.apache.sling.feature.starter-<version>.jar ... +``` diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..fc5d676 --- /dev/null +++ b/pom.xml @@ -0,0 +1,308 @@ +<?xml version="1.0" encoding="ISO-8859-1"?> +<!-- + 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> + <modelVersion>4.0.0</modelVersion> + <parent> + <groupId>org.apache.sling</groupId> + <artifactId>sling</artifactId> + <version>35</version> + <relativePath /> + </parent> + + <artifactId>org.apache.sling.kickstart</artifactId> + <version>0.0.1-SNAPSHOT</version> + <packaging>slingkickstart</packaging> + + <name>Apache Sling Kickstart Launcher</name> + <description> + An Executable JAR file to launch Sling + </description> + + <!-- <scm>--> + <!-- <connection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-starter.git</connection>--> + <!-- <developerConnection>scm:git:https://gitbox.apache.org/repos/asf/sling-org-apache-sling-feature-starter.git</developerConnection>--> + <!-- <url>https://gitbox.apache.org/repos/asf?p=sling-org-apache-sling-feature-starter.git</url>--> + <!-- <tag>HEAD</tag>--> + <!-- </scm>--> + + <properties> + <sling.java.version>8</sling.java.version> + <picocli.version>3.6.0</picocli.version> + <appassembler-maven-plugin.version>2.0.0</appassembler-maven-plugin.version> + <org.apache.sling.feature.extension.content.version>1.0.4</org.apache.sling.feature.extension.content.version> + <org.apache.sling.feature.launcher.version>1.1.2</org.apache.sling.feature.launcher.version> + <org.apache.sling.feature.io.version>1.2.2</org.apache.sling.feature.io.version> + <org.apache.felix.converter.version>1.0.8</org.apache.felix.converter.version> + + <sling.java.version>8</sling.java.version> + <IT.expected.bundles.count>126</IT.expected.bundles.count> + + <block.sling.at.the.end>false</block.sling.at.the.end> + </properties> + + <build> + <plugins> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>ianal-maven-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>verify-legal-files</goal> + </goals> + </execution> + </executions> + </plugin> + <plugin> + <artifactId>maven-clean-plugin</artifactId> + <configuration> + <filesets> + <fileset> + <directory>${basedir}</directory> + <includes> + <include>sling/**</include> + <include>coverage.ec</include> + </includes> + </fileset> + </filesets> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-dependency-plugin</artifactId> + <executions> + <execution> + <id>unpack-dependencies</id> + <phase>prepare-package</phase> + <goals> + <goal>unpack-dependencies</goal> + </goals> + <configuration> + <excludes>META-INF/**</excludes> + <outputDirectory>${project.build.directory}/classes</outputDirectory> + <overWriteReleases>false</overWriteReleases> + <overWriteSnapshots>true</overWriteSnapshots> + <includeArtifactIds> + commons-io, + org.apache.sling.feature.extension.content, + org.apache.sling.feature.launcher, + osgi.core,commons-lang, + org.apache.sling.feature, + org.apache.sling.feature.io, + org.apache.felix.converter, + picocli, + slf4j-api, + slf4j-simple + </includeArtifactIds> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-jar-plugin</artifactId> + <configuration> + <archive> + <manifest> + <mainClass>org.apache.sling.kickstart.app.SlingStarter</mainClass> + </manifest> + </archive> + </configuration> + </plugin> + <plugin> + <groupId>org.apache.rat</groupId> + <artifactId>apache-rat-plugin</artifactId> + <configuration> + <excludes> + <exclude>readme.md</exclude> + <exclude>src/main/resources/META-INF/services/**</exclude> + <exclude>**/*.properties</exclude> + <exclude>launcher/**</exclude> + </excludes> + </configuration> + </plugin> + <plugin> + <groupId>org.codehaus.mojo</groupId> + <artifactId>build-helper-maven-plugin</artifactId> + <executions> + <execution> + <id>reserve-network-port</id> + <goals> + <!-- pre-integration-test is too late --> + <goal>reserve-network-port</goal> + </goals> + <phase>process-resources</phase> + <configuration> + <portNames> + <portName>http.port</portName> + <portName>sling.control.port</portName> + </portNames> + </configuration> + </execution> + </executions> + </plugin> + <plugin> + <groupId>org.apache.sling</groupId> + <artifactId>sling-kickstart-maven-plugin</artifactId> + <version>0.0.1-SNAPSHOT</version> + <extensions>true</extensions> + <executions> + <execution> + <id>start-container-before-IT</id> + <goals> + <goal>start</goal> + </goals> + </execution> +<!-- <execution>--> +<!-- <id>stop-container-after-IT</id>--> +<!-- <goals>--> +<!-- <goal>stop</goal>--> +<!-- </goals>--> +<!-- <configuration>--> +<!-- <!– Let the Test Server run to manually verify the setup. TODO: remove later –>--> +<!-- <shouldBlockUntilKeyIsPressed>${block.sling.at.the.end}</shouldBlockUntilKeyIsPressed>--> +<!-- </configuration>--> +<!-- </execution>--> + </executions> + <configuration> + <launchpadJar>${project.build.directory}/${project.artifactId}-${project.version}.jar</launchpadJar> + <parallelExecution>false</parallelExecution> + <servers> + <server> + <port>${http.port}</port> + <controlPort>${sling.control.port}</controlPort> + <debug>true</debug> + </server> + </servers> + </configuration> + </plugin> + <plugin> + <artifactId>maven-failsafe-plugin</artifactId> + <executions> + <execution> + <goals> + <goal>integration-test</goal> + <goal>verify</goal> + </goals> + </execution> + </executions> + <configuration> + <systemPropertyVariables> + <launchpad.http.port>${http.port}</launchpad.http.port> + <IT.expected.bundles.count>${IT.expected.bundles.count}</IT.expected.bundles.count> + </systemPropertyVariables> + </configuration> + </plugin> + </plugins> + + <pluginManagement> + <plugins> + <plugin> + <!-- Extend RAT configuration from parent pom --> + <groupId>org.apache.rat</groupId> + <artifactId>apache-rat-plugin</artifactId> + <configuration> + <excludes combine.children="append"> + <!-- Exclude sling instance --> + <exclude>sling/**</exclude> + </excludes> + </configuration> + </plugin> + </plugins> + </pluginManagement> + </build> + + <dependencies> + <!-- + | CLI + --> + <dependency> + <groupId>info.picocli</groupId> + <artifactId>picocli</artifactId> + <version>${picocli.version}</version> + </dependency> + + <dependency> + <groupId>commons-lang</groupId> + <artifactId>commons-lang</artifactId> + <version>2.6</version> + </dependency> + <dependency> + <groupId>org.osgi</groupId> + <artifactId>osgi.core</artifactId> + <version>7.0.0</version> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-api</artifactId> + </dependency> + <dependency> + <groupId>org.slf4j</groupId> + <artifactId>slf4j-simple</artifactId> + </dependency> + + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.framework</artifactId> + <version>6.0.2</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.feature.extension.content</artifactId> + <version>${org.apache.sling.feature.extension.content.version}</version> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.feature.launcher</artifactId> + <version>${org.apache.sling.feature.launcher.version}</version> + </dependency> + <dependency> + <groupId>org.apache.sling</groupId> + <artifactId>org.apache.sling.feature.io</artifactId> + <version>${org.apache.sling.feature.io.version}</version> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.converter</artifactId> + <version>${org.apache.felix.converter.version}</version> + </dependency> + + <dependency> + <groupId>commons-io</groupId> + <artifactId>commons-io</artifactId> + <version>2.6</version> + </dependency> + + <!-- Testing dependencies --> + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.httpcomponents</groupId> + <artifactId>httpclient</artifactId> + <version>4.5.10</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.felix</groupId> + <artifactId>org.apache.felix.utils</artifactId> + <version>1.11.2</version> + <scope>test</scope> + </dependency> + </dependencies> +</project> diff --git a/src/main/java/org/apache/sling/kickstart/app/SlingStarter.java b/src/main/java/org/apache/sling/kickstart/app/SlingStarter.java new file mode 100644 index 0000000..beda54f --- /dev/null +++ b/src/main/java/org/apache/sling/kickstart/app/SlingStarter.java @@ -0,0 +1,322 @@ +/* + * 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.kickstart.app; + +import java.io.File; +import java.net.MalformedURLException; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import org.apache.commons.lang.StringUtils; +import org.apache.sling.feature.launcher.impl.Main; +import org.apache.sling.kickstart.control.ControlAction; +import org.apache.sling.kickstart.control.ControlListener; +import org.apache.sling.kickstart.control.ControlTarget; +import picocli.CommandLine; +import picocli.CommandLine.Command; +import picocli.CommandLine.Option; +import picocli.CommandLine.Parameters; + + +@Command( + name = "java -jar <Sling Feature Starter JAR File>", + description = "Apache Sling Feature Starter", + footer = "Copyright(c) 2019 The Apache Software Foundation." +) +public class SlingStarter implements Runnable, ControlTarget { + + @Option(names = { "-s", "--mainFeature" }, description = "main feature file (file path or URL) replacing the provided Sling Feature File", required = false) + private String mainFeatureFile; + + @Option(names = { "-af", "--additionalFeature" }, description = "additional feature files", required = false) + private List<String> additionalFeatureFile; + + @Option(names = { "-j", "--control" }, description = "host and port to use for control connection in the format '[host:]port' (default 127.0.0.1:0)", required = false) + private String controlAddress; + + @Option(names = { "-l", "--logLevel" }, description = "the initial loglevel (0..4, FATAL, ERROR, WARN, INFO, DEBUG)", required = false) + private String logLevel; + + @Option(names = { "-f", "--logFile" }, description = "the log file, \"-\" for stdout (default logs/error.log)", required = false) + private String logFile; + + @Option(names = { "-c", "--slingHome" }, description = "the sling context directory (default sling)", required = false) + private String slingHome; + + //AS TODO: does this still apply here + @Option(names = { "-i", "--launcherHome" }, description = "the launcher home directory (default launcher)", required = false) + private String launcherHome; + + @Option(names = { "-a", "--address" }, description = "the interface to bind to (use 0.0.0.0 for any)", required = false) + private String address; + + @Option(names = { "-p", "--port" }, description = "the port to listen to (default 8080)", required = false) + private String port; + + @Option(names = { "-r", "--context" }, description = "the root servlet context path for the http service (default is /)", required = false) + private String contextPath; + + @Option(names = { "-n", "--noShutdownHook" }, description = "don't install the shutdown hook") + private boolean noShutdownHook; + + @Option(names = { "-v", "--verbose" }, description = "the feature launcher is verbose on launch", required = false) + private boolean verbose; + + @Option(names = {"-D", "--define"}, description = "sets property n to value v. Make sure to use this option *after* the jar filename. " + + "The JVM also has a -D option which has a different meaning", required = false) + private Map<String, String> properties = new HashMap<>(); + + @Option(names = { "-h", "--help" }, usageHelp = true, description = "Display the usage message.") + private boolean helpRequested; + + @Parameters(paramLabel = "COMMAND", description = "Optional Command for Server Instance Interaction, can be one of: 'start', 'stop', 'status' or 'threads'", arity = "0..1") + private String command; + + // The name of the environment variable to consult to find out + // about sling.home + private static final String ENV_SLING_HOME = "SLING_HOME"; + + /** + * The name of the configuration property indicating the socket to use for + * the control connection. The value of this property is either just a port + * number (in which case the host is assumed to be <code>localhost</code>) + * or a host name (or IP address) and port number separated by a colon. + */ + protected static final String PROP_CONTROL_SOCKET = "sling.control.socket"; + + /** The Sling configuration property name setting the initial log level */ + private static final String PROP_LOG_LEVEL = "org.apache.sling.commons.log.level"; + + /** The Sling configuration property name setting the initial log file */ + private static final String PROP_LOG_FILE = "org.apache.sling.commons.log.file"; + + /** + * The configuration property setting the port on which the HTTP service + * listens + */ + private static final String PROP_PORT = "org.osgi.service.http.port"; + + /** + * The configuration property setting the context path where the HTTP service + * mounts itself. + */ + private static final String PROP_CONTEXT_PATH = "org.apache.felix.http.context_path"; + + /** + * Host name or IP Address of the interface to listen on. + */ + private static final String PROP_HOST = "org.apache.felix.http.host"; + + /** + * Name of the configuration property (or system property) indicating + * whether the shutdown hook should be installed or not. If this property is + * not set or set to {@code true} (case insensitive), the shutdown hook + * properly shutting down the framework is installed on startup. Otherwise, + * if this property is set to any value other than {@code true} (case + * insensitive) the shutdown hook is not installed. + * <p> + * The respective command line option is {@code -n}. + */ + private static final String PROP_SHUTDOWN_HOOK = "sling.shutdown.hook"; + + private boolean started = false; + + @Override + public void run() { + try { + URL mainFeatureURL = checkFeatureFile(mainFeatureFile); + if(mainFeatureURL == null) { + mainFeatureURL = getClass().getResource("/feature-sling12.json"); + } + List<String> argumentList = new ArrayList<>(); + argumentList.add("-f"); + argumentList.add(mainFeatureURL.toString()); + if(additionalFeatureFile != null) { + for (String additional : additionalFeatureFile) { + URL additionalURL = checkFeatureFile(additional); + if (additionalURL != null) { + argumentList.add("-f"); + argumentList.add(additionalURL.toString()); + } + } + } + if(StringUtils.isNotEmpty(logLevel)) { + addArgument(argumentList, PROP_LOG_LEVEL, logLevel); + } + if(StringUtils.isNotEmpty(logFile)) { + addArgument(argumentList, PROP_LOG_FILE, logFile); + } + if(StringUtils.isNotEmpty(port)) { + addArgument(argumentList, PROP_PORT, port); + } + if(StringUtils.isNotEmpty(address)) { + addArgument(argumentList, PROP_HOST, address); + } + if(StringUtils.isNotEmpty(contextPath)) { + addArgument(argumentList, PROP_CONTEXT_PATH, contextPath); + } + if(verbose) { + argumentList.add("-v"); + } + System.out.println("Before Launching Feature Launcher, arguments: " + argumentList); + // Now we have to handle any Start Option + ControlAction controlAction = getControlAction(command); + int answer = doControlAction(controlAction, controlAddress); + if (answer >= 0) { + doTerminateVM(answer); + return; + } + + // finally start Sling + if (!doStart(argumentList)) { + error("Failed to start Sling; terminating", null); + doTerminateVM(1); + return; + } + } catch(Throwable t) { + System.out.println("Caught an Exception: " + t.getLocalizedMessage()); + t.printStackTrace(); + } + } + + private void addArgument(List<String> list, String key, String value) { + list.add("-D"); + list.add(key + "=" + value); + } + + private URL checkFeatureFile(String featureFile) { + URL answer = null; + if(featureFile != null && !featureFile.isEmpty()) { + try { + URL check = new URL(featureFile); + check.toURI(); + answer = check; + } catch (MalformedURLException | URISyntaxException e) { + // Try it as a file + File check = new File(featureFile); + if (!check.exists() || !check.canRead()) { + throw new RuntimeException("Given Feature File is not a valid URL or File: '" + featureFile + "'", e); + } + try { + answer = check.toURI().toURL(); + } catch (MalformedURLException ex) { + throw new RuntimeException("Given Feature File cannot be converted to an URL: '" + featureFile + "'", e); + } + } + } + return answer; + } + + public static void main(String[] args) { + CommandLine.run(new SlingStarter(), args); + } + + private int doControlAction(ControlAction controlAction, String controlAddress) { + final ControlListener sl = new ControlListener( + this, + controlAddress + ); + switch (controlAction) { + case FOREGROUND: + if (!sl.listen()) { + return -1; + } + break; + case START: + if (!sl.listen()) { + // assume service already running + return 0; + } + break; + case STOP: + return sl.shutdownServer(); + case STATUS: + return sl.statusServer(); + case THREADS: + return sl.dumpThreads(); + } + return -1; + } + + private boolean doStart(List<String> argumentList) { + // prevent duplicate start + if ( this.started) { + info("Apache Sling has already been started", new Exception("Where did this come from")); + return true; + } + + info("Starting Apache Sling in " + slingHome, null); + this.started = true; + System.out.println("Start Command: '" + command + "'"); + try { + Main.main(argumentList.toArray(new String[]{})); + } catch(Error | RuntimeException e) { + error("Launching Sling Feature failed", e); + return false; + } + return true; + } + + private ControlAction getControlAction(String command) { + ControlAction answer = ControlAction.FOREGROUND; + try { + answer = ControlAction.valueOf(command.toUpperCase()); + } catch (IllegalArgumentException e) { + throw new RuntimeException("Given Control Action is not valid: '" + command.toUpperCase() + "'"); + } catch (NullPointerException e) { + // Ignore as we set the default to FOREGROUND anyhow + } + return answer; + } + + @Override + public String getHome() { + return slingHome; + } + + @Override + public void doStop() { + info("Stop Application", null); + System.exit(0); + } + + @Override + public void doTerminateVM(int status) { + info("Terminate VM, status: " + status, null); + System.exit(status); + } + + @Override + public void info(String message, Throwable t) { + System.out.println(message); + if(t != null) { + t.printStackTrace(); + } + } + + @Override + public void error(String message, Throwable t) { + System.err.println(message); + if(t != null) { + t.printStackTrace(System.err); + } + } +} diff --git a/src/main/java/org/apache/sling/kickstart/control/ControlAction.java b/src/main/java/org/apache/sling/kickstart/control/ControlAction.java new file mode 100644 index 0000000..feea4ca --- /dev/null +++ b/src/main/java/org/apache/sling/kickstart/control/ControlAction.java @@ -0,0 +1,53 @@ +/* + * 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.kickstart.control; + +/** + * The <code>ControlAction</code> defines values to used as the action for the + * Feature Launcher control with the {@link Main#doControlAction()} method. + */ +public enum ControlAction { + + /** + * Indicates the Feature Launcher application should be started and a listener should + * be installed to accept control commands. + */ + START, + + /** + * Indicates to connect to a running Feature Launcher application having installed a + * listener and send that application the command to shutdown. + */ + STOP, + + /** + * Indicates to connect to a running Feature Launcher application having installed a + * listener and ask that application about its state. + */ + STATUS, + + FOREGROUND, + + /** + * Indicates to connect to a running Feature Launcher application having installed a + * listener and ask for a thread dump. + */ + THREADS; + +} diff --git a/src/main/java/org/apache/sling/kickstart/control/ControlListener.java b/src/main/java/org/apache/sling/kickstart/control/ControlListener.java new file mode 100644 index 0000000..d621842 --- /dev/null +++ b/src/main/java/org/apache/sling/kickstart/control/ControlListener.java @@ -0,0 +1,628 @@ +/* + * 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.kickstart.control; + +import java.io.BufferedReader; +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileReader; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStreamReader; +import java.io.LineNumberReader; +import java.io.OutputStreamWriter; +import java.lang.management.LockInfo; +import java.lang.management.ManagementFactory; +import java.lang.management.MonitorInfo; +import java.lang.management.ThreadInfo; +import java.lang.management.ThreadMXBean; +import java.math.BigInteger; +import java.net.ConnectException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.security.SecureRandom; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; + +/** + * The <code>ControlListener</code> class is a helper class for the {@link ControlTarget} + * class to support in Sling standalone application process communication. This + * class implements the client and server sides of a TCP/IP based communication + * channel to control a running Sling application. + * <p> + * The server side listens for commands on a configurable host and port &endash; + * <code>localhost:63000</code> by default &endash; supporting the following + * commands: + * <table> + * <tr> + * <th>Command</th> + * <th>Description</th> + * </tr> + * <tr> + * <td><code>status</code></td> + * <td>Request status information. Currently only <i>OK</i> is sent back. If no + * connection can be created to the server the client assumes Sling is not + * running.</td> + * </tr> + * <tr> + * <td><code>stop</code></td> + * <td>Requests Sling to shutdown.</td> + * </tr> + * </table> + */ +public class ControlListener implements Runnable { + + // command sent by the client to cause Sling to shutdown + static final String COMMAND_STOP = "stop"; + + // command sent by the client to check for the status of the server + static final String COMMAND_STATUS = "status"; + + // command sent by the client to request a thread dump + static final String COMMAND_THREADS = "threads"; + + // the response sent by the server if the command executed successfully + private static final String RESPONSE_OK = "OK"; + + // the status response sent by the server when shutting down + private static final String RESPONSE_STOPPING = "STOPPING"; + + // The default interface to listen on + private static final String DEFAULT_LISTEN_INTERFACE = "127.0.0.1"; + + // The default port to listen on and to connect to - we select it randomly + private static final int DEFAULT_LISTEN_PORT = 0; + + // The reference to the Main class to shutdown on request + private final ControlTarget controlTarget; + + private final String listenSpec; + + private String secretKey; + private InetSocketAddress socketAddress; + + private volatile Thread shutdownThread = null; + + /** + * Creates an instance of this control support class. + * <p> + * The host (name or address) and port number of the socket is defined by + * the <code>listenSpec</code> parameter. This parameter is defined as + * <code>[ host ":" ] port</code>. If the parameter is empty or + * <code>null</code> it defaults to <i>localhost:0</i>. If the host name + * is missing it defaults to <i>localhost</i>. + * + * @param controlTarget The Main class reference. This is only required if this + * instance is used for the server side to listen for remote stop + * commands. Otherwise this argument may be <code>null</code>. + * @param listenSpec The specification for the host and port for the socket + * connection. See above for the format of this parameter. + */ + public ControlListener(final ControlTarget controlTarget, final String listenSpec) { + this.controlTarget = controlTarget; + this.listenSpec = listenSpec; // socketAddress = this.getSocketAddress(listenSpec, selectNewPort); + } + + /** + * Implements the server side of the control connection starting a thread + * listening on the host and port configured on setup of this instance. + */ + public boolean listen() { + final File configFile = getConfigFile(); + if (configFile.canRead() && statusServer() == 0) { + // server already running, fail + controlTarget.error("Sling already active in " + this.controlTarget.getHome(), null); + return false; + } + configFile.delete(); + + final Thread listener = new Thread(this); + listener.setDaemon(true); + listener.setName("Apache Sling Control Listener (inactive)"); + listener.start(); + return true; + } + + /** + * Implements the client side of the control connection sending the command + * to shutdown Sling. + */ + public int shutdownServer() { + return sendCommand(COMMAND_STOP); + } + + /** + * Implements the client side of the control connection sending the command + * to check whether Sling is active. + */ + public int statusServer() { + return sendCommand(COMMAND_STATUS); + } + + /** + * Implements the client side of the control connection sending the command + * to retrieve a thread dump. + */ + public int dumpThreads() { + return sendCommand(COMMAND_THREADS); + } + + // ---------- Runnable interface + + /** + * Implements the server thread receiving commands from clients and acting + * upon them. + */ + @Override + public void run() { + this.configure(false); + + final ServerSocket server; + try { + server = new ServerSocket(); + server.bind(this.socketAddress); + writePortToConfigFile(getConfigFile(), + new InetSocketAddress(server.getInetAddress(), server.getLocalPort()), this.secretKey); + Thread.currentThread().setName( + "Apache Sling Control Listener@" + server.getInetAddress() + ":" + server.getLocalPort()); + controlTarget.info("Apache Sling Control Listener started", null); + } catch (final IOException ioe) { + controlTarget.error("Failed to start Apache Sling Control Listener", ioe); + return; + } + + long delay = 0; + + try { + while (true) { + + final Socket s; + try { + s = server.accept(); + } catch (IOException ioe) { + // accept terminated, most probably due to Socket.close() + // just end the loop and exit + break; + } + + // delay processing after unsuccessful attempts + if (delay > 0) { + controlTarget.info(s.getRemoteSocketAddress() + ": Delay: " + (delay / 1000), null); + try { + Thread.sleep(delay); + } catch (InterruptedException e) { + } + } + + try { + final String commandLine = readLine(s); + if (commandLine == null) { + final String msg = "ERR: missing command"; + writeLine(s, msg); + continue; + } + + final int blank = commandLine.indexOf(' '); + if (blank < 0) { + final String msg = "ERR: missing key"; + writeLine(s, msg); + continue; + } + + if (!secretKey.equals(commandLine.substring(0, blank))) { + final String msg = "ERR: wrong key"; + writeLine(s, msg); + delay = (delay > 0) ? delay * 2 : 1000L; + continue; + } + + final String command = commandLine.substring(blank + 1); + controlTarget.info(s.getRemoteSocketAddress() + ">" + command, null); + + if (COMMAND_STOP.equals(command)) { + if (this.shutdownThread != null) { + writeLine(s, RESPONSE_STOPPING); + } else { + this.shutdownThread = new Thread("Apache Sling Control Listener: Shutdown") { + @Override + public void run() { + controlTarget.doStop(); + try { + server.close(); + } catch (final IOException ignore) { + } + } + }; + this.shutdownThread.start(); + writeLine(s, RESPONSE_OK); + } + + } else if (COMMAND_STATUS.equals(command)) { + writeLine(s, (this.shutdownThread == null) ? RESPONSE_OK : RESPONSE_STOPPING); + + } else if (COMMAND_THREADS.equals(command)) { + dumpThreads(s); + + } else { + final String msg = "ERR:" + command; + writeLine(s, msg); + + } + } finally { + try { + s.close(); + } catch (IOException ignore) { + } + } + } + } catch (final IOException ioe) { + controlTarget.error("Failure reading from client", ioe); + } finally { + try { + server.close(); + } catch (final IOException ignore) { + } + } + + getConfigFile().delete(); + + // everything has stopped and when this thread terminates, + // the VM should stop. If there are still some non-daemon threads + // active, this will not happen, so we force this here ... + controlTarget.info("Apache Sling terminated, exiting Java VM", null); + this.controlTarget.doTerminateVM(0); + } + + // ---------- socket support + + private void dumpThreads(final Socket socket) throws IOException { + + final ThreadMXBean threadBean = ManagementFactory.getThreadMXBean(); + final ThreadInfo[] threadInfos = threadBean.dumpAllThreads(true, true); + + for (ThreadInfo thread : threadInfos) { + printThread(socket, thread); + + // add locked synchronizers + final LockInfo[] locks = thread.getLockedSynchronizers(); + writeLine(socket, "-"); + writeLine(socket, "- Locked ownable synchronizers:"); + if (locks.length > 0) { + for (LockInfo li : locks) { + writeLine(socket, String.format("- - locked %s", + formatLockInfo( + li.getClassName(), + li.getIdentityHashCode() + ) + )); + } + } else { + writeLine(socket, "- - None"); + } + + // empty separator line + writeLine(socket, "-"); + } + + final long[] deadLocked; + if (threadBean.isSynchronizerUsageSupported()) { + deadLocked = threadBean.findDeadlockedThreads(); + } else { + deadLocked = threadBean.findMonitorDeadlockedThreads(); + } + if (deadLocked != null) { + final ThreadInfo[] dl = threadBean.getThreadInfo(deadLocked, true, true); + final Set<ThreadInfo> dlSet = new HashSet<ThreadInfo>(Arrays.asList(dl)); + int deadlockCount = 0; + for (ThreadInfo current : dl) { + if (dlSet.remove(current)) { + + // find and record a single deadlock + ArrayList<ThreadInfo> loop = new ArrayList<ThreadInfo>(); + do { + loop.add(current); + for (ThreadInfo cand : dl) { + if (cand.getThreadId() == current.getLockOwnerId()) { + current = (dlSet.remove(cand)) ? cand : null; + break; + } + } + } while (current != null); + + deadlockCount++; + + // print the deadlock + writeLine(socket, "-Found one Java-level deadlock:"); + writeLine(socket, "-============================="); + for (ThreadInfo thread : loop) { + writeLine(socket, String.format("-\"%s\" #%d", + thread.getThreadName(), + thread.getThreadId() + )); + writeLine(socket, String.format("- waiting on %s", + formatLockInfo( + thread.getLockInfo().getClassName(), + thread.getLockInfo().getIdentityHashCode() + ) + )); + writeLine(socket, String.format("- which is held by \"%s\" #%d", + thread.getLockOwnerName(), + thread.getLockOwnerId() + )); + } + writeLine(socket, "-"); + + writeLine(socket, "-Java stack information for the threads listed above:"); + writeLine(socket, "-==================================================="); + + for (ThreadInfo thread : loop) { + printThread(socket, thread); + } + writeLine(socket, "-"); + } + } + +// "Thread-8": +// waiting to lock monitor 7f89fb80da08 (object 7f37a0968, a java.lang.Object), +// which is held by "Thread-7" +// "Thread-7": +// waiting to lock monitor 7f89fb80b0b0 (object 7f37a0958, a java.lang.Object), +// which is held by "Thread-8" + + writeLine(socket, String.format("-Found %d deadlocks.", + deadlockCount + )); + } + + writeLine(socket, RESPONSE_OK); + } + + private String formatLockInfo(final String className, final int objectId) { + return String.format("<%08x> (a %s)", objectId, className); + } + + private void printThread(final Socket socket, final ThreadInfo thread) throws IOException { + writeLine(socket, String.format("-\"%s\" #%d", + thread.getThreadName(), + thread.getThreadId() + )); + + writeLine(socket, String.format("- java.lang.Thread.State: %s", + thread.getThreadState() + )); + + final MonitorInfo[] monitors = thread.getLockedMonitors(); + final StackTraceElement[] trace = thread.getStackTrace(); + for (int i=0; i < trace.length; i++) { + StackTraceElement ste = trace[i]; + if (ste.isNativeMethod()) { + writeLine(socket, String.format("- at %s.%s(Native Method)", + ste.getClassName(), + ste.getMethodName() + )); + } else { + writeLine(socket, String.format("- at %s.%s(%s:%d)", + ste.getClassName(), + ste.getMethodName(), + ste.getFileName(), + ste.getLineNumber() + )); + } + + if (i == 0 && thread.getLockInfo() != null) { + writeLine(socket, String.format("- - waiting on %s%s", + formatLockInfo( + thread.getLockInfo().getClassName(), + thread.getLockInfo().getIdentityHashCode() + ), + (thread.getLockOwnerId() >= 0) + ? String.format(" owned by \"%s\" #%d", + thread.getLockOwnerName(), + thread.getLockOwnerId() + ):"" + )); + } + + for (MonitorInfo mi : monitors) { + if (i == mi.getLockedStackDepth()) { + writeLine(socket, String.format("- - locked %s", + formatLockInfo( + mi.getClassName(), + mi.getIdentityHashCode() + ) + )); + } + } + } + } + + /** + * Sends the given command to the server indicated by the configured + * socket address and logs the reply. + * + * @param command The command to send + * + * @return A code indicating success of sending the command. + */ + private int sendCommand(final String command) { + if (configure(true)) { + if (this.secretKey == null) { + controlTarget.info("Missing secret key to protect sending '" + command + "' to " + this.socketAddress, null); + return 4; // LSB code for unknown status + } + + Socket socket = null; + try { + socket = new Socket(); + socket.connect(this.socketAddress); + writeLine0(socket, this.secretKey + " " + command); + final String result = readLine(socket); + controlTarget.info("Sent '" + command + "' to " + this.socketAddress + ": " + result, null); + return 0; // LSB code for everything's fine + } catch (final ConnectException ce) { + controlTarget.info("No Apache Sling running at " + this.socketAddress, null); + return 3; // LSB code for programm not running + } catch (final IOException ioe) { + controlTarget.error("Failed sending '" + command + "' to " + this.socketAddress, ioe); + return 1; // LSB code for programm dead + } finally { + if (socket != null) { + try { + socket.close(); + } catch (IOException ignore) { + } + } + } + } + controlTarget.info("No socket address to send '" + command + "' to", null); + return 4; // LSB code for unknown status + } + + private String readLine(final Socket socket) throws IOException { + final BufferedReader br = new BufferedReader(new InputStreamReader( + socket.getInputStream(), "UTF-8")); + + StringBuilder b = new StringBuilder(); + boolean more = true; + while (more) { + String s = br.readLine(); + if (s != null && s.startsWith("-")) { + s = s.substring(1); + } else { + more = false; + } + if (b.length() > 0) { + b.append("\r\n"); + } + b.append(s); + } + + return b.toString(); + } + + private void writeLine(final Socket socket, final String line) throws IOException { + controlTarget.info(socket.getRemoteSocketAddress() + "<" + line, null); + this.writeLine0(socket, line); + } + + private void writeLine0(final Socket socket, final String line) throws IOException { + final BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream(), "UTF-8")); + bw.write(line); + bw.write("\r\n"); + bw.flush(); + } + + /** + * Read the port from the config file + * @return The port or null + */ + private boolean configure(final boolean fromConfigFile) { + boolean result = false; + if (fromConfigFile) { + final File configFile = this.getConfigFile(); + if (configFile.canRead()) { + try ( final LineNumberReader lnr = new LineNumberReader(new FileReader(configFile))) { + this.socketAddress = getSocketAddress(lnr.readLine()); + this.secretKey = lnr.readLine(); + result = true; + } catch (final IOException ignore) { + // ignore + } + } + } else { + this.socketAddress = getSocketAddress(this.listenSpec); + this.secretKey = generateKey(); + result = true; + } + + return result; + } + + private static String generateKey() { + return new BigInteger(165, new SecureRandom()).toString(32); + } + + /** + * Return the control port file + */ + private File getConfigFile() { + final File configDir = new File(this.controlTarget.getHome(), "conf"); + return new File(configDir, "controlport"); + } + + private InetSocketAddress getSocketAddress(String listenSpec) { + try { + + final String address; + final int port; + if (listenSpec == null) { + address = DEFAULT_LISTEN_INTERFACE; + port = DEFAULT_LISTEN_PORT; + } else { + final int colon = listenSpec.indexOf(':'); + if (colon < 0) { + address = DEFAULT_LISTEN_INTERFACE; + port = Integer.parseInt(listenSpec); + } else { + address = listenSpec.substring(0, colon); + port = Integer.parseInt(listenSpec.substring(colon + 1)); + } + } + + final InetSocketAddress addr = new InetSocketAddress(address, port); + if (!addr.isUnresolved()) { + return addr; + } + + controlTarget.error("Unknown host in '" + listenSpec, null); + } catch (final NumberFormatException nfe) { + controlTarget.error("Cannot parse port number from '" + listenSpec + "'", + null); + } + + return null; + } + + private void writePortToConfigFile(final File configFile, final InetSocketAddress socketAddress, + final String secretKey) { + configFile.getParentFile().mkdirs(); + FileWriter fw = null; + try { + fw = new FileWriter(configFile); + fw.write(socketAddress.getAddress().getHostAddress()); + fw.write(':'); + fw.write(String.valueOf(socketAddress.getPort())); + fw.write('\n'); + fw.write(secretKey); + fw.write('\n'); + } catch (final IOException ignore) { + // ignore + } finally { + if (fw != null) { + try { + fw.close(); + } catch (final IOException ignore) { + } + } + } + } +} diff --git a/src/main/java/org/apache/sling/kickstart/control/ControlTarget.java b/src/main/java/org/apache/sling/kickstart/control/ControlTarget.java new file mode 100644 index 0000000..3f714ba --- /dev/null +++ b/src/main/java/org/apache/sling/kickstart/control/ControlTarget.java @@ -0,0 +1,32 @@ +/* + * 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.kickstart.control; + +public interface ControlTarget { + + public String getHome(); + + public void doStop(); + + public void doTerminateVM(int status); + + void info(String message, Throwable t); + + void error(String message, Throwable t); +} diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.launcher.spi.Launcher b/src/main/resources/META-INF/services/org.apache.sling.feature.launcher.spi.Launcher new file mode 100644 index 0000000..21483ff --- /dev/null +++ b/src/main/resources/META-INF/services/org.apache.sling.feature.launcher.spi.Launcher @@ -0,0 +1 @@ +org.apache.sling.feature.launcher.impl.launchers.FrameworkLauncher \ No newline at end of file diff --git a/src/main/resources/META-INF/services/org.apache.sling.feature.launcher.spi.extensions.ExtensionHandler b/src/main/resources/META-INF/services/org.apache.sling.feature.launcher.spi.extensions.ExtensionHandler new file mode 100644 index 0000000..e936f9a --- /dev/null +++ b/src/main/resources/META-INF/services/org.apache.sling.feature.launcher.spi.extensions.ExtensionHandler @@ -0,0 +1,2 @@ +org.apache.sling.feature.launcher.impl.extensions.handlers.RepoInitHandler +org.apache.sling.feature.launcher.impl.extensions.handlers.ContentPackageHandler diff --git a/src/main/resources/feature-sling12.json b/src/main/resources/feature-sling12.json new file mode 100644 index 0000000..85b68c4 --- /dev/null +++ b/src/main/resources/feature-sling12.json @@ -0,0 +1,1139 @@ +{ + "id":"org.apache.sling:org.apache.sling.feature.model.starter:slingosgifeature:sling12:12-SNAPSHOT", + "title":"Apache Sling Feature Module Starter Application", + "description":"The Sling Starter application built from Feature Models", + "vendor":"The Apache Software Foundation", + "license":"Apache License, Version 2.0", + "variables":{ + "composum.nodes.version":"1.11.3", + "oak.version":"1.16.0", + "jackson.version":"2.9.9", + "slf4j.version":"1.7.25", + "jackrabbit.version":"2.18.2" + }, + "bundles":[ + { + "id":"org.apache.aries:org.apache.aries.util:1.1.3", + "start-order":"1" + }, + { + "id":"org.apache.commons:commons-lang3:3.8.1", + "start-order":"1" + }, + { + "id":"org.apache.felix:org.apache.felix.configadmin:1.9.14", + "start-order":"1" + }, + { + "id":"org.apache.felix:org.apache.felix.eventadmin:1.5.0", + "start-order":"1" + }, + { + "id":"org.apache.geronimo.specs:geronimo-annotation_1.3_spec:1.1", + "start-order":"1" + }, + { + "id":"org.apache.geronimo.specs:geronimo-atinject_1.0_spec:1.1", + "start-order":"1" + }, + { + "id":"org.apache.geronimo.specs:geronimo-ws-metadata_2.0_spec:1.1.3", + "start-order":"1" + }, + { + "id":"org.apache.servicemix.bundles:org.apache.servicemix.bundles.jaxb-impl:2.2.11_1", + "start-order":"1" + }, + { + "id":"org.apache.servicemix.bundles:org.apache.servicemix.bundles.saaj-impl:1.3.23_2", + "start-order":"1" + }, + { + "id":"org.apache.servicemix.specs:org.apache.servicemix.specs.jaxb-api-2.2:2.9.0", + "start-order":"1" + }, + { + "id":"org.apache.servicemix.specs:org.apache.servicemix.specs.jaxws-api-2.2:2.9.0", + "start-order":"1" + }, + { + "id":"org.apache.servicemix.specs:org.apache.servicemix.specs.saaj-api-1.3:2.8.0", + "start-order":"1" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.log:5.1.10", + "start-order":"1" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.logservice:1.0.6", + "start-order":"1" + }, + { + "id":"org.apache.sling:org.apache.sling.installer.core:3.9.0", + "start-order":"1" + }, + { + "id":"org.apache.sling:org.apache.sling.installer.factory.configuration:1.2.2", + "start-order":"1" + }, + { + "id":"org.apache.sling:org.apache.sling.installer.provider.file:1.1.0", + "start-order":"1" + }, + { + "id":"org.apache.sling:org.apache.sling.javax.activation:0.1.0", + "start-order":"1" + }, + { + "id":"org.apache.sling:org.apache.sling.settings:1.3.10", + "start-order":"1" + }, + { + "id":"org.jvnet.staxex:stax-ex:1.7.6", + "start-order":"1" + }, + { + "id":"org.osgi:org.osgi.util.function:1.1.0", + "start-order":"1" + }, + { + "id":"org.osgi:org.osgi.util.promise:1.1.0", + "start-order":"1" + }, + { + "id":"org.slf4j:jcl-over-slf4j:1.7.25", + "start-order":"1" + }, + { + "id":"org.slf4j:log4j-over-slf4j:1.7.25", + "start-order":"1" + }, + { + "id":"org.slf4j:slf4j-api:1.7.25", + "start-order":"1" + }, + { + "id":"com.composum.sling.core:composum-sling-core-commons:1.11.3", + "start-order":"20" + }, + { + "id":"com.composum.sling.core:composum-sling-core-console:1.11.3", + "start-order":"20" + }, + { + "id":"com.composum.sling.core:composum-sling-core-jslibs:1.11.3", + "start-order":"20" + }, + { + "id":"com.composum.sling.core:composum-sling-package-manager:1.11.3", + "start-order":"20" + }, + { + "id":"com.composum.sling.core:composum-sling-user-management:1.11.3", + "start-order":"20" + }, + { + "id":"org.apache.jackrabbit.vault:org.apache.jackrabbit.vault:3.2.4", + "start-order":"20" + }, + { + "id":"org.apache.felix:org.apache.felix.healthcheck.api:2.0.2", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.healthcheck.core:2.0.6", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.healthcheck.generalchecks:2.0.4", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.healthcheck.webconsoleplugin:2.0.0", + "start-order":"5" + }, + { + "id":"org.apache.sling:org.apache.sling.hc.api:1.0.4", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.hc.support:1.0.6", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.launchpad.base:6.0.2-2.6.36", + "start-order":"20" + }, + { + "id":"org.apache.felix:org.apache.felix.jaas:1.0.2", + "start-order":"10" + }, + { + "id":"org.apache.jackrabbit:oak-api:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-blob-plugins:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-blob:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-commons:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-core-spi:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-core:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-jcr:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-lucene:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-query-spi:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-security-spi:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-store-composite:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-store-document:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:oak-store-spi:1.16.0", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.oak.server:1.2.2", + "start-order":"16" + }, + { + "id":"org.apache.jackrabbit:oak-segment-tar:1.16.0", + "run-modes":"oak_tar", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.repoinit:1.1.10", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.provisioning.model:1.8.4", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.repoinit.parser:1.2.4", + "start-order":"20" + }, + { + "id":"org.antlr:antlr4-runtime:4.7.1", + "start-order":"20" + }, + { + "id":"org.apache.servicemix.bundles:org.apache.servicemix.bundles.rhino:1.7.10_1", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.api:2.2.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.core:2.0.56", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.el-api:1.0.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.javascript:3.0.4", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.jsp-api:1.0.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.jsp.taglib:2.4.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.jsp:2.3.4", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.sightly.compiler.java:1.1.2-1.4.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.sightly.compiler:1.1.2-1.4.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.sightly.js.provider:1.0.28", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.sightly.models.provider:1.0.8", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.sightly.repl:1.0.6", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.sightly.runtime:1.1.0-1.4.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.scripting.sightly:1.1.2-1.4.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.caconfig.api:1.1.2", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.caconfig.impl:1.4.14", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.caconfig.spi:1.3.4", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.discovery.api:1.0.4", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.discovery.base:2.0.8", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.discovery.commons:1.0.20", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.discovery.oak:1.2.28", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.discovery.support:1.0.4", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.event.dea:1.1.4", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.event:4.2.12", + "start-order":"20" + }, + { + "id":"com.fasterxml.jackson.core:jackson-annotations:2.9.9", + "start-order":"20" + }, + { + "id":"com.fasterxml.jackson.core:jackson-core:2.9.9", + "start-order":"20" + }, + { + "id":"com.fasterxml.jackson.core:jackson-databind:2.9.9", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.models.jacksonexporter:1.0.8", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.validation.api:1.0.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.validation.core:1.0.4", + "start-order":"20" + }, + { + "id":"commons-codec:commons-codec:1.12", + "start-order":"20" + }, + { + "id":"commons-collections:commons-collections:3.2.2", + "start-order":"20" + }, + { + "id":"javax.mail:mail:1.5.0-b01", + "start-order":"20" + }, + { + "id":"org.apache.commons:commons-collections4:4.2", + "start-order":"20" + }, + { + "id":"org.apache.commons:commons-math:2.2", + "start-order":"20" + }, + { + "id":"org.apache.geronimo.bundles:jstl:1.2_1", + "start-order":"20" + }, + { + "id":"org.apache.httpcomponents:httpclient-osgi:4.5.6", + "start-order":"20" + }, + { + "id":"org.apache.httpcomponents:httpcore-osgi:4.4.10", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.adapter:2.1.10", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.auth.form:1.0.14", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.bundleresource.impl:2.3.2", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.classloader:1.4.4", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.compiler:2.3.6", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.fsclassloader:1.0.10", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.mime:2.2.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.osgi:2.4.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.scheduler:2.7.2", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.threads:3.2.18", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.engine:2.6.18", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.fsresource:2.1.14", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.i18n:2.5.14", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.installer.console:1.0.2", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.installer.hc:2.0.2", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.installer.provider.jcr:3.1.26", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.contentloader:2.3.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.resource:3.0.18", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.models.api:1.3.8", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.models.impl:1.4.10", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.resourceresolver:1.6.8", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.serviceuser.webconsole:1.0.0", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.serviceusermapper:1.4.4", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.servlets.get:2.1.40", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.servlets.post:2.3.30", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.servlets.resolver:2.5.2", + "start-order":"20" + }, + { + "id":"org.apache.sling:org.apache.sling.xss:2.1.8", + "start-order":"20" + }, + { + "id":"org.apache.felix:org.apache.felix.metatype:1.2.2", + "start-order":"4" + }, + { + "id":"org.apache.felix:org.apache.felix.scr:2.1.16", + "start-order":"4" + }, + { + "id":"commons-fileupload:commons-fileupload:1.3.3", + "start-order":"5" + }, + { + "id":"commons-io:commons-io:2.6", + "start-order":"5" + }, + { + "id":"org.apache.aries.jmx:org.apache.aries.jmx.api:1.1.5", + "start-order":"5" + }, + { + "id":"org.apache.aries.jmx:org.apache.aries.jmx.core:1.1.8", + "start-order":"5" + }, + { + "id":"org.apache.aries.jmx:org.apache.aries.jmx.whiteboard:1.2.0", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.bundlerepository:2.0.10", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.http.whiteboard:4.0.0", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.inventory:1.0.6", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.prefs:1.1.0", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.webconsole.plugins.ds:2.1.0", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.webconsole.plugins.event:1.1.8", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.webconsole.plugins.memoryusage:1.0.8", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.webconsole.plugins.obr:1.0.4", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.webconsole.plugins.packageadmin:1.0.4", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.webconsole:4.3.8", + "start-order":"5" + }, + { + "id":"org.apache.sling:org.apache.sling.api:2.20.0", + "start-order":"5" + }, + { + "id":"org.apache.sling:org.apache.sling.auth.core:1.4.2", + "start-order":"5" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.johnzon:1.1.2", + "start-order":"5" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.log.webconsole:1.0.0", + "start-order":"5" + }, + { + "id":"org.apache.sling:org.apache.sling.extensions.threaddump:0.2.2", + "start-order":"5" + }, + { + "id":"org.apache.sling:org.apache.sling.extensions.webconsolebranding:1.0.2", + "start-order":"5" + }, + { + "id":"org.apache.sling:org.apache.sling.extensions.webconsolesecurityprovider:1.2.2", + "start-order":"5" + }, + { + "id":"org.apache.sling:org.apache.sling.starter.content:1.0.4", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.http.sslfilter:1.2.6", + "start-order":"10" + }, + { + "id":"org.apache.pdfbox:fontbox:2.0.16", + "start-order":"10" + }, + { + "id":"org.apache.pdfbox:jempbox:1.8.16", + "start-order":"10" + }, + { + "id":"org.apache.pdfbox:pdfbox:2.0.16", + "start-order":"10" + }, + { + "id":"org.apache.tika:tika-core:1.21", + "start-order":"10" + }, + { + "id":"org.apache.tika:tika-parsers:1.21", + "start-order":"10" + }, + { + "id":"com.google.guava:guava:15.0", + "start-order":"15" + }, + { + "id":"io.dropwizard.metrics:metrics-core:3.2.6", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:jackrabbit-api:2.18.2", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:jackrabbit-data:2.18.2", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:jackrabbit-jcr-commons:2.18.2", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:jackrabbit-jcr-rmi:2.18.2", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:jackrabbit-spi-commons:2.18.2", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:jackrabbit-spi:2.18.2", + "start-order":"15" + }, + { + "id":"org.apache.jackrabbit:jackrabbit-webdav:2.18.2", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.commons.metrics:1.2.6", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.api:2.4.0", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.base:3.0.6", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.davex:1.3.10", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.jackrabbit.accessmanager:3.0.4", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.jackrabbit.usermanager:2.2.8", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.jcr-wrapper:2.0.0", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.registration:1.0.6", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.webconsole:1.0.2", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.webdav:2.3.8", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.resource.filter:1.0.0", + "start-order":"15" + }, + { + "id":"org.apache.sling:org.apache.sling.sample.slingshot:0.9.0", + "start-order":"20" + }, + { + "id":"org.apache.felix:org.apache.felix.http.jetty:4.0.8", + "run-modes":":standalone", + "start-order":"5" + }, + { + "id":"org.apache.felix:org.apache.felix.http.servlet-api:1.1.2", + "run-modes":":standalone", + "start-order":"5" + }, + { + "id":"org.apache.sling:org.apache.sling.jcr.packageinit:0.0.1-SNAPSHOT", + "start-order":"20" + } + ], + "configurations":{ + "org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment~composum":{ + "whitelist.bundles":[ + "com.composum.core.commons", + "com.composum.core.pckgmgr", + "com.composum.core.pckginstall" + ], + "whitelist.name":"composum" + }, + "org.apache.felix.hc.generalchecks.BundlesStartedCheck":{ + "hc.tags":[ + "bundles" + ] + }, + "org.apache.felix.hc.generalchecks.CpuCheck":{ + "hc.tags":[ + "cpu", + "system-resources" + ], + "cpuPercentageThresholdWarn":95 + }, + "org.apache.felix.hc.generalchecks.DiskSpaceCheck":{ + "hc.tags":[ + "diskspace", + "system-resources" + ], + "diskPaths":[ + "." + ] + }, + "org.apache.felix.hc.generalchecks.FrameworkStartCheck":{ + "hc.tags":[ + "systemalive" + ], + "targetStartLevel:Integer":"30" + }, + "org.apache.felix.hc.generalchecks.MemoryCheck":{ + "hc.tags":[ + "memory", + "system-resources" + ], + "heapUsedPercentageThresholdCritical":100, + "heapUsedPercentageThresholdWarn":95 + }, + "org.apache.felix.hc.generalchecks.ServicesCheck":{ + "hc.tags":[ + "systemalive" + ], + "services.list":[ + "org.apache.sling.jcr.api.SlingRepository", + "org.apache.sling.engine.auth.Authenticator", + "org.apache.sling.api.resource.ResourceResolverFactory", + "org.apache.sling.api.servlets.ServletResolver", + "javax.script.ScriptEngineManager" + ] + }, + "org.apache.felix.hc.generalchecks.ThreadUsageCheck":{ + "hc.tags":[ + "threads", + "cpu", + "system-resources" + ] + }, + "org.apache.felix.hc.core.impl.filter.ServiceUnavailableFilter~startupandshutdown":{ + "osgi.http.whiteboard.filter.regex":"(?!/system/).*", + "avoid404DuringStartup":true, + "service.ranking:Integer":"2147483647", + "includeExecutionResult":false, + "osgi.http.whiteboard.context.select":"(osgi.http.whiteboard.context.name=*)", + "tags":[ + "systemalive" + ], + "autoDisableFilter":true, + "responseTextFor503":"classpath:org.apache.sling.starter.content:content/content/startup/index.html" + }, + "org.apache.felix.hc.core.impl.servlet.HealthCheckExecutorServlet~default":{ + "servletPath":"/system/health" + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~hc-support":{ + "user.mapping":[ + "org.apache.sling.hc.support=sling-readall" + ] + }, + "org.apache.felix.jaas.ConfigurationSpi":{ + "jaas.defaultRealmName":"jackrabbit.oak", + "jaas.configProviderName":"FelixJaasProvider" + }, + "org.apache.jackrabbit.oak.security.authentication.AuthenticationConfigurationImpl":{ + "org.apache.jackrabbit.oak.authentication.configSpiName":"FelixJaasProvider" + }, + "org.apache.jackrabbit.oak.security.user.RandomAuthorizableNodeName":{ + "length:Integer":"21" + }, + "org.apache.jackrabbit.oak.security.user.UserConfigurationImpl":{ + "groupsPath":"/home/groups", + "defaultDepth":"1", + "importBehavior":"besteffort", + "usersPath":"/home/users" + }, + "org.apache.jackrabbit.oak.spi.security.user.action.DefaultAuthorizableActionProvider":{ + "userPrivilegeNames":[ + "jcr:all" + ], + "groupPrivilegeNames":[ + "jcr:read" + ], + "enabledActions":[ + "org.apache.jackrabbit.oak.spi.security.user.action.AccessControlAction" + ] + }, + "org.apache.felix.jaas.Configuration.factory~GuestLoginModule":{ + "jaas.controlFlag":"optional", + "jaas.classname":"org.apache.jackrabbit.oak.spi.security.authentication.GuestLoginModule", + "jaas.ranking:Integer":"300" + }, + "org.apache.felix.jaas.Configuration.factory~LoginModuleImpl":{ + "jaas.controlFlag":"required", + "jaas.classname":"org.apache.jackrabbit.oak.security.authentication.user.LoginModuleImpl" + }, + "org.apache.felix.jaas.Configuration.factory~TokenLoginModule":{ + "jaas.controlFlag":"sufficient", + "jaas.classname":"org.apache.jackrabbit.oak.security.authentication.token.TokenLoginModule", + "jaas.ranking:Integer":"200" + }, + "org.apache.jackrabbit.oak.segment.SegmentNodeStoreService":{ + "name":"Default NodeStore" + }, + "org.apache.sling.scripting.core.impl.ScriptCacheImpl":{ + "org.apache.sling.scripting.cache.additional_extensions":[ + "js" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~scripting":{ + "user.mapping":[ + "org.apache.sling.scripting.core=sling-scripting", + "org.apache.sling.scripting.sightly.js.provider=sling-scripting" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~sling-caconfig":{ + "user.mapping":[ + "org.apache.sling.caconfig.impl=sling-readall" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~sling.discovery":{ + "user.mapping":[ + "org.apache.sling.discovery.commons=sling-discovery", + "org.apache.sling.discovery.base=sling-discovery", + "org.apache.sling.discovery.oak=sling-discovery" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~sling.event":{ + "user.mapping":[ + "org.apache.sling.event=sling-event", + "org.apache.sling.event.dea=sling-event" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~validation":{ + "user.mapping":[ + "org.apache.sling.validation.core=sling-validation" + ] + }, + "org.apache.sling.commons.log.LogManager":{ + "org.apache.sling.commons.log.packagingDataEnabled":true, + "org.apache.sling.commons.log.pattern":"%d{dd.MM.yyyy HH:mm:ss.SSS} *%level* [%thread] %logger %msg%n", + "org.apache.sling.commons.log.level":"info", + "org.apache.sling.commons.log.file":"logs/error.log", + "org.apache.sling.commons.log.file.number:Integer":"7", + "org.apache.sling.commons.log.file.size":"'.'yyyy-MM-dd" + }, + "org.apache.sling.engine.impl.log.RequestLogger":{ + "access.log.enabled":true, + "request.log.outputtype:Integer":"0", + "access.log.output":"log.access", + "request.log.output":"log.request", + "request.log.enabled":true, + "access.log.outputtype:Integer":"0" + }, + "org.apache.sling.jcr.davex.impl.servlets.SlingDavExServlet":{ + "alias":"/server" + }, + "org.apache.sling.jcr.webdav.impl.servlets.SimpleWebDavServlet":{ + "dav.root":"/dav" + }, + "org.apache.sling.commons.log.LogManager.factory.config~access.log":{ + "org.apache.sling.commons.log.pattern":"%msg%n", + "org.apache.sling.commons.log.names":[ + "log.access" + ], + "org.apache.sling.commons.log.level":"info", + "org.apache.sling.commons.log.file":"logs/access.log" + }, + "org.apache.sling.commons.log.LogManager.factory.config~request.log":{ + "org.apache.sling.commons.log.pattern":"%msg%n", + "org.apache.sling.commons.log.names":[ + "log.request" + ], + "org.apache.sling.commons.log.level":"info", + "org.apache.sling.commons.log.file":"logs/request.log" + }, + "org.apache.sling.jcr.base.internal.LoginAdminWhitelist.fragment~sling":{ + "whitelist.bundles":[ + "org.apache.sling.discovery.commons", + "org.apache.sling.discovery.base", + "org.apache.sling.discovery.oak", + "org.apache.sling.extensions.webconsolesecurityprovider", + "org.apache.sling.i18n", + "org.apache.sling.jcr.base", + "org.apache.sling.jcr.contentloader", + "org.apache.sling.jcr.jackrabbit.usermanager", + "org.apache.sling.jcr.oak.server", + "org.apache.sling.jcr.repoinit", + "org.apache.sling.jcr.webconsole", + "org.apache.sling.servlets.post", + "org.apache.sling.serviceuser.webconsole" + ], + "whitelist.name":"sling" + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~i18n":{ + "user.mapping":[ + "org.apache.sling.i18n=sling-i18n" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~jcr-install":{ + "user.mapping":[ + "org.apache.sling.installer.provider.jcr=sling-jcr-install" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~jcr-resource":{ + "user.mapping":[ + "org.apache.sling.jcr.resource:validation=sling-readall" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~observation":{ + "user.mapping":[ + "org.apache.sling.jcr.resource:observation=sling-readall" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~resourceresolver":{ + "user.mapping":[ + "org.apache.sling.resourceresolver:mapping=sling-mapping", + "org.apache.sling.resourceresolver:hierarchy=sling-readall", + "org.apache.sling.resourceresolver:observation=sling-readall", + "org.apache.sling.resourceresolver:console=sling-readall" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~servletsresolver":{ + "user.mapping":[ + "org.apache.sling.servlets.resolver:console=sling-readall", + "org.apache.sling.servlets.resolver:scripts=sling-scripting" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~xss":{ + "user.mapping":[ + "org.apache.sling.xss=sling-xss" + ] + }, + "org.apache.sling.serviceusermapping.impl.ServiceUserMapperImpl.amended~sling.slingshot":{ + "user.mapping":[ + "org.apache.sling.sample.slingshot=slingshot-service" + ] + } + }, + "framework-properties":{ + "sling.run.mode.install.options":"oak_tar,oak_mongo", + "sling.jre.java.xml":",javax.xml;version=\"2.1.0\",javax.xml.datatype;uses:=\"javax.xml.namespace\";version=\"2.1.0\",javax.xml.namespace;version=\"2.1.0\",javax.xml.parsers;uses:=\"javax.xml.validation,org.w3c.dom,org.xml.sax,org.xml.sax.helpers\";version=\"2.1.0\",javax.xml.stream;uses:=\"javax.xml.namespace,javax.xml.stream.events,javax.xml.stream.util,javax.xml.transform\";version=\"1.0.0\",javax.xml.stream.events;uses:=\"javax.xml.namespace,javax.xml.stream\";version=\"1.0.0\",j [...] + "felix.systempackages.calculate.uses":"true", + "localIndexDir":"${sling.home}/repository/index", + "org.osgi.framework.system.packages":"org.osgi.framework;version=\"1.9\",org.osgi.framework.dto;version=\"1.8\";uses:=\"org.osgi.dto\",org.osgi.framework.hooks.bundle;version=\"1.1\";uses:=\"org.osgi.framework\",org.osgi.framework.hooks.resolver;version=\"1.0\";uses:=\"org.osgi.framework.wiring\",org.osgi.framework.hooks.service;version=\"1.1\";uses:=\"org.osgi.framework\",org.osgi.framework.hooks.weaving;version=\"1.1\";uses:=\"org.osgi.framework.wiring\",org.osgi.framework.launch;v [...] + "repository.home":"${sling.home}/repository", + "felix.systempackages.substitution":"true", + "sling.jre-jpms":"{dollar}{felix.jpms.java.base}{dollar}{felix.jpms.java.compiler}{dollar}{felix.jpms.java.datatransfer}{dollar}{felix.jpms.java.desktop}{dollar}{felix.jpms.java.instrument}{dollar}{felix.jpms.java.logging}{dollar}{felix.jpms.java.management}{dollar}{felix.jpms.java.management.rmi}{dollar}{felix.jpms.java.naming}{dollar}{felix.jpms.java.net.http}{dollar}{felix.jpms.java.prefs}{dollar}{felix.jpms.java.rmi}{dollar}{felix.jpms.java.scripting}{dollar}{felix.jpms.java.se}{ [...] + "sling.jpms.java.xml":"{dollar}{sling.jre.java.xml},javax.xml.catalog;uses:=\"javax.xml.namespace\";version=\"1.0.0\"", + "sling.jre-1.8":",java.applet;version=\"{dollar}{felix.detect.java.version}\",java.awt;version=\"{dollar}{felix.detect.java.version}\",java.awt.color;version=\"{dollar}{felix.detect.java.version}\",java.awt.datatransfer;version=\"{dollar}{felix.detect.java.version}\",java.awt.dnd;version=\"{dollar}{felix.detect.java.version}\",java.awt.event;version=\"{dollar}{felix.detect.java.version}\",java.awt.font;version=\"{dollar}{felix.detect.java.version}\",java.awt.geom;version=\"{dollar}{f [...] + }, + "assembled-features:ARTIFACTS|TRANSIENT":[ + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:boot:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:composum_composum-nodes:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:healthcheck:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:launchpad:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:oak:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:repoinit:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:scripting_sling:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:sling-caconfig:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:sling-discovery:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:sling-event:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:sling-models-jacksonexporter_models-jacksonexporter:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:sling-validation:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:sling:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:sling_slingshot:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:standalone:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.feature.model.starter:slingfeature:webapp:12-SNAPSHOT", + "org.apache.sling:org.apache.sling.jcr.packageinit:slingosgifeature:jcr-packageinit:0.0.1-SNAPSHOT" + ], + "repoinit:TEXT|true":[ + "# general", + "create path (sling:OrderedFolder) /content", + "set ACL for everyone", + "allow jcr:read\ton /content", + "end", + "", + "# sling-mapping", + "create service user sling-mapping", + "", + "set ACL for sling-mapping", + "allow jcr:read on /", + "end", + "", + "# sling-readall", + "create service user sling-readall", + "", + "set ACL for sling-readall", + "allow jcr:read on /", + "end", + "", + "# sling-xss", + "create service user sling-xss", + "", + "create path (sling:Folder) /apps/sling/xss", + "", + "set ACL for sling-xss", + "allow jcr:read on /apps/sling/xss", + "end", + "", + "# sling-i18n", + "create service user sling-i18n", + "", + "set ACL for sling-i18n", + "allow jcr:read on /", + "end", + "", + "# sling-jcr-install", + "create service user sling-jcr-install", + "", + "# used for config OSGi writeback", + "create path (sling:Folder) /apps/sling/install", + "", + "set ACL for sling-jcr-install", + "allow\tjcr:read\ton\t/", + "allow\trep:write\ton /apps/sling/install", + "end", + "", + "#<<< SLING-5848 - Define service user and ACLs for Scripting", + "create service user sling-scripting", + "", + "create path (sling:Folder) /libs", + "create path (sling:Folder) /apps", + "", + "set ACL for sling-scripting", + "deny jcr:all on /", + "allow jcr:read on /libs,/apps", + "end", + "# SLING-5848 - Define service user and ACLs for Scripting >>>", + "", + "create path (sling:Folder) /conf", + "", + "create service user sling-discovery", + "", + "create path (sling:Folder) /var/discovery", + "create path (sling:Folder) /var/discovery/oak", + "", + "set ACL for sling-discovery", + "allow jcr:read,rep:write on /var/discovery", + "end", + "", + "create service user sling-event", + "", + "create path (sling:Folder) /var", + "create path (sling:Folder) /var/eventing", + "", + "set ACL for sling-event", + "allow jcr:read,rep:write on /var/eventing", + "end", + "", + "create service user sling-validation", + "", + "create path (sling:Folder) /apps", + "create path (sling:Folder) /libs", + "", + "set ACL for sling-validation", + "allow jcr:read on /apps", + "allow jcr:read on /libs", + "end", + "", + "create service user slingshot-service", + "create user slingshot1 with password slingshot1", + "create user slingshot2 with password slingshot2", + "", + "create path (sling:Folder) /content/slingshot", + "create path (sling:Folder) /content/slingshot/users", + "create path (sling:Folder) /content/slingshot/users/slingshot1", + "create path (sling:Folder) /content/slingshot/users/slingshot2", + "", + "set ACL for slingshot-service", + "allow jcr:read,rep:write on /content/slingshot", + "end", + "", + "set ACL for slingshot1", + "allow jcr:read,rep:write on /content/slingshot/users/slingshot1", + "end", + "", + "set ACL for slingshot2", + "allow jcr:read,rep:write on /content/slingshot/users/slingshot2", + "end" + ] +} \ No newline at end of file diff --git a/src/test/java/org/apache/sling/feature/starter/it/LaunchpadReadyRule.java b/src/test/java/org/apache/sling/feature/starter/it/LaunchpadReadyRule.java new file mode 100644 index 0000000..f82bc3a --- /dev/null +++ b/src/test/java/org/apache/sling/feature/starter/it/LaunchpadReadyRule.java @@ -0,0 +1,121 @@ +/* + * 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.feature.starter.it; + +import java.io.BufferedReader; +import java.io.InputStreamReader; +import java.net.ConnectException; +import java.util.ArrayList; +import java.util.List; + +import org.apache.http.HttpResponse; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClients; +import org.junit.rules.ExternalResource; + +public class LaunchpadReadyRule extends ExternalResource { + + private static final int TRIES = 60; + private static final int WAIT_BETWEEN_TRIES_MILLIS = 1000; + + private final List<Check> checks = new ArrayList<>(); + + public LaunchpadReadyRule(int launchpadPort) { + + checks.add(new Check("http://localhost:" + launchpadPort + "/server/default/jcr:root/content")); + checks.add(new Check("http://localhost:" + launchpadPort + "/starter/index.html") { + @Override + public String runCheck(HttpResponse response) throws Exception { + try (InputStreamReader isr = new InputStreamReader(response.getEntity().getContent()); + BufferedReader reader = new BufferedReader(isr)) { + + String line; + while ((line = reader.readLine()) != null) { + if (line.contains("Do not remove this comment, used for Starter integration tests")) { + return null; + } + } + } + + return "Did not find 'ready' marker in the response body"; + } + }); + } + + @Override + protected void before() throws Throwable { + + try (CloseableHttpClient client = HttpClients.createDefault()) { + for (Check check : checks) { + runCheck(client, check); + } + } + } + + private void runCheck(CloseableHttpClient client, Check check) throws Exception { + + String lastFailure = null; + HttpGet get = new HttpGet(check.getUrl()); + + for (int i = 0; i < TRIES; i++) { + try (CloseableHttpResponse response = client.execute(get)) { + + if (response.getStatusLine().getStatusCode() != 200) { + lastFailure = "Status code is " + response.getStatusLine(); + Thread.sleep(WAIT_BETWEEN_TRIES_MILLIS); + continue; + } + + lastFailure = check.runCheck(response); + if (lastFailure == null) { + return; + } + } catch ( ConnectException e ) { + lastFailure = e.getClass().getName() + " : " + e.getMessage(); + } + + Thread.sleep(WAIT_BETWEEN_TRIES_MILLIS); + } + + throw new RuntimeException(String.format("Launchpad not ready. Failed check for URL %s with message '%s'", + check.getUrl(), lastFailure)); + } + + static class Check { + private String url; + + public Check(String url) { + this.url = url; + } + + public String getUrl() { + return url; + } + + /** + * @param response the HttpResponse + * @return null if check check was successful, an error description otherwise + * @throws Exception + */ + public String runCheck(HttpResponse response) throws Exception { + return null; + } + } + +} diff --git a/src/test/java/org/apache/sling/feature/starter/it/SmokeIT.java b/src/test/java/org/apache/sling/feature/starter/it/SmokeIT.java new file mode 100644 index 0000000..785e29e --- /dev/null +++ b/src/test/java/org/apache/sling/feature/starter/it/SmokeIT.java @@ -0,0 +1,195 @@ +/* + * 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.feature.starter.it; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.hamcrest.CoreMatchers.notNullValue; +import static org.junit.Assert.assertThat; +import static org.junit.Assert.fail; + +import java.util.List; +import java.util.Map; + +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; + +import org.apache.felix.utils.json.JSONParser; +import org.apache.http.Header; +import org.apache.http.HttpHost; +import org.apache.http.auth.AuthScope; +import org.apache.http.auth.UsernamePasswordCredentials; +import org.apache.http.client.CredentialsProvider; +import org.apache.http.client.methods.CloseableHttpResponse; +import org.apache.http.client.methods.HttpGet; +import org.apache.http.client.protocol.HttpClientContext; +import org.apache.http.impl.auth.BasicScheme; +import org.apache.http.impl.client.BasicAuthCache; +import org.apache.http.impl.client.BasicCredentialsProvider; +import org.apache.http.impl.client.CloseableHttpClient; +import org.apache.http.impl.client.HttpClientBuilder; +import org.hamcrest.CoreMatchers; +import org.junit.Before; +import org.junit.ClassRule; +import org.junit.Test; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; + +public class SmokeIT { + + private static final int LAUNCHPAD_PORT = Integer.getInteger("launchpad.http.port", 8080); + private static final int EXPECTED_BUNDLES_COUNT = Integer.getInteger("IT.expected.bundles.count", Integer.MAX_VALUE); + + @ClassRule + public static LaunchpadReadyRule LAUNCHPAD = new LaunchpadReadyRule(LAUNCHPAD_PORT); + private HttpClientContext httpClientContext; + + @Before + public void prepareHttpContext() { + + CredentialsProvider credsProvider = new BasicCredentialsProvider(); + UsernamePasswordCredentials creds = new UsernamePasswordCredentials("admin", "admin"); + credsProvider.setCredentials(new AuthScope("localhost", LAUNCHPAD_PORT), creds); + + BasicAuthCache authCache = new BasicAuthCache(); + BasicScheme basicAuth = new BasicScheme(); + authCache.put(new HttpHost("localhost", LAUNCHPAD_PORT, "http"), basicAuth); + + httpClientContext = HttpClientContext.create(); + httpClientContext.setCredentialsProvider(credsProvider); + httpClientContext.setAuthCache(authCache); + } + + private CloseableHttpClient newClient() { + + return HttpClientBuilder.create() + .setDefaultCredentialsProvider(httpClientContext.getCredentialsProvider()) + .build(); + } + + @Test + public void verifyAllBundlesStarted() throws Exception { + + try ( CloseableHttpClient client = newClient() ) { + + HttpGet get = new HttpGet("http://localhost:" + LAUNCHPAD_PORT + "/system/console/bundles.json"); + + // pass the context to ensure preemptive basic auth is used + // https://hc.apache.org/httpcomponents-client-ga/tutorial/html/authentication.html + try ( CloseableHttpResponse response = client.execute(get, httpClientContext) ) { + + if ( response.getStatusLine().getStatusCode() != 200 ) { + fail("Unexpected status line " + response.getStatusLine()); + } + + Header contentType = response.getFirstHeader("Content-Type"); + assertThat("Content-Type header", contentType.getValue(), CoreMatchers.startsWith("application/json")); + + Map<String, Object> obj = new JSONParser(response.getEntity().getContent()).getParsed(); + + @SuppressWarnings("unchecked") + List<Object> status = (List<Object>) obj.get("s"); + + @SuppressWarnings("unchecked") + List<Object> bundles = (List<Object>) obj.get("data"); + if(bundles.size() < EXPECTED_BUNDLES_COUNT) { + fail("Expected at least " + EXPECTED_BUNDLES_COUNT + " bundles, got " + bundles.size()); + } + + BundleStatus bs = new BundleStatus(status); + + if ( bs.resolvedBundles != 0 || bs.installedBundles != 0 ) { + + StringBuilder out = new StringBuilder(); + out.append("Expected all bundles to be active, but instead got ") + .append(bs.resolvedBundles).append(" resolved bundles, ") + .append(bs.installedBundles).append(" installed bundlles: "); + + for ( int i = 0 ; i < bundles.size(); i++ ) { + @SuppressWarnings("unchecked") + Map<String, Object> bundle = (Map<String, Object>) bundles.get(i); + + String bundleState = (String) bundle.get("state"); + String bundleSymbolicName = (String) bundle.get("symbolicName"); + String bundleVersion = (String) bundle.get("version"); + + switch ( bundleState ) { + case "Active": + case "Fragment": + continue; + + default: + out.append("\n- ").append(bundleSymbolicName).append(" ").append(bundleVersion).append(" is in state " ).append(bundleState); + } + } + + fail(out.toString()); + } + } + } + } + + @Test + public void ensureRepositoryIsStarted() throws Exception { + try ( CloseableHttpClient client = newClient() ) { + + HttpGet get = new HttpGet("http://localhost:" + LAUNCHPAD_PORT + "/server/default/jcr:root/content"); + + try ( CloseableHttpResponse response = client.execute(get) ) { + + if ( response.getStatusLine().getStatusCode() != 200 ) { + fail("Unexpected status line " + response.getStatusLine()); + } + + Header contentType = response.getFirstHeader("Content-Type"); + assertThat("Content-Type header", contentType.getValue(), equalTo("text/xml")); + + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + DocumentBuilder db = dbf.newDocumentBuilder(); + Document document = db.parse(response.getEntity().getContent()); + + Element docElement = document.getDocumentElement(); + NamedNodeMap attrs = docElement.getAttributes(); + + Node nameAttr = attrs.getNamedItemNS("http://www.jcp.org/jcr/sv/1.0", "name"); + assertThat("no 'name' attribute found", nameAttr, notNullValue()); + assertThat("Invalid name attribute value", nameAttr.getNodeValue(), equalTo("content")); + } + } + } + + static class BundleStatus { + + long totalBundles; + long activeBundles; + long activeFragments; + long resolvedBundles; + long installedBundles; + + public BundleStatus(List<Object> array) { + + totalBundles = (Long)array.get(0); + activeBundles = (Long)array.get(1); + activeFragments = (Long)array.get(2); + resolvedBundles = (Long)array.get(3); + installedBundles = (Long)array.get(4); + + } + } +} diff --git a/src/test/java/org/apache/sling/feature/starter/it/package-info.java b/src/test/java/org/apache/sling/feature/starter/it/package-info.java new file mode 100644 index 0000000..f70e5e8 --- /dev/null +++ b/src/test/java/org/apache/sling/feature/starter/it/package-info.java @@ -0,0 +1,30 @@ +/* + * 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. + */ +/** + * <h1>Smoke tests for the Sling Launchpad</h1> + * + * <p>This package contains a minimal set of tests for the Sling launchpad. The + * tests validate that the launchpad is correctly assembled and that there are + * no obvious mistakes such as bundles that can't be wired. More extensive + * tests must be placed in specific test projects.</p> + * + * <p>The launchpad tests don't depend on other Sling bundles,to prevent circular + * dependencies. As such, there is some duplication of code ( at least intent, if + * not implementation ) with some of the testing projects. This is another + * argument for keeping the tests minimal.</p> + */ +package org.apache.sling.launchpad; \ No newline at end of file
