diff --git a/pom.xml b/pom.xml index 4bdc797..0fa5076 100644 --- a/pom.xml +++ b/pom.xml @@ -188,12 +188,12 @@ slf4j-api provided - - org.slf4j - slf4j-log4j12 - 1.7.30 - test - + + ch.qos.logback + logback-classic + 1.2.3 + test + javax.jcr jcr diff --git a/src/main/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializerFactory.java b/src/main/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializerFactory.java index 8d30330..731388a 100644 --- a/src/main/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializerFactory.java +++ b/src/main/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializerFactory.java @@ -16,6 +16,7 @@ */ package org.apache.sling.jcr.repoinit.impl; +import java.io.IOException; import java.io.StringReader; import java.util.Arrays; import java.util.List; @@ -28,6 +29,7 @@ import org.apache.sling.jcr.api.SlingRepositoryInitializer; import org.apache.sling.jcr.repoinit.JcrRepoInitOpsProcessor; import org.apache.sling.repoinit.parser.RepoInitParser; +import org.apache.sling.repoinit.parser.RepoInitParsingException; import org.apache.sling.repoinit.parser.operations.Operation; import org.osgi.framework.Constants; import org.osgi.service.component.annotations.Activate; @@ -73,6 +75,8 @@ public class RepositoryInitializerFactory implements SlingRepositoryInitializer String[] scripts() default {}; } + protected static final String PROPERTY_FAIL_ON_ERROR = "org.apache.sling.jcr.repoinit.failOnError"; + private final Logger log = LoggerFactory.getLogger(getClass()); @@ -87,7 +91,11 @@ public class RepositoryInitializerFactory implements SlingRepositoryInitializer @Activate public void activate(final RepositoryInitializerFactory.Config config) { this.config = config; - log.debug("Activated: {}", this.toString()); + if (continueOnError()) { + log.info("Activated: {} (continue on repoinit errors)", this); + } else { + log.debug("Activated: {}", this.toString()); + } } @Override @@ -99,43 +107,50 @@ public String toString() { @Override public void processRepository(final SlingRepository repo) throws Exception { - if ( (config.references() != null && config.references().length > 0) - || (config.scripts() != null && config.scripts().length > 0 )) { + // loginAdministrative is ok here, definitely an admin operation + final Session s = repo.loginAdministrative(null); + try { + executeScripts(s, config); + } catch (Exception e) { + if (continueOnError()) { + log.error("Repoinit error, won't stop execution because {} is set to non-true. Without this " + + "setting the startup would fail.",PROPERTY_FAIL_ON_ERROR,e); + } else { + throw (e); + } + } finally { + s.logout(); + } + } - // loginAdministrative is ok here, definitely an admin operation - final Session s = repo.loginAdministrative(null); - try { - if ( config.references() != null ) { - final RepoinitTextProvider p = new RepoinitTextProvider(); - for(final String reference : config.references()) { - if(reference == null || reference.trim().length() == 0) { - continue; - } - final String repoinitText = p.getRepoinitText("raw:" + reference); - final List ops = parser.parse(new StringReader(repoinitText)); - String msg = String.format("Executing %s repoinit operations", ops.size()); - log.info(msg); - applyOperations(s,ops,msg); - } + + protected void executeScripts (Session session, RepositoryInitializerFactory.Config config) throws IOException, RepositoryException, RepoInitParsingException { + if (config.references() != null) { + final RepoinitTextProvider p = new RepoinitTextProvider(); + for (final String reference : config.references()) { + if (reference == null || reference.trim().length() == 0) { + continue; } - if ( config.scripts() != null ) { - for(final String script : config.scripts()) { - if(script == null || script.trim().length() == 0) { - continue; - } - final List ops = parser.parse(new StringReader(script)); - String msg = String.format("Executing %s repoinit operations", ops.size()); - log.info(msg); - applyOperations(s,ops,msg); - } + final String repoinitText = p.getRepoinitText("raw:" + reference); + final List ops = parser.parse(new StringReader(repoinitText)); + String msg = String.format("Executing %s repoinit operations", ops.size()); + log.info(msg); + applyOperations(session, ops, msg); + } + } + if (config.scripts() != null) { + for (final String script : config.scripts()) { + if (script == null || script.trim().length() == 0) { + continue; } - } finally { - s.logout(); + final List ops = parser.parse(new StringReader(script)); + String msg = String.format("Executing %s repoinit operations", ops.size()); + log.info(msg); + applyOperations(session, ops, msg); } } } - /** * Apply the operations within a session, support retries * @param session the JCR session to use @@ -177,5 +192,10 @@ protected void applyOperations(Session session, List ops, String logM } } + + protected boolean continueOnError() { + String failOnError = System.getProperty(PROPERTY_FAIL_ON_ERROR,"true"); + return !failOnError.equals("true"); + } } diff --git a/src/test/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializerFactoryTest.java b/src/test/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializerFactoryTest.java index ac69d7c..37d5f04 100644 --- a/src/test/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializerFactoryTest.java +++ b/src/test/java/org/apache/sling/jcr/repoinit/impl/RepositoryInitializerFactoryTest.java @@ -16,25 +16,33 @@ */ package org.apache.sling.jcr.repoinit.impl; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.mock; + +import java.util.Properties; + +import javax.jcr.RepositoryException; +import javax.jcr.Session; + +import org.apache.sling.jcr.api.SlingRepository; import org.apache.sling.jcr.repoinit.JcrRepoInitOpsProcessor; +import org.apache.sling.jcr.repoinit.impl.util.LogCapture; import org.apache.sling.repoinit.parser.RepoInitParser; +import org.apache.sling.testing.mock.sling.ResourceResolverType; import org.apache.sling.testing.mock.sling.junit.SlingContext; import org.junit.Before; import org.junit.Rule; import org.junit.Test; - import org.mockito.ArgumentMatchers; +import org.mockito.Mockito; -import javax.jcr.RepositoryException; -import javax.jcr.Session; - -import static org.mockito.Mockito.doThrow; -import static org.mockito.Mockito.mock; +import ch.qos.logback.classic.Level; public class RepositoryInitializerFactoryTest { @Rule - public SlingContext context = new SlingContext(); + public SlingContext context = new SlingContext(ResourceResolverType.JCR_OAK); RepositoryInitializerFactory sut; JcrRepoInitOpsProcessor processor; @@ -56,4 +64,20 @@ public void handleUncheckedErrorsInOperations() throws RepositoryException { .when(processor).apply(ArgumentMatchers.any(), ArgumentMatchers.any()); sut.applyOperations(mock(Session.class), null, null); } + + @Test + public void validateDeveloperMode() throws Exception { + + Properties oldProps = System.getProperties(); + try (LogCapture capture = new LogCapture(RepositoryInitializerFactory.class.getName(),true);){ + System.setProperty(RepositoryInitializerFactory.PROPERTY_FAIL_ON_ERROR, "false"); + RepositoryInitializerFactory spy = Mockito.spy(sut); + doThrow(new RepositoryException("reason")).when(spy).executeScripts(any(Session.class),any(RepositoryInitializerFactory.Config.class)); + spy.processRepository(context.getService(SlingRepository.class)); + capture.assertContains(Level.ERROR, "Repoinit error, won't stop execution"); + } finally { + System.clearProperty(RepositoryInitializerFactory.PROPERTY_FAIL_ON_ERROR); + } + } + } diff --git a/src/test/java/org/apache/sling/jcr/repoinit/impl/util/LogCapture.java b/src/test/java/org/apache/sling/jcr/repoinit/impl/util/LogCapture.java new file mode 100644 index 0000000..6e59896 --- /dev/null +++ b/src/test/java/org/apache/sling/jcr/repoinit/impl/util/LogCapture.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.sling.jcr.repoinit.impl.util; + +import static org.junit.Assert.fail; + +import java.io.Closeable; +import java.io.IOException; +import java.util.function.Predicate; +import java.util.stream.Stream; + +import org.slf4j.LoggerFactory; + +import ch.qos.logback.classic.Level; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.read.ListAppender; + +/** Capture logs for testing */ +public class LogCapture extends ListAppender implements Closeable { + private final boolean verboseFailure; + + /** Setup the capture and start it */ + public LogCapture(String loggerName, boolean verboseFailure) { + this.verboseFailure = verboseFailure; + Logger logger = (Logger) LoggerFactory.getLogger(loggerName); + logger.setLevel(Level.ALL); + setContext((LoggerContext) LoggerFactory.getILoggerFactory()); + logger.addAppender(this); + start(); + } + + public boolean anyMatch(Predicate p) { + return this.list.stream().anyMatch(p); + } + + public void assertContains(Level atLevel, String ... substrings) { + Stream.of(substrings).forEach(substring -> { + if(!anyMatch(event -> event.getLevel() == atLevel && event.getFormattedMessage().contains(substring))) { + if(verboseFailure) { + fail(String.format("No log message contains [%s] in log\n%s", substring, this.list.toString())); + } else { + fail(String.format("No log message contains [%s]", substring)); + } + } + }); + } + + @Override + public void close() throws IOException { + stop(); + } +} \ No newline at end of file diff --git a/src/test/resources/log4j.properties b/src/test/resources/log4j.properties deleted file mode 100644 index 3060198..0000000 --- a/src/test/resources/log4j.properties +++ /dev/null @@ -1,24 +0,0 @@ -# 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. - -log4j.rootLogger=INFO, file -log4j.appender.file=org.apache.log4j.FileAppender -log4j.appender.file.File=target/testing.log -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d{dd.MM.yyyy HH:mm:ss} *%-5p* [%t] %c{1}: %m (%F, line %L)\n - -log4j.logger.org.apache.jackrabbit=WARN \ No newline at end of file diff --git a/src/test/resources/logback.xml b/src/test/resources/logback.xml new file mode 100644 index 0000000..9d7162a --- /dev/null +++ b/src/test/resources/logback.xml @@ -0,0 +1,33 @@ + + + + + + %d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n + + + + + + + + + + + + \ No newline at end of file