Author: dulvac Date: Tue May 16 15:03:12 2017 New Revision: 1795326 URL: http://svn.apache.org/viewvc?rev=1795326&view=rev Log: SLING-6853 Fixed incomplete patch.
Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/Polling.java sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/ sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/HttpServerRule.java sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/SlingClientDoGetJsonTest.java sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/SlingClientWaitExistsTest.java sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/util/ sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/util/poller/ sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/util/poller/PollingTest.java Added: sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/Polling.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/Polling.java?rev=1795326&view=auto ============================================================================== --- sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/Polling.java (added) +++ sling/trunk/testing/http/clients/src/main/java/org/apache/sling/testing/clients/util/poller/Polling.java Tue May 16 15:03:12 2017 @@ -0,0 +1,131 @@ +/* + * 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.testing.clients.util.poller; + +import org.apache.sling.testing.timeouts.TimeoutsProvider; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; + +/** + * Helper for repeating a call until it returns true, with timeout capabilities. + * Subclasses should override the {@link #call()} method. + * Can be used with lambda expressions, using the constructor {@link #Polling(Callable c)}. + * + * @since 1.1.0 + */ +public class Polling implements Callable<Boolean> { + + /** + * Optional object to be used by the default implementation of call() + */ + protected final Callable<Boolean> c; + + /** + * Holder for the last exception thrown by call(), to be used for logging + */ + protected Exception lastException; + + /** + * Default constructor to be used in subclasses that override the {@link #call()} method. + * Should not be used directly on {@code Polling} instances, but only on extended classes. + * If used directly to get a {@code Polling} instance, executing {@link #poll(long timeout, long delay)} + * will be equivalent to {@code Thread.sleep(timeout)} + */ + public Polling() { + this.c = null; + this.lastException = null; + } + + /** + * Creates a new instance that uses the {@code Callable} parameter for polling + * + * @param c object whose {@code call()} method will be polled + */ + public Polling(Callable<Boolean> c) { + this.c = c; + this.lastException = null; + } + + /** + * <p>Method to be called by {@link #poll(long timeout, long delay)}, potentially multiple times, + * until it returns true or timeout is reached.<br/> + * Subclasses can override it to change the check accordingly. The method should return true + * only when the call was successful.<br/> + * It can return false or throw any {@code Exception} to make the poller try again later.</p> + * + * <p>The default implementation delegates the call to the {@code Callable c} instance.</p> + * + * @return {@code true} to end polling + * @throws Exception if unable to compute a result + */ + @Override + public Boolean call() throws Exception { + if (c != null) { + return c.call(); + } else { + return false; + } + } + + /** + * <p>Tries to execute {@link #call()} until it returns true or until {@code timeout} is reached. + * Between retries, it waits using {@code Thread.sleep(delay)}. It means the retry is not at a fixed pace, + * but depends on the execution time of the call itself.</p> + * <p>The method guarantees that the call() will be executed at least once. If the timeout is 0 or less, then + * call() will be executed exactly once.</p> + * <p>The timeout is adjusted using {@link TimeoutsProvider} so the final value can be changed using the + * system property: {@value org.apache.sling.testing.timeouts.TimeoutsProvider#PROP_TIMEOUT_MULTIPLIER}</p> + * + * @param timeout max total execution time, in milliseconds + * @param delay time to wait between calls, in milliseconds + * + * @throws TimeoutException if {@code timeout} was reached + * @throws InterruptedException if the thread was interrupted while sleeping; caller should throw it further + */ + public void poll(long timeout, long delay) throws TimeoutException, InterruptedException { + long start = System.currentTimeMillis(); + long effectiveTimeout = TimeoutsProvider.getInstance().getTimeout(timeout); + + do { + try { + boolean success = call(); + if (success) { + return; + } + Thread.sleep(delay); + } catch (InterruptedException e) { + throw e; + } catch (Exception e) { + lastException = e; + } + } while (System.currentTimeMillis() < start + effectiveTimeout); + + throw new TimeoutException(String.format(message(), effectiveTimeout, delay)); + } + + /** + * Returns the string to be used in the {@code TimeoutException}, if needed. + * The string is passed to {@code String.format(message(), timeout, delay)}, so it can be a format + * including {@code %1$} and {@code %2$}. The field {@code lastException} is also available for logging + * + * @return the format string + */ + protected String message() { + return "Call failed to return true in %1$d ms. Last exception was: " + lastException; + } +} Added: sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/HttpServerRule.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/HttpServerRule.java?rev=1795326&view=auto ============================================================================== --- sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/HttpServerRule.java (added) +++ sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/HttpServerRule.java Tue May 16 15:03:12 2017 @@ -0,0 +1,84 @@ +/******************************************************************************* + * 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 + * <p> + * http://www.apache.org/licenses/LICENSE-2.0 + * <p> + * 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.testing.clients; + +import java.io.IOException; +import java.net.URI; +import java.util.concurrent.TimeUnit; + +import org.apache.http.HttpHost; +import org.apache.http.client.utils.URIUtils; +import org.apache.http.config.SocketConfig; +import org.apache.http.impl.bootstrap.HttpServer; +import org.apache.http.impl.bootstrap.ServerBootstrap; +import org.apache.http.localserver.SSLTestContexts; +import org.junit.rules.ExternalResource; + +/** JUnit Rule that starts an HTTP server */ +public class HttpServerRule extends ExternalResource { + public static final String ORIGIN = "TEST/1.1"; + private HttpServer server; + private HttpHost host; + private URI uri; + + protected ServerBootstrap serverBootstrap; + + public static enum ProtocolScheme { + http, + https; + private ProtocolScheme() { + } + } + + protected final ProtocolScheme protocolScheme; + + public HttpServerRule() { + this(ProtocolScheme.http); + } + + public HttpServerRule(ProtocolScheme protocolScheme) { + this.protocolScheme = protocolScheme; + } + + @Override + protected void after() { + server.shutdown(-1, TimeUnit.SECONDS); + } + + @Override + protected void before() throws Throwable { + final SocketConfig socketConfig = SocketConfig.custom().setSoTimeout(5000).build(); + serverBootstrap = ServerBootstrap.bootstrap().setSocketConfig(socketConfig).setServerInfo(ORIGIN); + if(ProtocolScheme.https.equals(protocolScheme)) { + serverBootstrap.setSslContext(SSLTestContexts.createServerSSLContext()); + } + registerHandlers(); + server = serverBootstrap.create(); + server.start(); + host = new HttpHost("127.0.0.1", server.getLocalPort(), protocolScheme.name()); + uri = URIUtils.rewriteURI(new URI("/"), host); + } + + protected void registerHandlers() throws IOException { + } + + public URI getURI() { + return uri; + } +} \ No newline at end of file Added: sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/SlingClientDoGetJsonTest.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/SlingClientDoGetJsonTest.java?rev=1795326&view=auto ============================================================================== --- sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/SlingClientDoGetJsonTest.java (added) +++ sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/SlingClientDoGetJsonTest.java Tue May 16 15:03:12 2017 @@ -0,0 +1,71 @@ +/* + * 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.testing.clients; + +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.entity.StringEntity; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpRequestHandler; +import org.codehaus.jackson.JsonNode; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; + +import static org.junit.Assert.*; + +public class SlingClientDoGetJsonTest { + private static final String GET_JSON_PATH = "/test/json/resource"; + private static final String JSON_RESPONSE = "{\"jcr:primaryType\":\"cq:Page\",\"jcr:createdBy\":\"admin-json\"}"; + private static final String JSON_INF_RESPONSE = "{\"jcr:primaryType\":\"cq:Page\",\"jcr:createdBy\":\"admin-infinity\"}"; + + @ClassRule + public static HttpServerRule httpServer = new HttpServerRule() { + @Override + protected void registerHandlers() throws IOException { + serverBootstrap.registerHandler(GET_JSON_PATH + ".1.json", new HttpRequestHandler() { + @Override + public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { + response.setEntity(new StringEntity(JSON_RESPONSE)); + } + }); + + serverBootstrap.registerHandler(GET_JSON_PATH + ".infinity.json", new HttpRequestHandler() { + @Override + public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { + response.setEntity(new StringEntity(JSON_INF_RESPONSE)); + } + }); + } + }; + + @Test + public void testDoGetJson() throws Exception { + SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass"); + JsonNode res = c.doGetJson(GET_JSON_PATH, 1, 200); + assertEquals("admin-json", res.get("jcr:createdBy").getTextValue()); + } + + @Test + public void testDoGetJsonInfinity() throws Exception { + SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass"); + JsonNode res = c.doGetJson(GET_JSON_PATH, -1, 200); + assertEquals("admin-infinity", res.get("jcr:createdBy").getTextValue()); + } +} Added: sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/SlingClientWaitExistsTest.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/SlingClientWaitExistsTest.java?rev=1795326&view=auto ============================================================================== --- sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/SlingClientWaitExistsTest.java (added) +++ sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/SlingClientWaitExistsTest.java Tue May 16 15:03:12 2017 @@ -0,0 +1,93 @@ +/* + * 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.testing.clients; + +import org.apache.http.HttpException; +import org.apache.http.HttpRequest; +import org.apache.http.HttpResponse; +import org.apache.http.entity.StringEntity; +import org.apache.http.protocol.HttpContext; +import org.apache.http.protocol.HttpRequestHandler; +import org.junit.Assert; +import org.junit.ClassRule; +import org.junit.Test; + +import java.io.IOException; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.*; + +public class SlingClientWaitExistsTest { + private static final String GET_WAIT_PATH = "/test/wait/resource"; + private static final String OK_RESPONSE = "TEST_OK"; + private static final String NOK_RESPONSE = "TEST_OK"; + + private static int waitCount = 4; // truly randomly chosen by typing with the eyes closed + private static int callCount = 0; + + @ClassRule + public static HttpServerRule httpServer = new HttpServerRule() { + @Override + protected void registerHandlers() throws IOException { + serverBootstrap.registerHandler(GET_WAIT_PATH + ".json", new HttpRequestHandler() { + @Override + public void handle(HttpRequest request, HttpResponse response, HttpContext context) throws HttpException, IOException { + callCount++; + if (callCount == waitCount) { + response.setEntity(new StringEntity(OK_RESPONSE)); + } else { + response.setEntity(new StringEntity(NOK_RESPONSE)); + response.setStatusCode(404); + } + } + }); + } + }; + + @Test + public void testWaitExists() throws Exception { + callCount = 0; // reset counter + waitCount = 3; // less than timeout + SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass"); + c.waitExists(GET_WAIT_PATH, 500, 10); + assertEquals(waitCount, callCount); + } + + @Test + public void testWaitExistsTimeout() throws Exception { + callCount = 0; // reset counter + waitCount = 40; // to be sure we reach timeout + SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass"); + try { + c.waitExists(GET_WAIT_PATH, 200, 10); + } catch (TimeoutException e ) { + assertTrue("call was executed only " + callCount + " times", callCount > 3); + return; + } + + fail("waitExists did not timeout"); + } + + @Test + public void testWaitExistsOnce() throws Exception { + callCount = 0; // reset counter + waitCount = 1; // less than timeout + SlingClient c = new SlingClient(httpServer.getURI(), "user", "pass"); + c.waitExists(GET_WAIT_PATH, -1, 10); + assertEquals(1, callCount); + } +} Added: sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/util/poller/PollingTest.java URL: http://svn.apache.org/viewvc/sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/util/poller/PollingTest.java?rev=1795326&view=auto ============================================================================== --- sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/util/poller/PollingTest.java (added) +++ sling/trunk/testing/http/clients/src/test/java/org/apache/sling/testing/clients/util/poller/PollingTest.java Tue May 16 15:03:12 2017 @@ -0,0 +1,179 @@ +/* + * 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.testing.clients.util.poller; + +import org.apache.commons.lang3.mutable.MutableBoolean; +import org.apache.commons.lang3.mutable.MutableInt; +import org.junit.Test; + +import java.util.concurrent.Callable; +import java.util.concurrent.TimeoutException; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +public class PollingTest { + @Test + public void testCallOnce() throws Exception { + final MutableInt callCount = new MutableInt(0); + Polling p = new Polling() { + @Override + public Boolean call() throws Exception { + callCount.increment(); + return true; + } + }; + p.poll(500, 10); + + assertEquals(1, callCount.intValue()); + } + + @Test + public void testCallTwice() throws Exception { + final MutableInt callCount = new MutableInt(0); + final MutableBoolean called = new MutableBoolean(false); + Polling p = new Polling() { + @Override + public Boolean call() throws Exception { + callCount.increment(); + boolean b = called.booleanValue(); + called.setTrue(); + return b; + } + }; + p.poll(500, 10); + + assertEquals(2, callCount.intValue()); + } + + @Test + public void testCallTimeout() throws Exception { + final MutableInt callCount = new MutableInt(0); + Polling p = new Polling() { + @Override + public Boolean call() throws Exception { + callCount.increment(); + return false; + } + }; + + try { + p.poll(100, 10); + } catch (TimeoutException e ) { + assertTrue("Expected to execute call() at least 4 times, got instead only " + callCount.intValue() + " calls", + callCount.intValue() > 5); + return; + } + + fail("Did not reach timeout"); + } + + @Test + public void testNegativeTimeout() throws Exception { + final MutableInt callCount = new MutableInt(0); + Polling p = new Polling() { + @Override + public Boolean call() throws Exception { + callCount.increment(); + return true; + } + }; + p.poll(-1, 10); + + assertEquals(1, callCount.intValue()); + } + + // + // Tests with Callable + // + + @Test + public void testCallableOnce() throws Exception { + final MutableInt callCount = new MutableInt(0); + final MutableBoolean called = new MutableBoolean(false); + Polling p = new Polling(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + callCount.increment(); + return true; + } + }); + p.poll(500, 10); + + assertEquals(1, callCount.intValue()); + } + + @Test + public void testCallableTwice() throws Exception { + final MutableInt callCount = new MutableInt(0); + final MutableBoolean called = new MutableBoolean(false); + Polling p = new Polling(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + callCount.increment(); + boolean b = called.booleanValue(); + called.setTrue(); + return b; + } + }); + p.poll(500, 10); + + assertEquals(2, callCount.intValue()); + } + + @Test + public void testCallableTimeout() throws Exception { + final MutableInt callCount = new MutableInt(0); + Polling p = new Polling(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + callCount.increment(); + return false; + } + }); + + try { + p.poll(100, 10); + } catch (TimeoutException e ) { + assertTrue("Expected to execute call() at least 4 times, got instead only " + callCount.intValue() + " calls", + callCount.intValue() > 5); + return; + } + + fail("Did not reach timeout"); + } + + + @Test + public void testCallPriority() throws Exception { + Polling p = new Polling(new Callable<Boolean>() { + @Override + public Boolean call() throws Exception { + return false; + } + }) { + @Override + public Boolean call() throws Exception { + return true; + } + }; + + // Should not reach timeout since overridden call() has priority over Callable param + p.poll(100, 10); + } +} \ No newline at end of file