// Copyright 2014-2015 Boundary, Inc.
//
// 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 com.boundary.sdk.event;
// Create a whole load of events using many threads
import java.io.DataOutputStream;
//import java.io.InputStreamReader;
import java.net.Authenticator;
import java.net.HttpURLConnection;
import java.net.PasswordAuthentication;
import java.net.URL;
import java.util.Random;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
abstract class define {
public static final int THREADS = 30;
public static final int REPEATS = 20; // Each thread creates this number of events
public static final int UNIQUEMULTIPLIER = 100000000 ; // Every UNIQUEMULTIPLIER event will be de-duped in the thread. if this number is greater than repeats
// then there will be no de-duplication in the run.
public static final int MINIMUMRESPONSE = 500; // Milliseconds - rate dampener
public static final boolean DUMMY = false; // Run with no event creation?
public static final int MAINTHREADTIMEOUT = 120; // Seconds - used as a fail-safe. Main thread will stop all running threads on timeout.
public static final int COUNTDOWN = 3;
public static final int THREADMAX = 30; // Double key on the number of threads to prevent typos
public static final int RANDOMCRASH = -1; // Tests thread resiliency - any number between 0 and 4 will cause the thread to crash with a 1 in 5 probability
}
// Event Pump; pump out lots of events
class EventPump implements Runnable {
Thread t;
int myId;
CountDownLatch myLatch;
int myRepeats;
int myMinimumResponse;
int myCount = 0;
int myUniqueKey;
//boolean myUnique;
boolean myStop = false;
boolean myDummy = false;
EventPump(int id,CountDownLatch latch, int repeats, int minimumResponse, int uniqueKey, boolean dummy) {
// Create a new event pump
t = new Thread(this, "Event Pump");
//System.out.println(Long.toString(System.currentTimeMillis()) + " Thread # " + t.getId() + "\tCreated.\n");
myLatch = latch;
myRepeats = repeats;
myMinimumResponse = minimumResponse;
myDummy = dummy;
myId = id;
myUniqueKey = uniqueKey;
}
// This is the entry point for the thread
public void run() {
while (myCount < myRepeats & myStop == false) {
//System.out.println(Long.toString(System.currentTimeMillis()) + "\tThread:\t" + String.format("%1$" + 4 + "s", myId) + " Requesting work " + myRepeats +"\t" + myCount);
try {
doWork();
} catch (Exception e1) {
//e1.printStackTrace();
//System.out.println(Long.toString(System.currentTimeMillis()) + "\tThread:" + String.format("%1$" + 4 + "s", myId) + " Exception recovery from error: " +
//e1.getClass().toString() + " restarting thread at count: " + myCount);
System.err.println(Long.toString(System.currentTimeMillis()) + "\tThread:" + String.format("%1$" + 4 + "s", myId) + " Exception recovery from error: " +
e1.getClass().toString() + " restarting thread at count: " + myCount);
myCount --; // last count was unsuccessful
}
}
// We have finished - decrement the latch by 1
try {
myLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
private void doWork() throws Exception {
// String url = "https://api.boundary.com/7mKLvFvv2cZBhUpPQYlsg1oKEtC/events"; // 4FtQaHhAFejk0mhb1SJHKx8nzrL
String url = "https://api.boundary.com/4FtQaHhAFejk0mhb1SJHKx8nzrL/events";
int responseCode = 201;
HttpURLConnection con = null;
while (myCount < myRepeats && myStop == false) {
long now = System.currentTimeMillis();
myCount++; // persistent across exceptions
//System.out.println(System.currentTimeMillis() + "\tThread:" + String.format("%1$" + 4 + "s", myId) + " counters: " + myRepeats + " " + myCount);
if (!myDummy ) {
URL obj = new URL(url);
//System.out.println(System.currentTimeMillis() + "\tThread:" + String.format("%1$" + 4 + "s", myId) + " Http connection opening");
con = (HttpURLConnection) obj.openConnection();
//System.out.println(System.currentTimeMillis() + "\tThread:" + String.format("%1$" + 4 + "s", myId) + " Http connection now open");
// Testing code
if (define.RANDOMCRASH > 0) {
Random randomGenerator = new Random();
int randomInt = randomGenerator.nextInt(5);
// System.out.println(System.currentTimeMillis() + "\tThread:" + String.format("%1$" + 4 + "s", myId) + " random: " + randomInt);
if (randomInt == 3) {throw new Exception();}
}
// End of Testing code
con.setRequestMethod("POST");
con.setRequestProperty("User-Agent", "Mozilla/5.0");
con.setRequestProperty("Accept-Language", "en-US,en;q=0.5");
con.setRequestProperty("Content-Type" , "application/json" );
//con.setRequestProperty("Connection", "keep-alive");
con.setDoOutput(true);
String urlParameters = "{\"message\": \"Generated from XPS13\",\"tags\": [\"one\", \"two\", \"three\"]," +
"\"fingerprintFields\": [\"@title\"],\"source\": {\"ref\": \"Rivington\","
+ "\"type\": \"application\"},\"title\": \"Rivington Test Event " + myUniqueKey + "/" + myId + "/" + myCount%define.UNIQUEMULTIPLIER + "\"}" ;
//System.out.println(System.currentTimeMillis() + " " + urlParameters);
// Send post request
DataOutputStream wr = new DataOutputStream(con.getOutputStream());
wr.writeBytes(urlParameters);
wr.flush();
wr.close();
responseCode = con.getResponseCode();
}
if (responseCode == 201) {
long then = System.currentTimeMillis();
long elapsed = then - now;
System.out.println(System.currentTimeMillis() + "\tThread:" + String.format("%1$" + 4 + "s", myId) +
" New event\t" + myUniqueKey + "/" + myId + "/" + myCount%define.UNIQUEMULTIPLIER + "\t" + Long.toString(elapsed) + "\t(Ms) ") ;
if (elapsed < myMinimumResponse) {
try {
Thread.sleep(myMinimumResponse-elapsed);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
else {
System.out.println(System.currentTimeMillis() + "\tThread:" + String.format("%1$" + 4 + "s", myId) +
" Not the response code (201) that we wanted: " + responseCode);
if (responseCode == 429) {Thread.sleep(100);} // Server too busy so we should wait a short while
//System.exit(responseCode); /* Bad response code - give up */
myCount--; // And reset the persistent record counter
}
}
if (myStop) {
System.out.println(System.currentTimeMillis() + "\tThread:" + String.format("%1$" + 4 + "s", myId) +
" Terminated with STOP request");
}
else {
System.out.println(System.currentTimeMillis() + "\tThread:" + String.format("%1$" + 4 + "s", myId) +
" ended - " + myRepeats + " events created");
}
}
}
public class CreateEvents {
public static void main(String args[]) throws InterruptedException {
int threads = define.THREADS;
int repeats = define.REPEATS;
int minimumResponse = define.MINIMUMRESPONSE; // Milliseconds - used to slow down the threads pumping rate
int mainThreadTimeout = define.MAINTHREADTIMEOUT; // Seconds
boolean dummy = define.DUMMY; // Do everything but create an event
Authenticator.setDefault(new CustomAuthenticator());
for (int i=0;i<args.length;i++) {
System.out.println(args[i]);
// TODO: Able to compile on Java 6
// switch(args[i]) {
// case "-t": {
// threads = Integer.parseInt( args[i+1]);
// }
// case "-r": {
// repeats = Integer.parseInt( args[i+1]);
// }
// case "-m": {
// minimumResponse = Integer.parseInt( args[i+1]);
// }
// case "-w": {
// mainThreadTimeout = Integer.parseInt( args[i+1]);
// }
//
//
// }
}
if (threads > define.THREADMAX) {threads = define.THREADMAX;}
CountDownLatch latch = new CountDownLatch(threads);
boolean latchState = true;
System.out.println(System.currentTimeMillis() + "\tMain thread started and creating " + threads + " threads "
+ repeats + " repeats " + minimumResponse + " (Ms) minimum response. Main Thread Timeout " + mainThreadTimeout);
EventPump[] threadList = new EventPump[threads];
int uniqueKey = (int) System.currentTimeMillis()/1000 ;
for (int i = 0; i < threads; i++) {
threadList[i] = new EventPump(i,latch,repeats,minimumResponse,uniqueKey,dummy); // create a new thread but do not start
}
for (int i=define.COUNTDOWN; i>0; i--) {
System.out.println(System.currentTimeMillis() + "\tMain thread countdown: " + i);
Thread.sleep(1000);
}
long now = System.currentTimeMillis();
for (int i = 0; i < threads; i++) {
threadList[i].t.start(); // Start your engines!
}
System.out.println(System.currentTimeMillis() + "\tMain thread - all threads started. Waiting on latch.");
try {
latchState = latch.await(mainThreadTimeout, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
long then = System.currentTimeMillis();
if (latchState) {
System.out.println(then + "\tMain thread exiting. Run time for " + threads + " threads " +
repeats + " repeats: " + Long.toString(then-now) + " milliseconds.");
}
else {
System.out.println(then + "\tMain thread timed out waiting for child threads. " + Long.toString(then-now) + " milliseconds.");
for (int i=0; i < threads; i++) {
if (threadList[i].t.isAlive()) {
System.out.println(Long.toString(System.currentTimeMillis()) + " Closing down thread: " + i);
threadList[i].myStop = true;
//threadList[i].t.interrupt();
}
}
for (int i=0; i < threads;i++){
threadList[i].t.join(); // Make sure that all child threads stopped before stopping main thread
}
System.out.println(Long.toString(then) + "\tMain thread terminating after timeout. " + Long.toString(then-now) + " milliseconds.");
}
}
public static class CustomAuthenticator extends Authenticator {
// Called when password authorisation is needed
protected PasswordAuthentication getPasswordAuthentication() {
// Get information about the request
//String prompt = getRequestingPrompt();
//String hostname = getRequestingHost();
//InetAddress ipaddr = getRequestingSite();
//int port = getRequestingPort();
//System.out.println(System.currentTimeMillis() + "\tAuthentication requestor: "+ prompt +" " + hostname + " " + port);
String username = "E222tY6py4e9InofIEZOWkrjHDL";
String password = "";
// Return the information (a data holder that is used by Authenticator)
return new PasswordAuthentication(username, password.toCharArray());
}
}
}