requests-simulator
Changes
pom.xml 20(+20 -0)
Details
pom.xml 20(+20 -0)
diff --git a/pom.xml b/pom.xml
index 515e53c..f6b6ae0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,6 +10,26 @@
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
+ <dependencies>
+ <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpclient</artifactId>
+ <version>4.5.7</version>
+ </dependency>
+ <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpmime -->
+ <dependency>
+ <groupId>org.apache.httpcomponents</groupId>
+ <artifactId>httpmime</artifactId>
+ <version>4.5.7</version>
+ </dependency>
+ <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
+ <dependency>
+ <groupId>com.google.code.gson</groupId>
+ <artifactId>gson</artifactId>
+ <version>2.8.5</version>
+ </dependency>
+ </dependencies>
<build>
<plugins>
<plugin>
diff --git a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Main.java b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Main.java
index 1fb16fa..781ea77 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Main.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Main.java
@@ -18,22 +18,30 @@ public class Main {
public static void main(String[] args) {
System.setProperty("java.util.logging.SimpleFormatter.format", "[%1$tF %1$tT+%1$tL] [%4$-7s] [RequestsSimulator] %5$s %n");
- String path = null;
+ String profilePath = null;
+ long time = 0;
int users = 1;
- if (args.length < 1) {
- System.err.println("<RequestsPath> [<users>]");
+ if (args.length < 2) {
+ System.err.println("<ProfilePath> <time> [<users>]");
System.exit(1);
} else {
- path = args[0];
- if (args.length == 2) {
+ profilePath = args[0];
+ try {
+ time = Long.valueOf(args[1]);
+ } catch (NumberFormatException ex) {
+ System.err.println("<time> must be a number");
+ System.exit(1);
+ }
+ if (args.length == 3) {
try {
- users = Integer.valueOf(args[1]);
+ users = Integer.valueOf(args[2]);
} catch (NumberFormatException ex) {
System.err.println("<users> must be a number");
System.exit(1);
}
}
}
- Simulator.simulate(path, users);
+ Profile profile = ProfileReader.parseFile(profilePath);
+ Simulator.simulate(profile, time, users);
}
}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Profile.java b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Profile.java
new file mode 100644
index 0000000..3a0b866
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Profile.java
@@ -0,0 +1,30 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package br.ufrgs.inf.prosoft.requestssimulator;
+
+import br.ufrgs.inf.prosoft.requestssimulator.requests.RequestPlan;
+import java.util.Collection;
+
+/**
+ *
+ * @author romulo
+ */
+public class Profile {
+
+ private final Collection<RequestPlan> roots;
+
+ public Profile(Collection<RequestPlan> roots) {
+ this.roots = roots;
+ }
+
+ public void run(long time) {
+ long end = System.currentTimeMillis() + time;
+ while (System.currentTimeMillis() < end) {
+ Session session = new Session(this.roots);
+ session.run();
+ }
+ }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/ProfileReader.java b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/ProfileReader.java
new file mode 100644
index 0000000..5f006b0
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/ProfileReader.java
@@ -0,0 +1,59 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package br.ufrgs.inf.prosoft.requestssimulator;
+
+import br.ufrgs.inf.prosoft.requestssimulator.requests.RequestPlan;
+import com.google.gson.Gson;
+import com.google.gson.JsonObject;
+import com.google.gson.JsonParser;
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/**
+ *
+ * @author romulo
+ */
+public class ProfileReader {
+
+ public static Profile parseFile(String profilePath) {
+ String fileContent = null;
+ try (Stream<String> lines = Files.lines(Paths.get(profilePath))) {
+ fileContent = lines.collect(Collectors.joining());
+ } catch (IOException ex) {
+ Logger.getLogger(ProfileReader.class.getName()).log(Level.SEVERE, null, ex);
+ }
+ Map<String, RequestPlan> urlHasRequestPlan = new HashMap<>();
+ Gson gson = new Gson();
+ JsonParser jsonParser = new JsonParser();
+ JsonObject jsonObject = jsonParser.parse(fileContent).getAsJsonObject();
+ jsonObject.entrySet().forEach((entry) -> {
+ RequestPlan requestPlan = gson.fromJson(entry.getValue(), RequestPlan.class);
+ urlHasRequestPlan.put(entry.getKey(), requestPlan);
+ });
+ Map<String, RequestPlan> rootsReferences = new HashMap<>(urlHasRequestPlan);
+ urlHasRequestPlan.values().forEach(requestPlan -> {
+ requestPlan.linksReferences().forEach(linkReference -> {
+ RequestPlan reference = urlHasRequestPlan.get(linkReference);
+ if (reference == null) {
+ throw new RuntimeException("link not declared");
+ }
+ requestPlan.addLink(reference);
+ rootsReferences.remove(linkReference);
+ });
+ });
+ Collection<RequestPlan> roots = rootsReferences.values();
+ Profile profile = new Profile(roots);
+ return profile;
+ }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/GetRequest.java b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/GetRequest.java
new file mode 100644
index 0000000..ed586ca
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/GetRequest.java
@@ -0,0 +1,52 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package br.ufrgs.inf.prosoft.requestssimulator.requests;
+
+import java.io.IOException;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author romulo
+ */
+public class GetRequest extends Request {
+
+ protected GetRequest(RequestPlan requestPlan, String URL) {
+ super(requestPlan, URL);
+ }
+
+ protected GetRequest(RequestPlan requestPlan, String URL, String headers) {
+ super(requestPlan, URL, headers);
+ }
+
+ @Override
+ public void fire() {
+ try {
+ URL url = new URL(getURL());
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("GET");
+ getHeaders().forEach((entry) -> {
+ connection.setRequestProperty(entry.getKey(), entry.getValue());
+ });
+ int responseCode = connection.getResponseCode();
+ if (responseCode < 400) {
+ Logger.getGlobal().log(Level.INFO, "{0} {1}", new Object[]{getMethod(), getURL()});
+ } else {
+ Logger.getGlobal().log(Level.SEVERE, "error {0} on {1} {2}", new Object[]{responseCode, getMethod(), getURL()});
+ }
+ connection.disconnect();
+ } catch (MalformedURLException ex) {
+ Logger.getGlobal().log(Level.SEVERE, "Malormed URL");
+ } catch (IOException ex) {
+ Logger.getGlobal().log(Level.SEVERE, "IOException");
+ }
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/MultipartRequest.java b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/MultipartRequest.java
new file mode 100644
index 0000000..628f221
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/MultipartRequest.java
@@ -0,0 +1,86 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package br.ufrgs.inf.prosoft.requestssimulator.requests;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+import org.apache.http.HttpEntity;
+import org.apache.http.client.methods.CloseableHttpResponse;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.entity.mime.MultipartEntityBuilder;
+import org.apache.http.impl.client.CloseableHttpClient;
+import org.apache.http.impl.client.HttpClientBuilder;
+
+/**
+ *
+ * @author romulo
+ */
+public class MultipartRequest extends Request {
+
+ private final String forms;
+
+ protected MultipartRequest(RequestPlan requestPlan, String URL, String forms) {
+ super(requestPlan, URL);
+ this.forms = forms;
+ }
+
+ protected MultipartRequest(RequestPlan requestPlan, String URL, String headers, String forms) {
+ super(requestPlan, URL, headers);
+ this.forms = forms;
+ }
+
+ public Stream<Map.Entry<String, String>> getForms() {
+ Map<String, String> forms = new HashMap<>();
+ if (this.forms != null) {
+ String[] formsPairs = this.forms.split("; ");
+ for (String formPair : formsPairs) {
+ String[] pair = formPair.split(": ");
+ forms.put(pair[0], pair[1]);
+ }
+ }
+ return forms.entrySet().stream();
+ }
+
+ @Override
+ public void fire() {
+ try (CloseableHttpClient httpClient = HttpClientBuilder.create().build()) {
+ HttpPost httpPost = new HttpPost(getURL());
+ MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create();
+ getForms().forEach(entry -> {
+ if (entry.getKey().equals("file")) {
+ File value = new File(entry.getValue());
+ multipartEntityBuilder.addBinaryBody(entry.getKey(), value);
+ } else {
+ multipartEntityBuilder.addTextBody(entry.getKey(), entry.getValue());
+ }
+ });
+ HttpEntity httpEntity = multipartEntityBuilder.build();
+ httpPost.setEntity(httpEntity);
+ try (CloseableHttpResponse closeableHttpResponse = httpClient.execute(httpPost)) {
+ int responseCode = closeableHttpResponse.getStatusLine().getStatusCode();
+ if (responseCode < 400) {
+ Logger.getGlobal().log(Level.INFO, "{0} {1}", new Object[]{getMethod(), getURL()});
+ try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(closeableHttpResponse.getEntity().getContent()))) {
+ String response = bufferedReader.lines().collect(Collectors.joining());
+ }
+ } else {
+ Logger.getGlobal().log(Level.SEVERE, "error {0} on {1} {2}", new Object[]{responseCode, getMethod(), getURL()});
+ }
+ }
+ } catch (IOException e) {
+ Logger.getGlobal().log(Level.SEVERE, "IOException");
+ }
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/PostRequest.java b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/PostRequest.java
new file mode 100644
index 0000000..68bb47c
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/PostRequest.java
@@ -0,0 +1,60 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package br.ufrgs.inf.prosoft.requestssimulator.requests;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author romulo
+ */
+public class PostRequest extends Request {
+
+ private final String data;
+
+ protected PostRequest(RequestPlan requestPlan, String URL, String data) {
+ super(requestPlan, URL);
+ this.data = data;
+ }
+
+ protected PostRequest(RequestPlan requestPlan, String URL, String headers, String data) {
+ super(requestPlan, URL, headers);
+ this.data = data;
+ }
+
+ @Override
+ public void fire() {
+ try {
+ URL url = new URL(getURL());
+ HttpURLConnection connection = (HttpURLConnection) url.openConnection();
+ connection.setRequestMethod("POST");
+ connection.setDoOutput(true);
+ try (OutputStream outputStream = connection.getOutputStream()) {
+ outputStream.write(this.data.getBytes());
+ }
+ getHeaders().forEach((entry) -> {
+ connection.setRequestProperty(entry.getKey(), entry.getValue());
+ });
+ int responseCode = connection.getResponseCode();
+ if (responseCode < 400) {
+ Logger.getGlobal().log(Level.INFO, "{0} {1}", new Object[]{getMethod(), getURL()});
+ } else {
+ Logger.getGlobal().log(Level.SEVERE, "error {0} on {1} {2}", new Object[]{responseCode, getMethod(), getURL()});
+ }
+ connection.disconnect();
+ } catch (MalformedURLException ex) {
+ Logger.getGlobal().log(Level.SEVERE, "Malormed URL");
+ } catch (IOException ex) {
+ Logger.getGlobal().log(Level.SEVERE, "IOException");
+ }
+ }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/Request.java b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/Request.java
new file mode 100644
index 0000000..ff8df44
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/Request.java
@@ -0,0 +1,60 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package br.ufrgs.inf.prosoft.requestssimulator.requests;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.stream.Stream;
+
+/**
+ *
+ * @author romulo
+ */
+public abstract class Request {
+
+ private final RequestPlan requestPlan;
+ private final String URL;
+ private final String headers;
+ private Map<String, String> storedValues;
+
+ protected Request(RequestPlan requestPlan, String URL) {
+ this.requestPlan = requestPlan;
+ this.URL = URL;
+ this.headers = null;
+ }
+
+ protected Request(RequestPlan requestPlan, String URL, String headers) {
+ this.requestPlan = requestPlan;
+ this.URL = URL;
+ this.headers = headers;
+ }
+
+ public String getMethod() {
+ return this.requestPlan.getMethod();
+ }
+
+ public String getURL() {
+ return this.URL;
+ }
+
+ public Stream<Map.Entry<String, String>> getHeaders() {
+ Map<String, String> headers = new HashMap<>();
+ if (this.headers != null) {
+ String[] headerPairs = this.headers.split("; ");
+ for (String headerPair : headerPairs) {
+ String[] pair = headerPair.split(": ");
+ headers.put(pair[0], pair[1]);
+ }
+ }
+ return headers.entrySet().stream();
+ }
+
+ public Request pickNextRequest() {
+ return this.requestPlan.pickNextRequest();
+ }
+
+ public abstract void fire();
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/RequestPlan.java b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/RequestPlan.java
new file mode 100644
index 0000000..ab249dd
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/requests/RequestPlan.java
@@ -0,0 +1,194 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package br.ufrgs.inf.prosoft.requestssimulator.requests;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Objects;
+import java.util.Random;
+import java.util.stream.Stream;
+
+/**
+ *
+ * @author romulo
+ */
+public class RequestPlan {
+
+ private final String method;
+ private final String URL;
+ private final String data;
+ private Collection<String> linksReferences;
+ private List<RequestPlan> getLinks;
+ private List<RequestPlan> postLinks;
+ private Collection<String> storeFields;
+ private String contentType;
+ private String headers;
+ private String forms;
+
+ private RequestPlan(String URL) {
+ this.method = "GET";
+ this.URL = URL;
+ this.data = null;
+ resetFields();
+ }
+
+ private RequestPlan(String URL, String data) {
+ this.method = "POST";
+ this.URL = URL;
+ this.data = data;
+ resetFields();
+ }
+
+ private void resetFields() {
+ this.contentType = "application/json";
+ this.headers = "";
+ this.forms = "";
+ }
+
+ public static RequestPlan get(String URL) {
+ return new RequestPlan(URL);
+ }
+
+ public static RequestPlan post(String URL, String data) {
+ return new RequestPlan(URL, data);
+ }
+
+ public String getMethod() {
+ return method;
+ }
+
+ public RequestPlan addHeader(String header) {
+ if (this.headers == null || this.headers.isEmpty()) {
+ this.headers = header;
+ } else {
+ this.headers += "; " + header;
+ }
+ return this;
+ }
+
+ public RequestPlan setHeaders(String headers) {
+ this.headers = headers;
+ return this;
+ }
+
+ public RequestPlan addForm(String form) {
+ if (this.method.equals("GET")) {
+ throw new RuntimeException("Adding form to GET request");
+ }
+ if (this.forms == null) {
+ this.forms = form;
+ } else {
+ this.forms += "; " + form;
+ }
+ return this;
+ }
+
+ public RequestPlan setForms(String forms) {
+ if (this.method.equals("GET")) {
+ throw new RuntimeException("Adding form to GET request");
+ }
+ this.forms = forms;
+ return this;
+ }
+
+ public Stream<String> linksReferences() {
+ if (this.linksReferences == null) {
+ return Stream.empty();
+ }
+ return this.linksReferences.stream();
+ }
+
+ public RequestPlan addLink(RequestPlan requestPlan) {
+ if (requestPlan.getMethod().equals("GET")) {
+ if (this.getLinks == null) {
+ this.getLinks = new ArrayList<>();
+ }
+ this.getLinks.add(requestPlan);
+ } else {
+ if (this.postLinks == null) {
+ this.postLinks = new ArrayList<>();
+ }
+ this.postLinks.add(requestPlan);
+ }
+ return this;
+ }
+
+ public RequestPlan addLinks(Collection<RequestPlan> requestPlans) {
+ requestPlans.forEach(requestPlan -> addLink(requestPlan));
+ return this;
+ }
+
+ public RequestPlan setContentType(String contentType) {
+ this.contentType = contentType;
+ return this;
+ }
+
+ public Request pickNextRequest() {
+ Random random = new Random();
+ int probability = random.nextInt(100);
+ if ((this.getLinks == null || this.getLinks.isEmpty()) && (this.postLinks == null || this.postLinks.isEmpty())) {
+ throw new RuntimeException("GET and POST links empty");
+ }
+ RequestPlan chosen;
+ if (this.getLinks == null || this.getLinks.isEmpty()) {
+ probability = 100;
+ } else if (this.postLinks == null || this.postLinks.isEmpty()) {
+ probability = 0;
+ }
+ if (probability < 80) {
+ int chosenIndex = random.nextInt(this.getLinks.size());
+ chosen = this.getLinks.get(chosenIndex);
+ } else {
+ int chosenIndex = random.nextInt(this.postLinks.size());
+ chosen = this.postLinks.get(chosenIndex);
+ }
+ return chosen.build();
+ }
+
+ public Request build() {
+ Random random = new Random();
+ int randomInt = random.nextInt();
+ if (this.URL == null || this.URL.isEmpty()) {
+ throw new RuntimeException("URL is empty");
+ }
+ String URL = this.URL.replace("$", String.valueOf(randomInt));
+ String headers = this.headers;
+ if (this.contentType == null) {
+ this.contentType = "application/json";
+ }
+ if (this.contentType.startsWith("multipart")) {
+ String forms = this.forms;
+ return new MultipartRequest(this, URL, headers, forms);
+ }
+ if (this.method.equals("GET")) {
+ return new GetRequest(this, URL, headers);
+ }
+ String data = this.data;
+ return new PostRequest(this, URL, data, headers);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(this.method, this.URL);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj == null) {
+ return false;
+ }
+ if (!(obj instanceof RequestPlan)) {
+ return false;
+ }
+ RequestPlan other = (RequestPlan) obj;
+ return this.method.equals(other.method) && this.URL.equals(other.URL);
+ }
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Session.java b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Session.java
new file mode 100644
index 0000000..e150663
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Session.java
@@ -0,0 +1,47 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package br.ufrgs.inf.prosoft.requestssimulator;
+
+import br.ufrgs.inf.prosoft.requestssimulator.requests.Request;
+import br.ufrgs.inf.prosoft.requestssimulator.requests.RequestPlan;
+import java.util.Collection;
+import java.util.Random;
+
+/**
+ *
+ * @author romulo
+ */
+public class Session {
+
+ private final Collection<RequestPlan> roots;
+
+ public Session(Collection<RequestPlan> roots) {
+ this.roots = roots;
+ }
+
+ public void run() {
+ Random random = new Random();
+ Request request = null;
+ int probability = 0;
+ while (probability < 50) {
+ request = pickNextRequest(request);
+ request.fire();
+ probability = random.nextInt(100);
+ }
+ }
+
+ public Request pickNextRequest(Request currentRequest) {
+ if (currentRequest == null) {
+ if (this.roots == null || this.roots.isEmpty()) {
+ throw new RuntimeException("root within cycle");
+ }
+ currentRequest = RequestPlan.get("root")
+ .addLinks(this.roots)
+ .build();
+ }
+ return currentRequest.pickNextRequest();
+ }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Simulator.java b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Simulator.java
index f54e11e..3d0a94e 100644
--- a/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Simulator.java
+++ b/src/main/java/br/ufrgs/inf/prosoft/requestssimulator/Simulator.java
@@ -5,20 +5,10 @@
*/
package br.ufrgs.inf.prosoft.requestssimulator;
-import java.io.IOException;
-import java.io.OutputStream;
-import java.net.HttpURLConnection;
-import java.net.MalformedURLException;
-import java.net.URL;
-import java.nio.file.Files;
-import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collection;
-import java.util.List;
-import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
-import java.util.stream.Stream;
/**
*
@@ -28,133 +18,25 @@ public class Simulator {
private static final Logger logger = Logger.getLogger(Simulator.class.getName());
- public static final void simulate(String path) {
- simulate(path, 1);
+ public static final void simulate(Profile profile, long time) {
+ simulate(profile, time, 1);
}
- public static final void simulate(String path, int users) {
- if (users < 1) {
- logger.log(Level.SEVERE, "informed {0} users. Using 1", users);
- users = 1;
+ public static final void simulate(Profile profile, long time, int users) {
+ Collection<Thread> threads = new ArrayList<>();
+ for (int i = 0; i < users; i++) {
+ Thread thread = new Thread(() -> {
+ profile.run(time);
+ });
+ threads.add(thread);
+ thread.start();
}
- long startTime = System.currentTimeMillis();
- int requestsSize = 1;
- try {
- logger.log(Level.INFO, "requesting {0} URLS", requestsSize);
- if (users == 1) {
- requestsSize = (int) Files.lines(Paths.get(path)).count();
- Stream<String> lines = Files.lines(Paths.get(path));
- startTime = System.currentTimeMillis();
- lines.forEach(new RequestProcesser());
- } else {
- List<String> requests = Files.readAllLines(Paths.get(path));
- requestsSize = requests.size();
- int inclusiveStart = 0;
- int partitionSize = requestsSize / users;
- if (partitionSize < 1) {
- partitionSize = 1;
- logger.log(Level.SEVERE, "more users {0} than requests {1}. Using {1} users", new Object[]{users, requests.size()});
- }
- Collection<Thread> threads = new ArrayList<>();
- startTime = System.currentTimeMillis();
- while (inclusiveStart < requestsSize) {
- int exclusiveEnd = inclusiveStart + partitionSize;
- Thread thread = new Thread(new PartitionProcesser(requests, inclusiveStart, exclusiveEnd));
- thread.start();
- threads.add(thread);
- inclusiveStart = exclusiveEnd;
- }
- threads.forEach(thread -> {
- try {
- thread.join();
- } catch (InterruptedException ex) {
- logger.log(Level.SEVERE, "thread stopped");
- }
- });
- }
- } catch (IOException ex) {
- logger.log(Level.SEVERE, "cannot open file {0}", path);
- }
- long endTime = System.currentTimeMillis();
- long executionTime = startTime - endTime;
- int milliseconds = (int) (executionTime % 1000);
- int seconds = (int) (executionTime / 1000) % 60;
- int minutes = (int) (executionTime / (1000 * 60));
- double throughput = requestsSize / (double) seconds;
- System.out.println("Execution Time: " + minutes + ":" + seconds + "." + milliseconds + " minutes");
- System.out.println("Throughput: " + throughput + " requests per second");
- }
-
- private static class RequestProcesser implements Consumer<String> {
-
- private int i;
-
- public RequestProcesser() {
- this.i = 1;
- }
-
- public RequestProcesser(int i) {
- this.i = i;
- }
-
- @Override
- public void accept(String line) {
+ threads.forEach((thread) -> {
try {
- if (line.startsWith("POST")) {
- String[] split = line.split(" ");
- URL url = new URL(split[1]);
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setRequestMethod("POST");
- connection.setDoOutput(true);
- try (OutputStream outputStream = connection.getOutputStream()) {
- outputStream.write(split[2].getBytes());
- }
- int responseCode = connection.getResponseCode();
- if (responseCode == 200) {
- logger.log(Level.INFO, "{0} {1}", new Object[]{i, line});
- } else {
- logger.log(Level.SEVERE, "{0} error {1} on {2}", new Object[]{i, responseCode, line});
- }
- connection.disconnect();
- } else {
- URL url = new URL(line);
- HttpURLConnection connection = (HttpURLConnection) url.openConnection();
- connection.setRequestMethod("GET");
- int responseCode = connection.getResponseCode();
- if (responseCode == 200) {
- logger.log(Level.INFO, "{0} GET {1}", new Object[]{i, line});
- } else {
- logger.log(Level.SEVERE, "{0} error {1} on {2}", new Object[]{i, responseCode, line});
- }
- connection.disconnect();
- }
- } catch (MalformedURLException ex) {
- logger.log(Level.SEVERE, "Malormed URL");
- } catch (IOException ex) {
- logger.log(Level.SEVERE, "IOException");
+ thread.join();
+ } catch (InterruptedException ex) {
+ Logger.getLogger(Simulator.class.getName()).log(Level.SEVERE, null, ex);
}
- i++;
- }
- }
-
- private static class PartitionProcesser implements Runnable {
-
- private final Stream<String> requests;
- private final int inclusiveStart;
-
- public PartitionProcesser(List<String> requests, int incusiveStart, int exclusiveEnd) {
- this.inclusiveStart = incusiveStart;
- this.requests = requests.subList(incusiveStart, exclusiveEnd).stream();
- }
-
- public PartitionProcesser(Stream<String> requests) {
- this.requests = requests;
- this.inclusiveStart = 1;
- }
-
- @Override
- public void run() {
- this.requests.forEach(new RequestProcesser(this.inclusiveStart + 1));
- }
+ });
}
}