/*
* Licensed 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.esigate.extension.parallelesi;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.esigate.HttpErrorPage;
import org.esigate.Parameters;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Ensure the multi-threaded esi execution.
*
* @author Nicolas Richeton
*
*/
public class ParallelEsiTest extends AbstractElementTest {
@Override
protected void setUp() {
super.setUp();
addResource("/test", "test");
}
/**
* This tests uses a blocking executor and ensure all includes are started in a new thread before running them.
*
* @throws IOException
* @throws HttpErrorPage
*/
public void testParallelInclude() throws IOException, HttpErrorPage {
// ESI executor
final BlockingExecutor exe = new BlockingExecutor();
setTested(new EsiRenderer(exe));
// Build page and expected result
StringBuilder expect = new StringBuilder(Parameters.DEFAULT_BUFFER_SIZE);
StringBuilder page = new StringBuilder(Parameters.DEFAULT_BUFFER_SIZE);
page.append("before ");
expect.append("before ");
for (int i = 0; i < 100; i++) {
page.append("<esi:include src=\"$(PROVIDER{mock})/test\" />");
expect.append("test");
}
page.append(" after");
expect.append(" after");
// This watches over the blocking executor and release execution when
// all includes are added.
Runnable r = new Runnable() {
@Override
public void run() {
boolean quit = false;
while (!quit) {
if (exe.getCount() == 100) {
exe.perform();
quit = true;
}
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
ExecutorService watchDog = Executors.newSingleThreadExecutor();
watchDog.execute(r);
String result = render(page.toString());
// close
watchDog.shutdown();
// Asserts
assertEquals(100, exe.getCount());
assertEquals(expect.toString(), result);
}
/**
* A blocking executor which will not start runnable unless {@link #perform()} is called.
*
* @author nricheto
*
*/
static class BlockingExecutor implements Executor {
private static final Logger LOG = LoggerFactory.getLogger(BlockingExecutor.class);
private List<Runnable> runnables = new ArrayList<>();
@Override
public void execute(Runnable command) {
LOG.info("Adding command {}", command);
this.runnables.add(command);
}
/**
* Start thread execution
*/
public void perform() {
Executor exe = Executors.newCachedThreadPool();
for (Runnable r : this.runnables) {
exe.execute(r);
}
}
/**
* Get current number of Runnable queued.
*
* @return current number of Runnable queued.
*/
public int getCount() {
return this.runnables.size();
}
}
}