/* * Copyright 2012-2016 the original author or authors. * * 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.glowroot.agent.ui.sandbox; import java.io.IOException; import java.util.Random; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; import java.util.concurrent.atomic.AtomicLong; import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.ning.http.client.AsyncHttpClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class ExpensiveCall { private static final Logger logger = LoggerFactory.getLogger(ExpensiveCall.class); private static final Random random = new Random(); private static final AsyncHttpClient asyncHttpClient = new AsyncHttpClient(); private final int maxTimeMillis; private final int maxTraceEntryMessageLength; ExpensiveCall(int maxTimeMillis, int maxTraceEntryMessageLength) { this.maxTimeMillis = maxTimeMillis; this.maxTraceEntryMessageLength = maxTraceEntryMessageLength; } void execute() throws Exception { int route = random.nextInt(11); switch (route) { case 0: execute0(); return; case 1: execute1(); return; case 2: execute2(); return; case 3: execute3(); return; case 4: execute4(); return; case 5: execute5(); return; case 6: execute6(); return; case 7: execute7(); return; case 8: execute8(); return; case 9: execute9(); return; default: new AsyncWrapperCall(maxTimeMillis, maxTraceEntryMessageLength).execute(); } } private void execute0() throws InterruptedException { expensive(); execute1(); } private void execute1() throws InterruptedException { expensive(); } private void execute2() throws InterruptedException { expensive(); execute3(); } private void execute3() throws InterruptedException { expensive(); } private void execute4() throws InterruptedException { expensive(); execute5(); try { asyncHttpClient.prepareGet("http://example.org").execute().get(); } catch (ExecutionException e) { logger.error(e.getMessage(), e); } catch (IOException e) { logger.error(e.getMessage(), e); } } private void execute5() throws InterruptedException { expensive(); } private void execute6() throws InterruptedException { expensive(); execute7(); } private void execute7() throws InterruptedException { expensive(); } private void execute8() throws InterruptedException { expensive(); execute9(); } private void execute9() throws InterruptedException { expensive(); } public String getTraceEntryMessage() { return getTraceEntryMessage(random.nextInt(5) > 0); } // this is just to prevent jvm from optimizing away for the loop below public static final AtomicLong dummy = new AtomicLong(); // need private static final Object lock = new Object(); static { ThreadFactory threadFactory = new ThreadFactoryBuilder().setDaemon(true).build(); Executors.newSingleThreadExecutor(threadFactory).submit(new Callable<Void>() { @Override public Void call() throws InterruptedException { // this loop is used to block threads executing expensive() below while (true) { synchronized (lock) { Thread.sleep(random.nextInt(10)); } Thread.sleep(1); } } }); } private void expensive() throws InterruptedException { int millis = random.nextInt(maxTimeMillis) / 4; // spend a quarter of the time taxing the cpu and doing memory allocation long start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < millis) { for (int i = 0; i < 100000; i++) { dummy.addAndGet(random.nextInt(1024)); if (i % 100 == 0) { dummy.addAndGet(new byte[random.nextInt(1024)].length); } } } // spend the rest of the time in both blocking and waiting states start = System.currentTimeMillis(); while (System.currentTimeMillis() - start < 3 * millis) { synchronized (lock) { Thread.sleep(random.nextInt(10)); dummy.incrementAndGet(); } Thread.sleep(1); } } private String getTraceEntryMessage(boolean spaces) { int traceEntryMessageLength = random.nextInt(maxTraceEntryMessageLength); StringBuilder sb = new StringBuilder(traceEntryMessageLength); for (int i = 0; i < traceEntryMessageLength; i++) { // random lowercase character sb.append((char) ('a' + random.nextInt(26))); if (spaces && random.nextInt(6) == 0 && i != 0 && i != traceEntryMessageLength - 1) { // on average, one of six characters will be a space (but not first or last char) sb.append(' '); } } return sb.toString(); } }