adaptive-monitoring-framework
Changes
.gitignore 30(+30 -0)
README.md 79(+79 -0)
tigris/pom.xml 209(+209 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/ComponentScan.java 26(+26 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/TigrisConfiguration.java 52(+52 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/CustomMonitoring.java 14(+14 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/TigrisCoordinator.java 257(+257 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/AsyncFileRepository.java 173(+173 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/ConsoleRepository.java 37(+37 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/MemoryRepository.java 48(+48 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/MongoRepository.java 96(+96 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/RedisRepository.java 47(+47 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/RepositoryFactory.java 70(+70 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/AnonymousUserGetter.java 13(+13 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/SpringUserGetter.java 44(+44 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/UserGetterFactory.java 28(+28 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/threads/NamedThreads.java 52(+52 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/threads/VerboseRunnable.java 206(+206 -0)
tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/PerformanceBaselineDataSet.java 32(+32 -0)
tigris/src/main/resources/logback.xml 40(+40 -0)
tigris/src/test/resources/logback-test.xml 35(+35 -0)
Details
.gitignore 30(+30 -0)
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..36b9275
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,30 @@
+*.class
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# Other Files and Directories #
+build/
+target/
+.idea/
+
+# Eclipse
+.classpath
+.project
+.settings/
+
+# Intellij
+.idea/
+*.iml
+*.iws
+
+# Mac
+.DS_Store
+
+# Maven
+log/
+
+#Gradle
+build.gradle
README.md 79(+79 -0)
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..dc33580
--- /dev/null
+++ b/README.md
@@ -0,0 +1,79 @@
+Tigris Framework
+=========
+
+Tigris is a two-phase framework for filtering and sampling execution traces at runtime. It can be used within a feedback loop to effectively monitor a managed software system and provide information for different purposes, e.g. to identify security vulnerabilities, model inconsistencies or performance bugs and, eventually, adapt the system. Tigris includes a domain-specific language (DSL) that allows users to express the goal of monitoring in the form of high-level relevance criteria. At runtime, Tigris automatically instruments the monitored system according to the specified relevance criteria. In its first phase, the framework performs a lightweight and coarse-grained monitoring to collect the metrics needed to guide an in-depth monitoring. Software metrics and relevance criteria are derived from a systematic literature review. The framework identifies relevant parts of the software based on the information collected in the first phase and then, in the second phase, collects traces at a fine-grained level.
+
+Features
+------------
+- Declarative configuration
+- Enable and disable features by configuration or at runtime
+- Automatic monitoring of relevant methods
+
+Tigris is implemented in Java and thus can be instantiated and integrated into monitoring-based approaches to leverage the monitoring results of Java-based approaches and applications.
+
+Required Tools
+--------------
+- AOP-enabled compilation
+- Maven 3
+- Java 8
+
+Build and Dependency
+--------------------
+
+Go to the framework folder and build the `pom.xml` with Maven: `mvn clean install`
+
+If your project is maven-based, in your `pom.xml`:
+```xml
+<dependencies>
+ <dependency>
+ <groupId>br.ufrgs.inf.prosoft</groupId>
+ <artifactId>tigris</artifactId>
+ <version>0.10.0-SNAPSHOT</version>
+ </dependency>
+</dependencies>
+```
+
+Otherwise, you can also get the `.jar` produced by maven can be just added to the classpath.
+
+Usage
+-----
+
+###### Application example
+
+In order to have a practice reference, consider the project [`app-example`](../app-example/). It should be found along with the source of the framework.
+
+Create a file tigris.properties under the resources folder of your application to specify the configuration desired. The TigrisDSL specification, as well as metrics to be used while assessing execution events, are manually configured through a properties file called `tigris.properties`. An example of such configuration is shown as follows.
+
+```
+//example of tigris.properties
+criteria = (more frequent U most expensive) C least changeable
+frequency = tigris.metrics.Method.INVOCATION_FREQUENCY
+expensiveness = tigris.metrics.Method.EXECUTION_TIME
+changeability = tigris.metrics.Method.COMPUTATION_PATTERN
+```
+
+Then, create a configuration class which consists of `@TigrisConfiguration` and `@ComponentScan` annotations, on the root package of the system:
+
+```
+@TigrisConfiguration(logRepository = RepositoryType.MEMORY, criteria = "(more frequent U most expensive) C least changeable")
+@ComponentScan(allowed = "org.springframework.samples.petclinic.*", denied = "org.springframework.samples.petclinic.model.*")
+```
+
+Inner workings
+--------------
+
+To collect data during the coarse-grained and fine-grained monitoring phases, we intercept method executions with [AspectJ](https://eclipse.org/aspectj/), which allows Tigris to acquire lightweight and dynamic software metrics without changing the base code. Tigris also supports loading output metrics from [Understand](https://scitools.com/), to evaluate criteria that are based on static metrics. Metrics available in the framework are the most frequently used metrics in the literature, which are listed in table below.
+
+| Metric | Description | Estimation |
+|----------|------------------------|-------------|
+| Concurrency Level | the number of times a method is executed concurrently | mean number of active threads during all calls of the method |
+| Computation Pattern | the number of times that each pair of input and output of method occurs | standard deviation of the return size of all calls of a method |
+| Energy Consumption | the amount of energy demanded by a method | mean estimate of energy consumption of all calls of the method |
+| Error level | the number of times the execution of a method thrown exceptions | absolute number of exceptions raised by all calls of the method |
+| Execution Time | the time taken to execute a method | mean execution time of all calls of the method |
+| Inter-Arrival Time | the time taken between executions of a method | mean time between each call of a method and the next |
+| Invocation Frequency | the number of times a method occurs | absolute number of calls of a method |
+| Memory Consumption | the amount of memory demanded by a method | mean estimate memory consumption of all calls of the method |
+| User Behavior | the number of times a method is shared among different users | absolute number of user sessions that lead to calls of a method |
+
+Tigris was designed to be extensible and flexible. New criteria and metrics can be included by extending and implementing interfaces provided by the framework. The same interfaces are used to customize and define how metrics should be calculated. It is also possible to configure the framework so that relevance criteria and metrics can change at runtime, and thus achieve increased adaptation levels. In addition to the usage of TigrisDSL filters to identify relevant event executions based on the goal of monitoring, Tigris also offers customizations. For example, it is possible to set up the framework to focus on specific monitoring locations, which can improve the set of events to be evaluated as relevant as well as exclude events that must not be monitored, thus reducing the time overhead for tracking them.
\ No newline at end of file
tigris/pom.xml 209(+209 -0)
diff --git a/tigris/pom.xml b/tigris/pom.xml
new file mode 100644
index 0000000..4f748a8
--- /dev/null
+++ b/tigris/pom.xml
@@ -0,0 +1,209 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <modelVersion>4.0.0</modelVersion>
+
+ <groupId>br.ufrgs.inf.prosoft</groupId>
+ <artifactId>tigris</artifactId>
+ <version>0.10.0-SNAPSHOT</version>
+
+ <properties>
+ <aspectj.version>1.8.9</aspectj.version>
+ <java.version>1.8</java.version>
+ </properties>
+
+ <dependencies>
+
+ <dependency>
+ <groupId>org.reflections</groupId>
+ <artifactId>reflections</artifactId>
+ <version>0.9.10</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjrt</artifactId>
+ <version>${aspectj.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjweaver</artifactId>
+ <version>${aspectj.version}</version>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-core</artifactId>
+ <version>1.1.7</version>
+ </dependency>
+
+ <dependency>
+ <groupId>ch.qos.logback</groupId>
+ <artifactId>logback-classic</artifactId>
+ <version>1.1.7</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.slf4j</groupId>
+ <artifactId>slf4j-api</artifactId>
+ <version>1.7.21</version>
+ </dependency>
+
+ <dependency>
+ <groupId>junit</groupId>
+ <artifactId>junit</artifactId>
+ <version>4.12</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.ehcache</groupId>
+ <artifactId>sizeof</artifactId>
+ <version>0.3.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-lang3</artifactId>
+ <version>3.5</version>
+ </dependency>
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-collections4</artifactId>
+ <version>4.4</version>
+ </dependency>
+
+
+ <dependency>
+ <groupId>com.fasterxml.jackson.core</groupId>
+ <artifactId>jackson-databind</artifactId>
+ <version>2.7.4</version>
+ </dependency>
+ <dependency>
+ <groupId>com.fasterxml.jackson.datatype</groupId>
+ <artifactId>jackson-datatype-hibernate4</artifactId>
+ <version>2.5.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mongodb</groupId>
+ <artifactId>mongo-java-driver</artifactId>
+ <version>3.2.2</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.redisson</groupId>
+ <artifactId>redisson</artifactId>
+ <version>3.1.0</version>
+ </dependency>
+
+ <!--Caching providers-->
+ <dependency>
+ <groupId>com.google.guava</groupId>
+ <artifactId>guava</artifactId>
+ <version>20.0</version>
+ </dependency>
+ <dependency>
+ <groupId>com.github.ben-manes.caffeine</groupId>
+ <artifactId>caffeine</artifactId>
+ <version>2.3.0</version>
+ </dependency>
+ <dependency>
+ <groupId>net.sf.ehcache</groupId>
+ <artifactId>ehcache</artifactId>
+ <version>2.10.3</version>
+ </dependency>
+
+ <dependency>
+ <groupId>redis.clients</groupId>
+ <artifactId>jedis</artifactId>
+ <version>2.7.2</version>
+ <type>jar</type>
+ <scope>compile</scope>
+ </dependency>
+
+ <dependency>
+ <groupId>net.spy</groupId>
+ <artifactId>spymemcached</artifactId>
+ <version>2.12.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.springframework</groupId>
+ <artifactId>spring-core</artifactId>
+ <version>4.2.6.RELEASE</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.mockito</groupId>
+ <artifactId>mockito-core</artifactId>
+ <version>2.2.11</version>
+ </dependency>
+
+ <dependency>
+ <groupId>com.esotericsoftware</groupId>
+ <artifactId>kryo</artifactId>
+ <version>4.0.0</version>
+ </dependency>
+
+ <dependency>
+ <groupId>org.apache.commons</groupId>
+ <artifactId>commons-math3</artifactId>
+ <version>3.6.1</version>
+ </dependency>
+
+ <dependency>
+ <groupId>ca.umontreal.iro.simul</groupId>
+ <artifactId>ssj</artifactId>
+ <version>3.3.1</version>
+ </dependency>
+
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.maven.plugins</groupId>
+ <artifactId>maven-compiler-plugin</artifactId>
+ <version>3.3</version>
+ <configuration>
+ <source>${java.version}</source>
+ <target>${java.version}</target>
+ <useIncrementalCompilation>false</useIncrementalCompilation>
+ </configuration>
+ </plugin>
+ <plugin>
+ <groupId>org.codehaus.mojo</groupId>
+ <artifactId>aspectj-maven-plugin</artifactId>
+ <version>1.7</version>
+ <configuration>
+ <showWeaveInfo>true</showWeaveInfo>
+ <complianceLevel>${java.version}</complianceLevel>
+ <source>${java.version}</source>
+ <target>${java.version}</target>
+ <Xlint>ignore</Xlint>
+ <encoding>UTF-8</encoding>
+ <verbose>true</verbose>
+ </configuration>
+ <executions>
+ <execution>
+ <phase>process-sources</phase>
+ <goals>
+ <goal>compile</goal>
+ <goal>test-compile</goal>
+ </goals>
+ </execution>
+ </executions>
+ <dependencies>
+ <dependency>
+ <groupId>org.aspectj</groupId>
+ <artifactId>aspectjtools</artifactId>
+ <version>${aspectj.version}</version>
+ </dependency>
+ </dependencies>
+ </plugin>
+ </plugins>
+ </build>
+
+</project>
\ No newline at end of file
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/ComponentScan.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/ComponentScan.java
new file mode 100644
index 0000000..2a74f85
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/ComponentScan.java
@@ -0,0 +1,26 @@
+package br.ufrgs.inf.prosoft.tigris.configuration.annotation;
+
+import java.lang.annotation.*;
+
+/**
+ * The interface Component scan.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface ComponentScan {
+
+ /**
+ * Base packages to scan for annotated components.
+ *
+ * @return the string pattern
+ */
+ String allowed();
+
+ /**
+ * Denied string pattern.
+ *
+ * @return the string pattern
+ */
+ String denied();
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/TigrisConfiguration.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/TigrisConfiguration.java
new file mode 100644
index 0000000..a99a114
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/annotation/TigrisConfiguration.java
@@ -0,0 +1,52 @@
+package br.ufrgs.inf.prosoft.tigris.configuration.annotation;
+
+import br.ufrgs.inf.prosoft.tigris.configuration.types.RepositoryType;
+
+import java.lang.annotation.*;
+
+/**
+ * The interface Tigris configuration.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface TigrisConfiguration {
+
+ /**
+ * Static metric file string.
+ *
+ * @return the string
+ */
+ String staticMetricFile();
+
+
+ /**
+ * Log repository repository type.
+ *
+ * @return the repository type
+ */
+ RepositoryType logRepository() default RepositoryType.MONGODB;
+
+ /**
+ * Clear monitoring data on start boolean.
+ *
+ * @return the boolean
+ */
+ boolean clearMonitoringDataOnStart() default false;
+
+ /**
+ * Criteria in form of SET operations over the criteria.
+ * <p>
+ * Operations allowed: union (U), intersection (I), difference (D), complement (C)
+ * Verbs: frequent, expensive, changeable, errorprone, globalimpact
+ * Additional: more, less
+ * <p>
+ * Examples:
+ * more frequent U more expensive C less changeable
+ * less frequent C less expensive
+ *
+ * @return the string
+ */
+ String criteria();
+
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/types/Modelling.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/types/Modelling.java
new file mode 100644
index 0000000..304f332
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/types/Modelling.java
@@ -0,0 +1,26 @@
+package br.ufrgs.inf.prosoft.tigris.configuration.types;
+
+/**
+ * The enum Modelling.
+ */
+public enum Modelling {
+
+ /**
+ * The Fullexploration.
+ */
+//erase all previous data
+ FULLEXPLORATION,
+
+ /**
+ * The Accumulation.
+ */
+//do not erase anything
+ ACCUMULATION,
+
+ /**
+ * The Partialexploration.
+ */
+//erase only the oldest % of previous data
+ PARTIALEXPLORATION;
+
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/types/RepositoryType.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/types/RepositoryType.java
new file mode 100644
index 0000000..c78f34f
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/configuration/types/RepositoryType.java
@@ -0,0 +1,27 @@
+package br.ufrgs.inf.prosoft.tigris.configuration.types;
+
+/**
+ * The enum Repository type.
+ */
+public enum RepositoryType {
+ /**
+ * Mongodb repository type.
+ */
+ MONGODB,
+ /**
+ * Textfile repository type.
+ */
+ TEXTFILE,
+ /**
+ * The Memory.
+ */
+ @Deprecated MEMORY,
+ /**
+ * The Console.
+ */
+ @Deprecated CONSOLE,
+ /**
+ * Redis repository type.
+ */
+ REDIS;
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/exceptions/ConfigurationException.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/exceptions/ConfigurationException.java
new file mode 100644
index 0000000..8a389fa
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/exceptions/ConfigurationException.java
@@ -0,0 +1,35 @@
+package br.ufrgs.inf.prosoft.tigris.exceptions;
+
+/**
+ * The type Configuration exception.
+ */
+public class ConfigurationException extends RuntimeException {
+
+ /**
+ * Instantiates a new Configuration exception.
+ *
+ * @param message the message
+ * @param cause the cause
+ */
+ public ConfigurationException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Instantiates a new Configuration exception.
+ *
+ * @param cause the cause
+ */
+ public ConfigurationException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Instantiates a new Configuration exception.
+ *
+ * @param message the message
+ */
+ public ConfigurationException(String message) {
+ super(message);
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/exceptions/StorageException.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/exceptions/StorageException.java
new file mode 100644
index 0000000..8fb8a71
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/exceptions/StorageException.java
@@ -0,0 +1,35 @@
+package br.ufrgs.inf.prosoft.tigris.exceptions;
+
+/**
+ * The type Storage exception.
+ */
+public class StorageException extends RuntimeException {
+
+ /**
+ * Instantiates a new Storage exception.
+ *
+ * @param message the message
+ * @param cause the cause
+ */
+ public StorageException(String message, Throwable cause) {
+ super(message, cause);
+ }
+
+ /**
+ * Instantiates a new Storage exception.
+ *
+ * @param cause the cause
+ */
+ public StorageException(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Instantiates a new Storage exception.
+ *
+ * @param message the message
+ */
+ public StorageException(String message) {
+ super(message);
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/metrics/DataFiltering.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/metrics/DataFiltering.java
new file mode 100644
index 0000000..3e92dd0
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/metrics/DataFiltering.java
@@ -0,0 +1,250 @@
+package br.ufrgs.inf.prosoft.tigris.metrics;
+
+import br.ufrgs.inf.prosoft.utils.StatisticalTest;
+import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.util.Map;
+
+/**
+ * The type Data filtering.
+ */
+public class DataFiltering {
+
+ private final StaticMetrics staticMetrics;
+ private final Map<String, LightweightMetrics> metrics;
+
+ public DataFiltering(StaticMetrics staticMetrics, Map<String, LightweightMetrics> metrics) {
+ this.staticMetrics = staticMetrics;
+ this.metrics = metrics;
+ }
+
+ public boolean extendedFilter(LightweightMetrics methodMetrics) {
+ DescriptiveStatistics frequencies = new DescriptiveStatistics();
+ DescriptiveStatistics changeabilities = new DescriptiveStatistics();
+ DescriptiveStatistics errorprones = new DescriptiveStatistics();
+ DescriptiveStatistics latencies = new DescriptiveStatistics();
+ DescriptiveStatistics maintainabilities = new DescriptiveStatistics();
+ DescriptiveStatistics userBehaviors = new DescriptiveStatistics();
+ DescriptiveStatistics concurrencies = new DescriptiveStatistics();
+ DescriptiveStatistics expensiveness = new DescriptiveStatistics();
+ DescriptiveStatistics globalimpacts = new DescriptiveStatistics();
+ System.out.println("---------------------------");
+ for (String method : metrics.keySet()) {
+ System.out.println(method);
+ LightweightMetrics sc = metrics.get(method);
+ StaticMetrics.StaticMetric staticMetric = staticMetrics.getMetrics(sc.getName());
+ if (staticMetric == null)
+ continue;
+ frequencies.addValue(sc.getFrequency());
+ changeabilities.addValue(sc.getChangeability());
+ errorprones.addValue(staticMetric.maxNesting);
+ latencies.addValue(sc.getLatency());
+ maintainabilities.addValue(staticMetric.cyclomatic);
+ userBehaviors.addValue(sc.getUserBehavior());
+ concurrencies.addValue(sc.getConcurrency());
+ expensiveness.addValue(sc.getExpensiveness());
+ globalimpacts.addValue(staticMetric.countOutput);
+ }
+
+ System.out.println("---------------------------");
+ System.out.println("Metric: frequency; Distribution: " +
+ ((StatisticalTest.isNormalDistribution(frequencies.getValues(), 0.05)) ? "normal" : "not_normal"));
+ System.out.println("Metric: maintainability; Distribution: " +
+ ((StatisticalTest.isNormalDistribution(maintainabilities.getValues(), 0.05)) ? "normal" : "not_normal"));
+ System.out.println("Metric: changeability; Distribution: " +
+ ((StatisticalTest.isNormalDistribution(changeabilities.getValues(), 0.05)) ? "normal" : "not_normal"));
+ System.out.println("Metric: userBehavior; Distribution: " +
+ ((StatisticalTest.isNormalDistribution(userBehaviors.getValues(), 0.05)) ? "normal" : "not_normal"));
+ System.out.println("Metric: concurrency; Distribution: " +
+ ((StatisticalTest.isNormalDistribution(concurrencies.getValues(), 0.05)) ? "normal" : "not_normal"));
+ System.out.println("Metric: errorprone; Distribution: " +
+ ((StatisticalTest.isNormalDistribution(errorprones.getValues(), 0.05)) ? "normal" : "not_normal"));
+ System.out.println("Metric: expensiveness; Distribution: " +
+ ((StatisticalTest.isNormalDistribution(expensiveness.getValues(), 0.05)) ? "normal" : "not_normal"));
+ System.out.println("Metric: globalimpact; Distribution: " +
+ ((StatisticalTest.isNormalDistribution(globalimpacts.getValues(), 0.05)) ? "normal" : "not_normal"));
+ System.out.println("Metric: latency; Distribution: " +
+ ((StatisticalTest.isNormalDistribution(latencies.getValues(), 0.05)) ? "normal" : "not_normal"));
+
+ try {
+ final PrintWriter pw = new PrintWriter(new File("lightweightanalysis.csv"));
+ pw.write("method,frequency,maintainability,changeability,userBehavior,concurrency,errorprone,expensiveness,globalimpact,latency,vfrequency,vmaintainability,vchangeability,vuserBehavior,vconcurrency,verrorprone,vexpensiveness,vglobalimpact,vlatency\n");
+ for (String name : metrics.keySet()) {
+ LightweightMetrics lm = metrics.get(name);
+ StaticMetrics.StaticMetric staticMetric = staticMetrics.getMetrics(lm.getName());
+ if (staticMetric == null)
+ continue;
+ pw.write(metrics.get(name).getName() + "," +
+ DataFiltering.getGroup(frequencies, metrics.get(name).getFrequency()) +
+ "," + DataFiltering.getGroup(maintainabilities, staticMetric.cyclomatic) +
+ "," + DataFiltering.getGroup(changeabilities, lm.getChangeability()) +
+ "," + DataFiltering.getGroup(userBehaviors, lm.getUserBehavior()) +
+ "," + DataFiltering.getGroup(concurrencies, lm.getConcurrency()) +
+ "," + DataFiltering.getGroup(errorprones, staticMetric.maxNesting) +
+ "," + DataFiltering.getGroup(expensiveness, lm.getExpensiveness()) +
+ "," + DataFiltering.getGroup(globalimpacts, staticMetric.countOutput) +
+ "," + DataFiltering.getGroup(latencies, metrics.get(name).getLatency()) +
+ "," +
+ DataFiltering.allMetricsToString(lm, staticMetrics) + '\n');
+ }
+ pw.close();
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+
+ return
+ //extended filter: less changeable C more frequent C (more user_behavior U (less user_behavior C more expensive))
+ ((DataFiltering.isGroup("less", changeabilities, methodMetrics.getChangeability())
+ || DataFiltering.isGroup("normal", changeabilities, methodMetrics.getChangeability()))
+ && (DataFiltering.isGroup("more", frequencies, methodMetrics.getFrequency())
+ || DataFiltering.isGroup("normal", frequencies, methodMetrics.getFrequency()))
+ && ((DataFiltering.isGroup("more", userBehaviors, methodMetrics.getUserBehavior())
+ || DataFiltering.isGroup("normal", userBehaviors, methodMetrics.getUserBehavior()))
+ || ((DataFiltering.isGroup("less", userBehaviors, methodMetrics.getUserBehavior())
+ || DataFiltering.isGroup("normal", userBehaviors, methodMetrics.getUserBehavior()))
+ && (DataFiltering.isGroup("normal", expensiveness, methodMetrics.getExpensiveness())
+ || DataFiltering.isGroup("more", expensiveness, methodMetrics.getExpensiveness())))));
+
+ //restricted filter: less changeable C more frequent C (more user_behavior U (less user_behavior C more expensive))
+// (DataFiltering.isGroup("less", changeabilities, sc.getChangeability())
+// && DataFiltering.isGroup("more", frequencies, sc.getFrequency())
+// && (DataFiltering.isGroup("more", userBehaviors, sc.getUserBehavior())
+// || (DataFiltering.isGroup("less", userBehaviors, sc.getUserBehavior())
+// && DataFiltering.isGroup("more", expensiveness, sc.getExpensiveness()))))
+
+// if (DataFiltering.isGroup("least", changeabilities, sc.getChangeability())
+// && DataFiltering.isGroup("most", frequencies, sc.getFrequency())
+// && (DataFiltering.isGroup("most", userBehaviors, sc.getUserBehavior())
+// || (DataFiltering.isGroup("least", userBehaviors, sc.getUserBehavior())
+// && DataFiltering.isGroup("most", expensiveness, sc.getExpensiveness()))))
+
+// if (DataFiltering.isGroup("less", changeabilities, sc.getChangeability())
+// && DataFiltering.isGroup("most", frequencies, sc.getFrequency())
+// && DataFiltering.isGroup("most", expensiveness, sc.getExpensiveness()))
+ }
+
+ /**
+ * Is group boolean.
+ *
+ * @param group the group
+ * @param criteria the criteria
+ * @param value the value
+ * @return the boolean
+ */
+ public static boolean isGroup(String group, DescriptiveStatistics criteria, double value) {
+
+ boolean isNormal = StatisticalTest.isNormalDistribution(criteria.getValues(), 0.05);
+ DescriptiveStatistics half = new DescriptiveStatistics();
+ if (!isNormal && (group.equals("most") || group.equals("least"))) {
+ for (double val : criteria.getValues())
+ if (group.equals("most") && val >= criteria.getPercentile(75))
+ half.addValue(val);
+ else if (group.equals("least") && val <= criteria.getPercentile(25))
+ half.addValue(val);
+ }
+
+ switch (group) {
+ case "normal":
+ if (isNormal)
+ return (value > (criteria.getMean() - criteria.getStandardDeviation()) &&
+ value < (criteria.getMean() + criteria.getStandardDeviation()));
+ else
+ return (value > criteria.getPercentile(25) &&
+ value < criteria.getPercentile(75));
+ case "most":
+ if (isNormal)
+ return value >= (criteria.getMean() + (2 * criteria.getStandardDeviation()));
+ else
+ return value >= half.getPercentile(50);
+ case "least":
+ if (isNormal)
+ return value <= (criteria.getMean() + (2 * criteria.getStandardDeviation()));
+ else
+ return value <= half.getPercentile(50);
+ case "more":
+ if (isNormal)
+ return value >= (criteria.getMean() + criteria.getStandardDeviation());
+ else
+ return value >= criteria.getPercentile(75);
+ case "less":
+ if (isNormal)
+ return value <= (criteria.getMean() - criteria.getStandardDeviation());
+ else
+ return value <= criteria.getPercentile(25);
+ }
+
+ throw new RuntimeException("Não foi possível classificar...");
+ }
+
+ /**
+ * Get group string.
+ *
+ * @param criteria the criteria
+ * @param value the value
+ * @return the string
+ */
+ public static String getGroup(DescriptiveStatistics criteria, double value) {
+
+ boolean isNormal = StatisticalTest.isNormalDistribution(criteria.getValues(), 0.05);
+ DescriptiveStatistics upperHalf = new DescriptiveStatistics();
+ DescriptiveStatistics lowerHalf = new DescriptiveStatistics();
+ if (!isNormal) {
+ for (double val : criteria.getValues())
+ if (val >= criteria.getPercentile(75))
+ upperHalf.addValue(val);
+ else if (val <= criteria.getPercentile(25))
+ lowerHalf.addValue(val);
+ }
+
+ if (isNormal) {
+ if (value > (criteria.getMean() - criteria.getStandardDeviation()) &&
+ value < (criteria.getMean() + criteria.getStandardDeviation()))
+ return "normal";
+ else if (value >= (criteria.getMean() + (2 * criteria.getStandardDeviation())))
+ return "most";
+ else if (value <= (criteria.getMean() + (2 * criteria.getStandardDeviation())))
+ return "least";
+ else if (value >= (criteria.getMean() + criteria.getStandardDeviation()))
+ return "more";
+ else if (value <= (criteria.getMean() - criteria.getStandardDeviation()))
+ return "less";
+ } else {
+ if (value > criteria.getPercentile(25) &&
+ value < criteria.getPercentile(75))
+ return "normal";
+ else if (value >= upperHalf.getPercentile(50))
+ return "most";
+ else if (value <= lowerHalf.getPercentile(50))
+ return "least";
+ else if (value >= criteria.getPercentile(75))
+ return "more";
+ else if (value <= criteria.getPercentile(25))
+ return "less";
+ }
+
+ throw new RuntimeException("Não foi possível classificar...");
+ }
+
+ /**
+ * All metrics to string string.
+ *
+ * @param lightweightMetrics the lightweight metrics
+ * @param staticMetrics the static metrics
+ * @return the string
+ */
+ public static String allMetricsToString(LightweightMetrics lightweightMetrics, StaticMetrics staticMetrics) {
+ //frequency,maintainability,changeability,userBehavior,concurrency,errorprone,expensiveness,globalimpact,latency
+ return lightweightMetrics.getFrequency() +
+ "," + staticMetrics.getMetrics(lightweightMetrics.getName()).cyclomatic +
+ "," + lightweightMetrics.getChangeability() +
+ "," + lightweightMetrics.getUserBehavior() +
+ "," + lightweightMetrics.getConcurrency() +
+ "," + staticMetrics.getMetrics(lightweightMetrics.getName()).maxNesting +
+ "," + lightweightMetrics.getExpensiveness() +
+ "," + staticMetrics.getMetrics(lightweightMetrics.getName()).countOutput +
+ "," + lightweightMetrics.getLatency();
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/metrics/LightweightMetrics.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/metrics/LightweightMetrics.java
new file mode 100644
index 0000000..3a72a63
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/metrics/LightweightMetrics.java
@@ -0,0 +1,227 @@
+package br.ufrgs.inf.prosoft.tigris.metrics;
+
+import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
+import org.ehcache.sizeof.SizeOf;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * The type Lightweight metrics.
+ */
+public class LightweightMetrics {
+
+ private static SizeOf sizeOf = SizeOf.newInstance();
+ private long occurrences;
+ private SummaryStatistics timeStatistics;
+ private SummaryStatistics memoryStatistics;
+ private SummaryStatistics threadStatistics;
+ private SummaryStatistics intervalStatistics;
+ private List<Long> startTimes;
+ private SummaryStatistics returnSizeStatistics;
+ private Set<String> users;
+ private String name;
+ private String longName;
+ private long thrownCounter;
+ private StaticMetrics staticMetrics;
+
+ /**
+ * Instantiates a new Lightweight metrics.
+ *
+ * @param name the name
+ * @param longName the long name
+ * @throws IOException the io exception
+ */
+ public LightweightMetrics(String name, String longName) throws IOException {
+ this.name = name;
+ this.longName = longName;
+ startTimes = new ArrayList<>();
+ timeStatistics = new SummaryStatistics();
+ memoryStatistics = new SummaryStatistics();
+ threadStatistics = new SummaryStatistics();
+ returnSizeStatistics = new SummaryStatistics();
+ intervalStatistics = new SummaryStatistics();
+ intervalStatistics.addValue(0);
+ users = new HashSet<>();
+ }
+
+ /**
+ * Add time.
+ *
+ * @param startTime the start time
+ * @param time the time
+ */
+ public void addTime(long startTime, long time){
+ startTimes.add(startTime);
+ timeStatistics.addValue(time);
+ if(startTimes.size() > 1)
+ intervalStatistics.addValue(startTime - startTimes.get(startTimes.size() - 2));
+ }
+
+ public void addThreadNumber(long threads){
+ threadStatistics.addValue(threads);
+ }
+
+ public void addMemoryConsumption(long memory){
+ memoryStatistics.addValue(memory);
+ }
+
+ /**
+ * Add return size.
+ *
+ * @param returnObject the return object
+ */
+ public void addReturnSize(Object returnObject){
+ if(returnObject != null)
+ returnSizeStatistics.addValue(sizeOf.sizeOf(returnObject));
+ else
+ returnSizeStatistics.addValue(0);
+ }
+
+ public void incThrown() {
+ thrownCounter++;
+ }
+
+ /**
+ * Inc occurrence.
+ */
+ public void incOccurrence() {
+ occurrences++;
+ }
+
+ /**
+ * Add user.
+ *
+ * @param currentUser the current user
+ */
+ public void addUser(String currentUser) {
+ users.add(currentUser);
+ }
+
+ /**
+ * Inter intervals list.
+ *
+ * @return the list
+ */
+ public List<Long> interIntervals(){
+ List<Long> intervalTimes = new ArrayList<>();
+ long lastTime = startTimes.get(0);
+ for(Long l : startTimes){
+ intervalTimes.add(l - lastTime);
+ lastTime = l;
+ }
+ return intervalTimes;
+ }
+
+ /**
+ * Gets name.
+ *
+ * @return the name
+ */
+ public String getName() {
+ return name;
+ }
+
+ /**
+ * Gets long name.
+ *
+ * @return the long name
+ */
+ public String getLongName() {
+ return longName;
+ }
+
+ @Override
+ public String toString() {
+ return "LightweightMetrics{" +
+ "name='" + name + '\'' +
+ ", frequency=" + getFrequency() +
+ ", expensiveness=" + getExpensiveness() +
+ '}';
+ }
+
+ //Metrics below
+
+ /**
+ * Number of occurrences (calls)
+ *
+ * @return number of occurrences
+ */
+ public long getFrequency() {
+ return occurrences;
+ }
+
+ /**
+ * Cyclomatic complexity (Cyclomatic at understand tool)
+ * @return Cyclomatic complexity from static metrics file
+ */
+ public long getMaintainability(){
+ return staticMetrics.getMetrics(name).cyclomatic;
+ }
+
+ /**
+ * std classSize of the return
+ *
+ * @return class size
+ */
+ public double getChangeability(){
+ return returnSizeStatistics.getStandardDeviation();
+ }
+
+ /**
+ * Mean number of different users that triggered the method
+ *
+ * @return number of different users
+ */
+ public long getUserBehavior(){
+ return users.size();
+ }
+
+ /**
+ * Mean Interval inter-calls
+ *
+ * @return inter interval times
+ */
+ public double getConcurrency(){
+ return intervalStatistics.getMean();
+ }
+
+ /**
+ * Number of constructs (if, while, for) (MaxNesting at understand tool)
+ * @return MaxNesting from static metrics file
+ */
+ public long getErrorprone(){
+ return staticMetrics.getMetrics(name).maxNesting;
+ }
+
+ /**
+ * Mean processing time
+ *
+ * @return processing times
+ */
+ public double getExpensiveness(){
+ return timeStatistics.getMean();
+ }
+
+
+ /**
+ * Number of static resources (variables and methods) referenced and thread creations (CountOutput at understand tool)
+ * @return CountOutput from static metrics file
+ */
+ public long getGlobalImpact(){
+ return staticMetrics.getMetrics(name).countOutput;
+ }
+
+ /**
+ * Throughput
+ *
+ * @return Throughput double
+ */
+ public double getLatency(){
+ return (startTimes.get(startTimes.size()-1) - startTimes.get(0)) / startTimes.size();
+ }
+
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/metrics/StaticMetrics.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/metrics/StaticMetrics.java
new file mode 100644
index 0000000..0800dbd
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/metrics/StaticMetrics.java
@@ -0,0 +1,103 @@
+package br.ufrgs.inf.prosoft.tigris.metrics;
+
+
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * The type Static metrics.
+ */
+public class StaticMetrics {
+
+ private Map<String, StaticMetric> metrics;
+
+ /**
+ * Instantiates a new Static metrics.
+ *
+ * @param filename the filename
+ * @throws IOException the io exception
+ */
+ public StaticMetrics(String filename) throws IOException {
+ loadMetrics(filename);
+ }
+
+ /**
+ * Get metrics static metric.
+ *
+ * @param method the method
+ * @return the static metric
+ */
+ public StaticMetric getMetrics(String method){
+ return metrics.get(method);
+ }
+
+ /**
+ * Load metrics.
+ *
+ * @param filename the filename
+ * @throws IOException the io exception
+ */
+ public void loadMetrics(String filename) throws IOException {
+ BufferedReader br = new BufferedReader(new FileReader(filename));
+ String line = null;
+ metrics = new HashMap<>();
+
+ while ((line = br.readLine()) != null) {
+ if (line.contains("Kind")) continue;
+ String str[] = line.split(",");
+ metrics.put(str[1], new StaticMetric(Long.valueOf(str[2]), Long.valueOf(str[3]), Long.valueOf(str[4])));
+ }
+ }
+
+ /**
+ * Gets all metrics.
+ *
+ * @return the all metrics
+ */
+ public Map<String, StaticMetric> getAllMetrics() {
+ return metrics;
+ }
+
+ /**
+ * The type Static metric.
+ */
+ public class StaticMetric {
+ /**
+ * The Count output.
+ */
+ public long countOutput;
+ /**
+ * The Cyclomatic.
+ */
+ public long cyclomatic;
+ /**
+ * The Max nesting.
+ */
+ public long maxNesting;
+
+ /**
+ * Instantiates a new Static metric.
+ *
+ * @param countOutput the count output
+ * @param cyclomatic the cyclomatic
+ * @param maxNesting the max nesting
+ */
+ public StaticMetric(long countOutput, long cyclomatic, long maxNesting) {
+ this.countOutput = countOutput;
+ this.cyclomatic = cyclomatic;
+ this.maxNesting = maxNesting;
+ }
+
+ @Override
+ public String toString() {
+ return "StaticMetric{" +
+ "countOutput=" + countOutput +
+ ", cyclomatic=" + cyclomatic +
+ ", maxNesting=" + maxNesting +
+ '}';
+ }
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/CustomMonitoring.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/CustomMonitoring.java
new file mode 100644
index 0000000..834130f
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/CustomMonitoring.java
@@ -0,0 +1,14 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.aspects;
+
+import br.ufrgs.inf.prosoft.tigris.monitoring.metadata.Key;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.Repository;
+
+public interface CustomMonitoring extends Runnable {
+ void initialize(Repository repository);
+
+ boolean beforeExecuting(Key key);
+
+ void afterExecuting(Key key, Object result);
+
+ Object returnResult(Key key);
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/MonitorAspect.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/MonitorAspect.java
new file mode 100644
index 0000000..b22726a
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/MonitorAspect.java
@@ -0,0 +1,39 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.aspects;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Pointcut;
+
+import java.io.IOException;
+
+@Aspect
+public class MonitorAspect {
+
+ @Pointcut(
+ //any execution except the own framework
+ "(execution(!void *(..)) && !within(br.ufrgs.inf.prosoft..*) " +
+ //avoid calls from repository while serializing objects, it is necessary if a hash could not be used
+ "&& !cflow(call(* br.ufrgs.inf.prosoft.tigris.monitoring.storage..*(..))) " +
+ //conditional to enable and disable at runtime
+ "&& if())"
+ )
+ public static boolean anyCall() {
+ return TigrisCoordinator.isEnabled();
+ }
+
+ private TigrisCoordinator tigrisCoordinator;
+
+ public MonitorAspect() throws IOException {
+ tigrisCoordinator = new TigrisCoordinator();
+ }
+
+ @Around("anyCall()")
+ public Object aroundMethods(ProceedingJoinPoint joinPoint) throws Throwable {
+ if (tigrisCoordinator.isAllowed(joinPoint)) {
+ return tigrisCoordinator.process(joinPoint);
+ } else {
+ return joinPoint.proceed();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/TigrisCoordinator.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/TigrisCoordinator.java
new file mode 100644
index 0000000..71eed61
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/aspects/TigrisCoordinator.java
@@ -0,0 +1,257 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.aspects;
+
+import br.ufrgs.inf.prosoft.tigris.configuration.annotation.ComponentScan;
+import br.ufrgs.inf.prosoft.tigris.configuration.annotation.TigrisConfiguration;
+import br.ufrgs.inf.prosoft.tigris.exceptions.ConfigurationException;
+import br.ufrgs.inf.prosoft.tigris.metrics.DataFiltering;
+import br.ufrgs.inf.prosoft.tigris.metrics.LightweightMetrics;
+import br.ufrgs.inf.prosoft.tigris.metrics.StaticMetrics;
+import br.ufrgs.inf.prosoft.tigris.monitoring.metadata.Key;
+import br.ufrgs.inf.prosoft.tigris.monitoring.metadata.LogTrace;
+import br.ufrgs.inf.prosoft.tigris.monitoring.metadata.MethodInfo;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.Repository;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.RepositoryFactory;
+import br.ufrgs.inf.prosoft.tigris.monitoring.util.threads.NamedThreads;
+import br.ufrgs.inf.prosoft.tigris.sampling.Granularity;
+import br.ufrgs.inf.prosoft.tigris.sampling.Sampling;
+import br.ufrgs.inf.prosoft.tigris.sampling.SamplingConfiguration;
+import br.ufrgs.inf.prosoft.utils.ConfigurationUtils;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.IOException;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentSkipListSet;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Pattern;
+
+import static java.lang.System.currentTimeMillis;
+
+public class TigrisCoordinator implements Runnable {
+ private static boolean enabled = true;
+ private boolean coarseMonitoringEnabled = true;
+
+ Logger logger = LoggerFactory.getLogger(TigrisCoordinator.class);
+
+ //traceable configuration
+ private Pattern allowedPattern;
+ private Pattern deniedPattern;
+
+ //tigris config
+ private final TigrisConfiguration tigrisConfiguration;
+ private final SamplingConfiguration samplingConfiguration;
+ private final Sampling sampling;
+ private final Repository repository;
+ private Class<?> customMonitoringClass;
+ private CustomMonitoring customMonitoring;
+
+ private final String staticFile;
+ private Map<String, LightweightMetrics> metrics;
+
+ private final ScheduledExecutorService samplingAdaptationExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreads(
+ "sampling-adaptation",
+ "adapting sampling rate and sample readiness"
+ ));
+
+ private final ScheduledExecutorService lightweightAnalyzerExecutor = Executors.newSingleThreadScheduledExecutor(new NamedThreads(
+ "lightweight-analyzer",
+ "computing lightweight metrics and setting allowed methods"
+ ));
+
+ public TigrisCoordinator() throws IOException {
+ Class<?> configClass = ConfigurationUtils.getAvailableConfigurationClass(TigrisConfiguration.class);
+ if (configClass == null) {
+ turnoff();
+ logger.info("Tigris tracing disabled, there is no annotations.");
+ throw new ConfigurationException("Tigris tracing disabled, there is no annotations.");
+ }
+
+ tigrisConfiguration = configClass.getAnnotation(TigrisConfiguration.class);
+ staticFile = tigrisConfiguration.staticMetricFile();
+
+ //getting allowed packages from @ComponentScan
+ ComponentScan componentScanConfig = configClass.getAnnotation(ComponentScan.class);
+ if (componentScanConfig == null) {
+ //if not specified, it assumes the same package where @AdaptiveCaching were declared
+ allowedPattern = Pattern.compile(configClass.getPackage().getName() + ".*");
+ logger.info("ComponentScan for TigrisConfiguration not found, assuming the same package where @TigrisConfiguration were declared.");
+ }
+ allowedPattern = Pattern.compile(componentScanConfig.allowed());
+ deniedPattern = Pattern.compile(componentScanConfig.denied());
+ logger.info("@TigrisConfiguration will trace and cache methods into {} and deny {} package.", allowedPattern.pattern(), deniedPattern.pattern());
+
+ samplingConfiguration = configClass.getAnnotation(SamplingConfiguration.class);
+ sampling = new Sampling(samplingConfiguration.samplingPercentage(), samplingConfiguration.cycleTimeInSeconds());
+ samplingAdaptationExecutor.scheduleWithFixedDelay(
+ sampling, 120000, 450000, TimeUnit.MILLISECONDS);
+
+ repository = RepositoryFactory.getRepository(null, tigrisConfiguration.logRepository());
+
+ if (tigrisConfiguration.clearMonitoringDataOnStart()) {
+ repository.removeAll();
+ logger.debug("Repository starting cleaned.");
+ }
+
+ if (coarseMonitoringEnabled) {
+ metrics = new ConcurrentHashMap<>();
+ lightweightAnalyzerExecutor.scheduleWithFixedDelay(
+ this::run,
+ 120000, 450000, TimeUnit.MILLISECONDS);
+ }
+
+ try {
+
+ customMonitoringClass = ConfigurationUtils.getAvailableCustomMonitoringClass();
+ if (customMonitoringClass != null) {
+ customMonitoring = (CustomMonitoring) customMonitoringClass.newInstance();
+ }
+
+ customMonitoring.initialize(repository);
+ } catch (ConfigurationException e){
+ logger.info("AdaptiveCaching not found, disabling...");
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public boolean isAllowed(ProceedingJoinPoint joinPoint) {
+ MethodSignature signature = (MethodSignature) joinPoint.getSignature();
+ return
+ isEnabled()
+ && !signature.getName().contains("br.ufrgs.inf.prosoft")
+ && allowedPattern.matcher(signature.getMethod().getDeclaringClass().getPackage().getName()).matches()
+ && !deniedPattern.matcher(signature.getMethod().getDeclaringClass().getPackage().getName()).matches();
+ }
+
+ public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
+ boolean detailedTrace = (!coarseMonitoringEnabled ||
+ (coarseMonitoringEnabled && allowedFineGrained
+ .contains(joinPoint.getSignature().toLongString())));
+
+ //generate a hash of the method that will be used as: key to cache and compare if the method is allowed or not
+ Key key = new Key(joinPoint);
+
+ boolean customProcess = customMonitoring != null &&
+ customMonitoring.beforeExecuting(key);
+ if (customProcess) {
+ Object returnResult = customMonitoring.returnResult(key);
+ if (returnResult != null)
+ return returnResult;
+ }
+
+ Object[] joinPointArgs = null;
+ //method calls can change the args, so it is better to get it at the beginning
+ if (detailedTrace)
+ joinPointArgs = joinPoint.getArgs();
+
+ String signature = joinPoint.getSignature().toString();
+
+ long startTime = currentTimeMillis();
+ Object result = joinPoint.proceed();
+ long endTime = currentTimeMillis();
+
+ if (coarseMonitoringEnabled) {
+ LightweightMetrics metric = metrics.get(signature);
+ if (metric != null)
+ metric.incOccurrence();
+ else {
+ String name = joinPoint.getSignature().toString().split(" ")[1];
+ name = name.substring(0, name.indexOf("("));
+ metric = new LightweightMetrics(name, joinPoint.getSignature().toLongString());
+ metric.incOccurrence();
+ metrics.put(signature, metric);
+ }
+ metric.addTime(startTime, endTime - startTime);
+ metric.addReturnSize(result);
+ }
+
+ Granularity granularity = new Granularity(samplingConfiguration.granularity(), signature);
+
+ if(sampling.isPerformanceBaselineEnabled()) {
+ sampling.addPerformanceBaselineItem(granularity, endTime - startTime);
+ }
+
+ //trace only allowed by lightweight metrics
+ if (coarseMonitoringEnabled
+ && detailedTrace
+ && sampling.samplingDecision(granularity)) {
+ logger.debug("New trace: " + signature);
+
+ //we do not cache null returns, but we trace them
+ //maybe the method can sometimes return null... so there is not verification here
+ LogTrace logTrace = new LogTrace();
+ logTrace.setStartTime(startTime);
+ logTrace.setEndTime(endTime);
+
+ MethodInfo methodInfo = new MethodInfo(joinPoint.getSignature().toLongString(), joinPointArgs, result, key);
+ logTrace.setMethodInfo(methodInfo);
+
+ try {
+ repository.save(logTrace);
+ logger.debug("New trace entry: " + logTrace);
+ } catch (Exception e) {
+ logger.debug("Couldn't trace " + logTrace.getMethodInfo().getSignature() + " due to: " + e.getMessage());
+ }
+ }
+
+ if (detailedTrace && customProcess && result != null) {
+ customMonitoring.afterExecuting(key, result);
+ }
+
+ return result;
+ }
+
+ public void turnoff() {
+ enabled = false;
+ }
+
+ public static boolean isEnabled() {
+ return enabled;
+ }
+
+ public Repository getRepository() {
+ return repository;
+ }
+
+ /**
+ * The constant allowedFineGrained.
+ */
+ public static Set<String> allowedFineGrained = new ConcurrentSkipListSet<>();
+
+ @Override
+ public void run() {
+ try {
+ logger.info("Analyzing {} lightweight methods for relevance...", metrics.size());
+
+ StaticMetrics staticMetrics = new StaticMetrics(staticFile);
+ DataFiltering dataFiltering = new DataFiltering(staticMetrics, metrics);
+
+ //cleaning selected methods
+ allowedFineGrained = new ConcurrentSkipListSet<>();
+
+ //TODO process SET operations dynamically
+ // using criteria
+
+ for(String method : metrics.keySet()) {
+ LightweightMetrics sc = metrics.get(method);
+// if (dataFiltering.retrictedFilter())
+ if (dataFiltering.extendedFilter(sc))
+ allowedFineGrained.add(sc.getLongName());
+ }
+ logger.info("Selected methods for fine-grained ({}): {}", allowedFineGrained.size(), allowedFineGrained);
+
+ //analyze once
+ coarseMonitoringEnabled = false;
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+}
\ No newline at end of file
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/metadata/Key.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/metadata/Key.java
new file mode 100644
index 0000000..ff15e46
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/metadata/Key.java
@@ -0,0 +1,124 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.metadata;
+
+import br.ufrgs.inf.prosoft.tigris.monitoring.util.Mnemos;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+
+/**
+ * Key of a callable target.
+ */
+public class Key {
+
+ /**
+ * MethodInfo.
+ */
+ private final transient Method method;
+ /**
+ * Object callable (or class, if static method).
+ */
+ private final transient Object target;
+ /**
+ * Arguments.
+ */
+ private final transient Object[] arguments;
+
+ /**
+ * The Logger.
+ */
+ Logger logger = LoggerFactory.getLogger(Key.class);
+
+ /**
+ * Public ctor.
+ *
+ * @param point Joint point
+ */
+ public Key(final JoinPoint point) {
+ this.method = MethodSignature.class
+ .cast(point.getSignature()).getMethod();
+ this.target = Key.targetize(point);
+ this.arguments = point.getArgs();
+ }
+
+ /**
+ * Calculate its target.
+ *
+ * @param point Proceeding point
+ * @return The target
+ */
+ private static Object targetize(final JoinPoint point) {
+ final Object tgt;
+ final Method method = MethodSignature.class
+ .cast(point.getSignature()).getMethod();
+ if (Modifier.isStatic(method.getModifiers())) {
+ tgt = method.getDeclaringClass();
+ } else {
+ tgt = point.getTarget();
+ }
+ return tgt;
+ }
+
+ @Override
+ public String toString() {
+ return "Key{" +
+ "method=" + method +
+ ", target=" + target +
+ ", arguments=" + Arrays.toString(arguments) +
+ '}';
+ }
+
+ @Override
+ public final int hashCode() {
+ return this.method.hashCode();
+ }
+
+ @Override
+ public final boolean equals(final Object obj) {
+
+ final boolean equals;
+ if (this == obj) {
+ equals = true;
+ } else if (obj instanceof Key) {
+ final Key key = Key.class.cast(obj);
+ equals = key.method.equals(this.method)
+ //&& this.target.equals(key.target)
+ && Arrays.deepEquals(key.arguments, this.arguments);
+ } else {
+ equals = false;
+ }
+ return equals;
+ }
+
+ /**
+ * Send a result through, with necessary logging.
+ *
+ * @param result The result to send through
+ * @return The same result/object
+ * @checkstyle DesignForExtensionCheck (2 lines)
+ */
+ public Object through(final Object result) {
+ final Class<?> type = this.method.getDeclaringClass();
+ logger.debug(
+ "%s: %s from model (hit #%d, %[ms]s old)",
+ this,
+ Mnemos.toText(result, true, false)
+ );
+ return result;
+ }
+
+ /**
+ * Is it related to the same target?
+ *
+ * @param point Proceeding point
+ * @return True if the target is the same
+ */
+ public final boolean sameTarget(final JoinPoint point) {
+ return Key.targetize(point).equals(this.target);
+ }
+
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/metadata/LogTrace.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/metadata/LogTrace.java
new file mode 100644
index 0000000..1e5ec6e
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/metadata/LogTrace.java
@@ -0,0 +1,127 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.metadata;
+
+/**
+ * The type Log trace.
+ */
+public class LogTrace{
+
+ private MethodInfo methodInfo;
+ private long startTime;
+ private long endTime;
+ private String userId;
+
+ /**
+ * Gets method info.
+ *
+ * @return the method info
+ */
+ public MethodInfo getMethodInfo() {
+ return this.methodInfo;
+ }
+
+ /**
+ * Sets method info.
+ *
+ * @param methodInfo the method info
+ */
+ public void setMethodInfo(MethodInfo methodInfo) {
+ this.methodInfo = methodInfo;
+ }
+
+ /**
+ * Total time long.
+ *
+ * @return the long
+ */
+ public long totalTime() {
+ return endTime - startTime;
+ }
+
+ /**
+ * Gets start time.
+ *
+ * @return the start time
+ */
+ public long getStartTime() {
+ return this.startTime;
+ }
+
+ /**
+ * Sets start time.
+ *
+ * @param startTime the start time
+ */
+ public void setStartTime(long startTime) {
+ this.startTime = startTime;
+ }
+
+ /**
+ * Gets end time.
+ *
+ * @return the end time
+ */
+ public long getEndTime() {
+ return this.endTime;
+ }
+
+ /**
+ * Sets end time.
+ *
+ * @param endTime the end time
+ */
+ public void setEndTime(long endTime) {
+ this.endTime = endTime;
+ }
+
+ /**
+ * Gets user id.
+ *
+ * @return the user id
+ */
+ public String getUserId() {
+ return userId;
+ }
+
+ /**
+ * Sets user id.
+ *
+ * @param userId the user id
+ */
+ public void setUserId(String userId) {
+ this.userId = userId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (!(o instanceof LogTrace)) return false;
+
+ LogTrace logTrace = (LogTrace) o;
+
+ if (endTime != logTrace.endTime) return false;
+ if (startTime != logTrace.startTime) return false;
+ if (methodInfo != null ? !methodInfo.equals(logTrace.methodInfo) : logTrace.methodInfo != null) return false;
+ if (userId != null ? !userId.equals(logTrace.userId) : logTrace.userId != null) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = methodInfo != null ? methodInfo.hashCode() : 0;
+ result = 31 * result + (int) (startTime ^ (startTime >>> 32));
+ result = 31 * result + (int) (endTime ^ (endTime >>> 32));
+ result = 31 * result + (userId != null ? userId.hashCode() : 0);
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ return "LogTrace{" +
+ "methodInfo=" + methodInfo +
+ ", startTime=" + startTime +
+ ", endTime=" + endTime +
+ ", userId='" + userId + '\'' +
+ '}';
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/metadata/MethodInfo.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/metadata/MethodInfo.java
new file mode 100644
index 0000000..8bb46ea
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/metadata/MethodInfo.java
@@ -0,0 +1,138 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.metadata;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+
+import java.util.Objects;
+
+/**
+ * The type Method info.
+ */
+public class MethodInfo {
+
+ private String signature;
+
+ private Object[] arguments;
+
+ private Object returnedValue;
+
+ private Key key;
+
+ /**
+ * Instantiates a new Method info.
+ */
+ public MethodInfo() {
+ }
+
+ /**
+ * Instantiates a new Method info.
+ *
+ * @param signature the signature
+ * @param arguments the arguments
+ */
+ public MethodInfo(String signature, Object[] arguments) {
+ this.signature = signature;
+ this.arguments = arguments;
+ }
+
+ /**
+ * Instantiates a new Method info.
+ *
+ * @param signature the signature
+ * @param arguments the arguments
+ * @param returnedValue the returned value
+ */
+ public MethodInfo(String signature, Object[] arguments, Object returnedValue) {
+ this(signature, arguments);
+ this.returnedValue = returnedValue;
+ }
+
+ /**
+ * Instantiates a new Method info.
+ *
+ * @param s the s
+ * @param joinPointArgs the join point args
+ * @param result the result
+ * @param key the key
+ */
+ public MethodInfo(String s, Object[] joinPointArgs, Object result, Key key) {
+ this(s, joinPointArgs, result);
+ this.key = key;
+ }
+
+ /**
+ * Gets signature.
+ *
+ * @return the signature
+ */
+ public String getSignature() {
+ return this.signature;
+ }
+
+ /**
+ * Get arguments object [ ].
+ *
+ * @return the object [ ]
+ */
+ public Object[] getArguments() {
+ return this.arguments;
+ }
+
+ /**
+ * Gets returned value.
+ *
+ * @return the returned value
+ */
+ public Object getReturnedValue() {
+ return this.returnedValue;
+ }
+
+ /**
+ * Gets key.
+ *
+ * @return the key
+ */
+ public Object getKey() {
+ return key;
+ }
+
+ /**
+ * Equals without returned value boolean.
+ *
+ * @param o the o
+ * @return the boolean
+ */
+ public boolean equalsWithoutReturnedValue(MethodInfo o) {
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ if (!signature.equals(o.signature)) return false;
+ if (!(Objects.deepEquals(arguments, o.arguments) || EqualsBuilder.reflectionEquals(arguments, o.arguments))) return false;
+
+ return true;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ MethodInfo that = (MethodInfo) o;
+
+ if (!signature.equals(that.signature)) return false;
+ if (!EqualsBuilder.reflectionEquals(arguments, that.arguments)) return false;
+ return EqualsBuilder.reflectionEquals(returnedValue, that.returnedValue) || Objects.deepEquals(returnedValue, that.returnedValue);
+ }
+
+ @Override
+ public int hashCode() {
+ //TODO equals is enough to distinghish, not able to get a unique hashcode
+ return 1;
+ //return HashCodeBuilder.reflectionHashCode(this, "key");
+ }
+
+ public String toString() {
+ return "br.ufrgs.inf.prosoft.tigris.monitoring.application.metadata.MethodInfo(signature=" + this.signature + ", arguments=" + this.arguments + ", returnedValue=" + this.returnedValue + ")";
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/AsyncFileRepository.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/AsyncFileRepository.java
new file mode 100644
index 0000000..b2c178c
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/AsyncFileRepository.java
@@ -0,0 +1,173 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers;
+
+import br.ufrgs.inf.prosoft.tigris.exceptions.StorageException;
+import br.ufrgs.inf.prosoft.tigris.monitoring.metadata.LogTrace;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.Repository;
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.IOException;
+import java.io.Writer;
+import java.util.List;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The type Async file repository.
+ */
+public class AsyncFileRepository implements Runnable, Repository<LogTrace> {
+ private final File file;
+ private final Writer out;
+ private final BlockingQueue<Item> queue = new LinkedBlockingQueue<Item>();
+ private final ObjectMapper mapper;
+ private volatile boolean started = false;
+ private volatile boolean stopped = false;
+
+ /**
+ * Instantiates a new Async file repository.
+ *
+ * @param file the file
+ * @throws IOException the io exception
+ */
+ public AsyncFileRepository(File file) throws IOException {
+ this.file = file;
+ this.out = new BufferedWriter(new java.io.FileWriter(file));
+ this.mapper = new ObjectMapper();
+ }
+
+ /**
+ * Append.
+ *
+ * @param seq the seq
+ */
+ public void append(CharSequence seq) {
+ if (!started) {
+ throw new IllegalStateException("open() call expected before append()");
+ }
+ try {
+ queue.put(new CharSeqItem(seq));
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+ /**
+ * Indent.
+ *
+ * @param indent the indent
+ */
+ public void indent(int indent) {
+ if (!started) {
+ throw new IllegalStateException("open() call expected before append()");
+ }
+ try {
+ queue.put(new IndentItem(indent));
+ } catch (InterruptedException ignored) {
+ }
+ }
+
+ /**
+ * Open.
+ */
+ public void open() {
+ this.started = true;
+ new Thread(this).start();
+ }
+
+ public void run() {
+ while (!stopped) {
+ try {
+ Item item = queue.poll(100, TimeUnit.MICROSECONDS);
+ if (item != null) {
+ try {
+ item.write(out);
+ } catch (IOException logme) {
+ }
+ }
+ } catch (InterruptedException e) {
+ }
+ }
+ try {
+ out.close();
+ } catch (IOException ignore) {
+ }
+ }
+
+ /**
+ * Close.
+ */
+ public void close() {
+ this.stopped = true;
+ }
+
+ @Override
+ public void save(LogTrace logTrace) throws StorageException {
+ try {
+ append(mapper.writeValueAsString(logTrace));
+ } catch (Exception e) {
+ throw new StorageException("Json serialization fail", e);
+ }
+ }
+
+ @Override
+ public List<LogTrace> findAll() {
+ return null;
+ }
+
+ @Override
+ public void removeAll() {
+
+ }
+
+ @Override
+ public void saveAll(List<LogTrace> toSave) {
+
+ }
+
+ private static interface Item {
+ /**
+ * Write.
+ *
+ * @param out the out
+ * @throws IOException the io exception
+ */
+ void write(Writer out) throws IOException;
+ }
+
+ private static class CharSeqItem implements Item {
+ private final CharSequence sequence;
+
+ /**
+ * Instantiates a new Char seq item.
+ *
+ * @param sequence the sequence
+ */
+ public CharSeqItem(CharSequence sequence) {
+ this.sequence = sequence;
+ }
+
+ public void write(Writer out) throws IOException {
+ out.append(sequence);
+ }
+ }
+
+ private static class IndentItem implements Item {
+ private final int indent;
+
+ /**
+ * Instantiates a new Indent item.
+ *
+ * @param indent the indent
+ */
+ public IndentItem(int indent) {
+ this.indent = indent;
+ }
+
+ public void write(Writer out) throws IOException {
+ for (int i = 0; i < indent; i++) {
+ out.append(" ");
+ }
+ }
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/ConsoleRepository.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/ConsoleRepository.java
new file mode 100644
index 0000000..68bcbf6
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/ConsoleRepository.java
@@ -0,0 +1,37 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers;
+
+import br.ufrgs.inf.prosoft.tigris.exceptions.StorageException;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.Repository;
+
+import java.util.List;
+
+/**
+ * The type Console repository.
+ *
+ * @param <T> the type parameter
+ */
+public class ConsoleRepository<T> implements Repository<T> {
+
+
+ @Override
+ public void save(T t) throws StorageException {
+ System.out.println("Saving to console: " + t.toString());
+ }
+
+ @Override
+ public List<T> findAll() {
+ return null;
+ }
+
+ @Override
+ public void removeAll() {
+
+ }
+
+ @Override
+ public void saveAll(List<T> toSave) {
+ for(T t : toSave){
+ System.out.println("Saving to console: " + t.toString());
+ }
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/MemoryRepository.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/MemoryRepository.java
new file mode 100644
index 0000000..6aa66d2
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/MemoryRepository.java
@@ -0,0 +1,48 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers;
+
+import br.ufrgs.inf.prosoft.tigris.exceptions.StorageException;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.Repository;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * The type Memory repository.
+ *
+ * @param <T> the type parameter
+ */
+public class MemoryRepository<T> implements Repository<T> {
+
+ /**
+ * The Logs.
+ */
+ List<T> logs;
+
+ /**
+ * Instantiates a new Memory repository.
+ */
+ public MemoryRepository() {
+ logs = Collections.synchronizedList(new ArrayList<>());
+ }
+
+ @Override
+ public void save(T t) throws StorageException {
+ logs.add(t);
+ }
+
+ @Override
+ public List<T> findAll() {
+ return logs;
+ }
+
+ @Override
+ public void removeAll() {
+ logs.clear();
+ }
+
+ @Override
+ public void saveAll(List<T> toSave) {
+
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/MongoRepository.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/MongoRepository.java
new file mode 100644
index 0000000..23d813c
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/MongoRepository.java
@@ -0,0 +1,96 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers;
+
+import br.ufrgs.inf.prosoft.tigris.exceptions.StorageException;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.Repository;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.SerializationFeature;
+import com.mongodb.client.MongoCollection;
+import com.mongodb.client.MongoCursor;
+import org.bson.Document;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * The type Mongo repository.
+ *
+ * @param <T> the type parameter
+ */
+public class MongoRepository<T> implements Repository<T> {
+
+ /**
+ * The Type parameter class.
+ */
+ final Class<T> typeParameterClass;
+ private final ObjectMapper mapper;
+ private final MongoCollection<Document> dbCollection;
+ /**
+ * The Logger.
+ */
+ Logger logger = LoggerFactory.getLogger(MongoRepository.class);
+
+ /**
+ * Instantiates a new Mongo repository.
+ *
+ * @param dbCollection the db collection
+ * @param typeParameterClass the type parameter class
+ */
+ public MongoRepository(MongoCollection<Document> dbCollection, Class<T> typeParameterClass) {
+ this.dbCollection = dbCollection;
+ this.typeParameterClass = typeParameterClass;
+ this.mapper = new ObjectMapper();
+ mapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
+ mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
+ }
+
+ @Override
+ public void save(T o) throws StorageException {
+ try {
+ //TODO hash the return value and arguments
+ Document parse = Document.parse(mapper.writeValueAsString(o));
+ dbCollection.insertOne(parse);
+ } catch (Exception e) {
+ throw new StorageException("Json serialization fail", e);
+ }
+ }
+
+ @Override
+ public List<T> findAll() {
+ List<T> logList = new ArrayList<>();
+
+ MongoCursor<Document> cursor = dbCollection.find().iterator();
+ try {
+ while (cursor.hasNext()) {
+ try {
+ logList.add((T) mapper.readValue(mapper.writeValueAsString(cursor.next()), typeParameterClass));
+ } catch (Exception e) {
+ throw new StorageException("Cannot convert data from database, serialization fail", e);
+ }
+ }
+ } finally {
+ cursor.close();
+ }
+
+ return logList;
+ }
+
+ @Override
+ public void removeAll() {
+ dbCollection.drop();
+ }
+
+ @Override
+ public void saveAll(List<T> toSave) {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ public String toString() {
+ return "MongoRepository {" +
+ dbCollection.toString() +
+ '}';
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/RedisRepository.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/RedisRepository.java
new file mode 100644
index 0000000..a21b6e0
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/providers/RedisRepository.java
@@ -0,0 +1,47 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers;
+
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.Repository;
+import org.redisson.Redisson;
+import org.redisson.api.RList;
+import org.redisson.api.RedissonClient;
+
+import java.util.List;
+
+/**
+ * The type Redis repository.
+ *
+ * @param <T> the type parameter
+ */
+public class RedisRepository<T> implements Repository<T> {
+
+ private final RedissonClient redisson;
+ private final RList<T> traces;
+
+ /**
+ * Instantiates a new Redis repository.
+ */
+ public RedisRepository(){
+ redisson = Redisson.create();
+ traces = redisson.getList("tracer");
+ }
+
+ @Override
+ public void save(T t) {
+ traces.add(t);
+ }
+
+ @Override
+ public List<T> findAll() {
+ return traces.readAll();
+ }
+
+ @Override
+ public void removeAll() {
+ traces.clearExpireAsync();
+ }
+
+ @Override
+ public void saveAll(List<T> toSave) {
+ traces.addAll(toSave);
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/Repository.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/Repository.java
new file mode 100644
index 0000000..6e8ad74
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/Repository.java
@@ -0,0 +1,41 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.storage;
+
+
+import br.ufrgs.inf.prosoft.tigris.exceptions.StorageException;
+
+import java.util.List;
+
+/**
+ * The interface Repository.
+ *
+ * @param <T> the type parameter
+ */
+public interface Repository<T> {
+
+ /**
+ * Save.
+ *
+ * @param t the t
+ * @throws StorageException the storage exception
+ */
+ void save(T t) throws StorageException;
+
+ /**
+ * Find all list.
+ *
+ * @return the list
+ */
+ List<T> findAll();
+
+ /**
+ * Remove all.
+ */
+ void removeAll();
+
+ /**
+ * Save all.
+ *
+ * @param toSave the to save
+ */
+ void saveAll(List<T> toSave);
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/RepositoryFactory.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/RepositoryFactory.java
new file mode 100644
index 0000000..8b17ddd
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/storage/RepositoryFactory.java
@@ -0,0 +1,70 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.storage;
+
+import br.ufrgs.inf.prosoft.tigris.configuration.types.RepositoryType;
+import br.ufrgs.inf.prosoft.tigris.exceptions.StorageException;
+import br.ufrgs.inf.prosoft.tigris.monitoring.metadata.LogTrace;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers.AsyncFileRepository;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers.ConsoleRepository;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers.MemoryRepository;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers.MongoRepository;
+import br.ufrgs.inf.prosoft.tigris.monitoring.storage.providers.RedisRepository;
+import com.mongodb.MongoClient;
+import com.mongodb.MongoTimeoutException;
+import com.mongodb.client.MongoDatabase;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Properties;
+
+/**
+ * The type Repository factory.
+ */
+public class RepositoryFactory {
+
+ /**
+ * The Logger.
+ */
+ static Logger logger = LoggerFactory.getLogger(RepositoryFactory.class);
+
+ /**
+ * Gets repository.
+ *
+ * @param properties the properties
+ * @param repositoryType the repository type
+ * @return the repository
+ * @throws IOException the io exception
+ */
+ public static Repository getRepository(Properties properties, RepositoryType repositoryType) throws IOException {
+
+ switch (repositoryType) {
+ case MONGODB:
+ try {
+ MongoClient mongo = new MongoClient(properties.getProperty("adaptivecaching.monitoring.db.address"), Integer.parseInt(properties.getProperty("adaptivecaching.monitoring.db.port")));
+ MongoDatabase database = mongo.getDatabase(properties.getProperty("adaptivecaching.monitoring.db.scheme"));
+ Repository repository = new MongoRepository<>(database.getCollection(properties.getProperty("adaptivecaching.monitoring.db.name")), LogTrace.class);
+ logger.debug("Repository created to MongoDB: " + repository.toString());
+ return repository;
+ } catch (MongoTimeoutException e) {
+ throw new StorageException("Cannot connect with MongoDB with the defined properties.", e);
+ }
+ case REDIS:
+ logger.debug("Repository is configured to Redis.");
+ return new RedisRepository<LogTrace>();
+ case TEXTFILE:
+ AsyncFileRepository as = new AsyncFileRepository(new File("traces.txt"));
+ as.open();
+ logger.debug("Repository is configured to save logs in textfile.");
+ return as;
+ case MEMORY:
+ logger.warn("Repository is configured to save logs in Memory, which is not recommended because it can take too much from the application.");
+ return new MemoryRepository<LogTrace>();
+ case CONSOLE:
+ logger.warn("Repository is configured to just show logs in console. Nothing will be recorded");
+ return new ConsoleRepository<LogTrace>();
+ default:
+ throw new StorageException("The specified repository type [" + repositoryType +"] does not have an available implementation.");
+ }
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/AnonymousUserGetter.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/AnonymousUserGetter.java
new file mode 100644
index 0000000..b6ea4aa
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/AnonymousUserGetter.java
@@ -0,0 +1,13 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.usersession;
+
+/**
+ * The type Anonymous user getter.
+ */
+public class AnonymousUserGetter implements UserGetter {
+
+ @Override
+ public String getCurrentUser() {
+ return "Anonymous";
+ }
+
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/SpringUserGetter.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/SpringUserGetter.java
new file mode 100644
index 0000000..7ebec0a
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/SpringUserGetter.java
@@ -0,0 +1,44 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.usersession;
+
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * The type Spring user getter.
+ */
+public class SpringUserGetter implements UserGetter {
+
+ private final Object auth;
+ /**
+ * The Logger.
+ */
+ Logger logger = LoggerFactory.getLogger(SpringUserGetter.class);
+
+ /**
+ * Instantiates a new Spring user getter.
+ *
+ * @throws NoSuchMethodException the no such method exception
+ * @throws ClassNotFoundException the class not found exception
+ * @throws IllegalAccessException the illegal access exception
+ * @throws InstantiationException the instantiation exception
+ * @throws InvocationTargetException the invocation target exception
+ */
+ public SpringUserGetter() throws NoSuchMethodException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException {
+ Object contextHolder = Class.forName("org.springframework.security.core.context.SecurityContextHolder").newInstance();
+ Object context = contextHolder.getClass().getMethod("getContext").invoke(contextHolder);
+ auth = context.getClass().getMethod("getAuthentication").invoke(context);
+ }
+
+ @Override
+ public String getCurrentUser() {
+ try {
+ return (String) auth.getClass().getMethod("getName").invoke(auth);
+ } catch (Exception e) {
+ logger.debug("Not able to get the current user.", e);
+ }
+ return "Anonymous";
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/UserGetter.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/UserGetter.java
new file mode 100644
index 0000000..2e66e61
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/UserGetter.java
@@ -0,0 +1,15 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.usersession;
+
+/**
+ * The interface User getter.
+ */
+public interface UserGetter {
+
+ /**
+ * Gets current user.
+ *
+ * @return the current user
+ */
+ String getCurrentUser();
+
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/UserGetterFactory.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/UserGetterFactory.java
new file mode 100644
index 0000000..1fd1977
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/usersession/UserGetterFactory.java
@@ -0,0 +1,28 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.usersession;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The type User getter factory.
+ */
+public class UserGetterFactory {
+
+ /**
+ * The Logger.
+ */
+ static Logger logger = LoggerFactory.getLogger(UserGetterFactory.class);
+
+ /**
+ * Gets instance.
+ *
+ * @return the instance
+ */
+ public static UserGetter getInstance() {
+ try {
+ return new SpringUserGetter();
+ } catch (Exception e) {
+ return new AnonymousUserGetter();
+ }
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/Mnemos.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/Mnemos.java
new file mode 100644
index 0000000..6a613c6
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/Mnemos.java
@@ -0,0 +1,338 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.util;
+
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.reflect.MethodSignature;
+
+import java.lang.reflect.Method;
+import java.util.Arrays;
+
+/**
+ * The type Mnemos.
+ */
+@SuppressWarnings({"PMD.TooManyMethods", "PMD.AvoidUsingShortType"})
+public class Mnemos {
+
+ /**
+ * Comma between elements.
+ */
+ private static final String COMMA = ", ";
+
+ /**
+ * Dots that skip.
+ */
+ private static final String DOTS = "...";
+
+ /**
+ * Private ctor, it's a utility class.
+ */
+ private Mnemos() {
+ // intentionally empty
+ }
+
+ /**
+ * Make a string out of point.
+ *
+ * @param point The point
+ * @param trim Shall we trim long texts?
+ * @param skip Shall we skip details and output just dots?
+ * @param logthis Shall we add toString result to log?
+ * @return Text representation of it
+ * @checkstyle ParameterNumber (3 lines)
+ * @since 0.8.1
+ */
+ public static String toText(final ProceedingJoinPoint point,
+ final boolean trim, final boolean skip, final boolean logthis) {
+ final String additional;
+ if (logthis && (point.getThis() != null)) {
+ additional = point.getThis().toString();
+ } else {
+ additional = "";
+ }
+ return Mnemos.toText(
+ MethodSignature.class.cast(point.getSignature()).getMethod(),
+ point.getArgs(), additional,
+ trim, skip
+ );
+ }
+
+ /**
+ * Make a string out of point.
+ *
+ * @param point The point
+ * @param trim Shall we trim long texts?
+ * @param skip Shall we skip details and output just dots?
+ * @return Text representation of it
+ * @since 0.7.19
+ */
+ public static String toText(final ProceedingJoinPoint point,
+ final boolean trim, final boolean skip) {
+ return Mnemos.toText(
+ MethodSignature.class.cast(point.getSignature()).getMethod(),
+ point.getArgs(),
+ trim, skip
+ );
+ }
+
+ /**
+ * Make a string out of point.
+ *
+ * @param point The point
+ * @param trim Shall we trim long texts?
+ * @return Text representation of it
+ * @deprecated Use toText(MethodInfo,Object,boolean,boolean) instead
+ */
+ @Deprecated
+ public static String toText(final ProceedingJoinPoint point,
+ final boolean trim) {
+ return Mnemos.toText(point, trim, false);
+ }
+
+ /**
+ * Make a string out of point.
+ *
+ * @param point The point
+ * @param trim Shall we trim long texts?
+ * @return Text representation of it
+ * @deprecated Use toText() instead
+ */
+ @Deprecated
+ public static String toString(final ProceedingJoinPoint point,
+ final boolean trim) {
+ return Mnemos.toText(point, trim, false);
+ }
+
+ /**
+ * Make a string out of method.
+ *
+ * @param method The method
+ * @param args Actual arguments of the method
+ * @param additional Additional text to add before log line
+ * @param trim Shall we trim long texts?
+ * @param skip Shall we skip details and output just dots?
+ * @return Text representation of it
+ * @checkstyle ParameterNumber (4 lines)
+ * @since 0.8.1
+ */
+ public static String toText(final Method method, final Object[] args,
+ final String additional, final boolean trim, final boolean skip) {
+ final StringBuilder log = new StringBuilder();
+ if (additional != null) {
+ log.append(additional);
+ }
+ log.append('#').append(method.getName()).append('(');
+ if (skip) {
+ log.append(Mnemos.DOTS);
+ } else {
+ for (int pos = 0; pos < args.length; ++pos) {
+ if (pos > 0) {
+ log.append(Mnemos.COMMA);
+ }
+ log.append(Mnemos.toText(args[pos], trim, false));
+ }
+ }
+ log.append(')');
+ return log.toString();
+ }
+
+ /**
+ * Make a string out of method.
+ *
+ * @param method The method
+ * @param args Actual arguments of the method
+ * @param trim Shall we trim long texts?
+ * @param skip Shall we skip details and output just dots?
+ * @return Text representation of it
+ * @checkstyle ParameterNumber (4 lines)
+ * @since 0.7.19
+ */
+ public static String toText(final Method method, final Object[] args,
+ final boolean trim, final boolean skip) {
+ return Mnemos.toText(method, args, "", trim, skip);
+ }
+
+ /**
+ * Make a string out of method.
+ *
+ * @param method The method
+ * @param args Actual arguments of the method
+ * @param trim Shall we trim long texts?
+ * @return Text representation of it
+ * @deprecated Use toText(MethodInfo,Object,boolean,boolean) instead
+ */
+ @Deprecated
+ public static String toText(final Method method, final Object[] args,
+ final boolean trim) {
+ return Mnemos.toText(method, args, trim, false);
+ }
+
+ /**
+ * Make a string out of method.
+ *
+ * @param method The method
+ * @param args Actual arguments of the method
+ * @param trim Shall we trim long texts?
+ * @return Text representation of it
+ * @deprecated Use toText() instead
+ */
+ @Deprecated
+ public static String toString(final Method method, final Object[] args,
+ final boolean trim) {
+ return Mnemos.toText(method, args, trim, false);
+ }
+
+ /**
+ * Make a string out of an exception.
+ *
+ * @param exp The exception
+ * @return Text representation of it
+ */
+ public static String toText(final Throwable exp) {
+ final StringBuilder text = new StringBuilder();
+ text.append(exp.getClass().getName());
+ final String msg = exp.getMessage();
+ if (msg != null) {
+ text.append('(').append(msg).append(')');
+ }
+ return text.toString();
+ }
+
+ /**
+ * Make a string out of an object.
+ *
+ * @param arg The argument
+ * @param trim Shall we trim long texts?
+ * @param skip Shall we skip it with dots?
+ * @return Text representation of it
+ * @since 0.7.19
+ */
+ @SuppressWarnings("PMD.AvoidCatchingThrowable")
+ public static String toText(final Object arg, final boolean trim,
+ final boolean skip) {
+ final StringBuilder text = new StringBuilder();
+ if (arg == null) {
+ text.append("NULL");
+ } else if (skip) {
+ text.append(Mnemos.DOTS);
+ } else {
+ try {
+ final String mnemo = Mnemos.toText(arg);
+ if (trim) {
+ text.append(String.format("%[text]s", mnemo));
+ } else {
+ text.append(mnemo);
+ }
+ // @checkstyle IllegalCatch (1 line)
+ } catch (final Throwable ex) {
+ text.append(
+ String.format(
+ "[%s thrown %s]",
+ arg.getClass().getName(),
+ Mnemos.toText(ex)
+ )
+ );
+ }
+ }
+ return text.toString();
+ }
+
+ /**
+ * Make a string out of an object.
+ *
+ * @param arg The argument
+ * @param trim Shall we trim long texts?
+ * @return Text representation of it
+ * @deprecated Use toText(Object,boolean,boolean) instead
+ */
+ @Deprecated
+ public static String toText(final Object arg, final boolean trim) {
+ return Mnemos.toText(arg, trim, false);
+ }
+
+ /**
+ * Make a string out of an object.
+ *
+ * @param arg The argument
+ * @param trim Shall we trim long texts?
+ * @return Text representation of it
+ * @deprecated Use toText() instead
+ */
+ @Deprecated
+ public static String toString(final Object arg, final boolean trim) {
+ return Mnemos.toText(arg, trim, false);
+ }
+
+ /**
+ * Make a string out of an object.
+ *
+ * @param arg The argument
+ * @return Text representation of it
+ */
+ private static String toText(final Object arg) {
+ String text;
+ if (arg.getClass().isArray()) {
+ if (arg instanceof Object[]) {
+ text = Mnemos.objectArrays((Object[]) arg);
+ } else {
+ text = Mnemos.primitiveArrays(arg);
+ }
+ } else {
+ final String origin = arg.toString();
+ if (arg instanceof String || origin.contains(" ")
+ || origin.isEmpty()) {
+ text = String.format("'%s'", origin);
+ } else {
+ text = origin;
+ }
+ }
+ return text;
+ }
+
+ /**
+ * Text representation of object arrays.
+ *
+ * @param arg Array to change into String.
+ * @return Array in String.
+ */
+ private static String objectArrays(final Object... arg) {
+ final StringBuilder bldr = new StringBuilder();
+ bldr.append('[');
+ for (final Object item : arg) {
+ if (bldr.length() > 1) {
+ bldr.append(Mnemos.COMMA);
+ }
+ bldr.append(Mnemos.toText(item, false, false));
+ }
+ return bldr.append(']').toString();
+ }
+
+ /**
+ * Text representation of primitive arrays.
+ *
+ * @param arg Array to change into String.
+ * @return Array in String.
+ */
+ private static String primitiveArrays(final Object arg) {
+ final String text;
+ if (arg instanceof char[]) {
+ text = Arrays.toString((char[]) arg);
+ } else if (arg instanceof byte[]) {
+ text = Arrays.toString((byte[]) arg);
+ } else if (arg instanceof short[]) {
+ text = Arrays.toString((short[]) arg);
+ } else if (arg instanceof int[]) {
+ text = Arrays.toString((int[]) arg);
+ } else if (arg instanceof long[]) {
+ text = Arrays.toString((long[]) arg);
+ } else if (arg instanceof float[]) {
+ text = Arrays.toString((float[]) arg);
+ } else if (arg instanceof double[]) {
+ text = Arrays.toString((double[]) arg);
+ } else if (arg instanceof boolean[]) {
+ text = Arrays.toString((boolean[]) arg);
+ } else {
+ text = "[unknown]";
+ }
+ return text;
+ }
+}
+
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/threads/NamedThreads.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/threads/NamedThreads.java
new file mode 100644
index 0000000..f73c9eb
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/threads/NamedThreads.java
@@ -0,0 +1,52 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.util.threads;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * The type Named threads.
+ */
+public class NamedThreads implements ThreadFactory {
+
+ /**
+ * Name of the thread.
+ */
+ private final transient String name;
+ /**
+ * Purpose of these threads.
+ */
+ private final transient String purpose;
+ /**
+ * Thread group to use.
+ */
+ private final transient ThreadGroup group;
+ /**
+ * The Logger.
+ */
+ Logger logger = LoggerFactory.getLogger(NamedThreads.class);
+
+ /**
+ * Public ctor.
+ *
+ * @param suffix Suffix of thread names
+ * @param desc Description of purpose
+ */
+ public NamedThreads(final String suffix, final String desc) {
+ this.name = String.format("%s", suffix);
+ this.purpose = desc;
+ this.group = new ThreadGroup("tigris");
+ }
+
+ @Override
+ public Thread newThread(final Runnable runnable) {
+ final Thread thread = new Thread(this.group, runnable);
+ thread.setName(this.name);
+ thread.setDaemon(true);
+ logger.info("tigris started new daemon thread " + this.name + " for " + this.purpose);
+ return thread;
+ }
+
+}
+
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/threads/VerboseRunnable.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/threads/VerboseRunnable.java
new file mode 100644
index 0000000..9b88e6c
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/monitoring/util/threads/VerboseRunnable.java
@@ -0,0 +1,206 @@
+package br.ufrgs.inf.prosoft.tigris.monitoring.util.threads;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.concurrent.Callable;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * The type Verbose runnable.
+ */
+@SuppressWarnings("PMD.DoNotUseThreads")
+public final class VerboseRunnable implements Runnable {
+
+ /**
+ * Original runnable.
+ */
+ private final transient Runnable origin;
+ /**
+ * Rethrow exceptions (TRUE) or swallow them?
+ */
+ private final transient boolean rethrow;
+ /**
+ * Shall we report a full stacktrace?
+ */
+ private final transient boolean verbose;
+ /**
+ * The Logger.
+ */
+ Logger logger = LoggerFactory.getLogger(NamedThreads.class);
+
+ /**
+ * Default constructor, doesn't swallow exceptions.
+ *
+ * @param runnable Runnable to wrap
+ */
+ public VerboseRunnable(final Runnable runnable) {
+ this(runnable, false);
+ }
+
+ /**
+ * Default constructor, doesn't swallow exceptions.
+ *
+ * @param callable Callable to wrap
+ * @since 0.7.17
+ */
+ public VerboseRunnable(final Callable<?> callable) {
+ this(callable, false);
+ }
+
+ /**
+ * Default constructor, doesn't swallow exceptions.
+ *
+ * @param callable Callable to wrap
+ * @param swallow Shall we swallow exceptions ({@code TRUE}) or re-throw ({@code FALSE})? Exception swallowing means that {@link #run()} will never throw any exceptions (in any case all exceptions are logged using {@link Logger}.
+ * @since 0.1.10
+ */
+ public VerboseRunnable(final Callable<?> callable, final boolean swallow) {
+ this(callable, swallow, true);
+ }
+
+ /**
+ * Default constructor.
+ *
+ * @param callable Callable to wrap
+ * @param swallow Shall we swallow exceptions ({@code TRUE}) or re-throw ({@code FALSE})? Exception swallowing means that {@link #run()} will never throw any exceptions (in any case all exceptions are logged using {@link Logger}.
+ * @param vrbs Shall we report the entire stacktrace of the exception ({@code TRUE}) or just its message in one line ({@code FALSE})
+ * @since 0.7.17
+ */
+ @SuppressWarnings("PMD.AvoidCatchingGenericException")
+ public VerboseRunnable(final Callable<?> callable,
+ final boolean swallow, final boolean vrbs) {
+ this(
+ new Runnable() {
+ @Override
+ public void run() {
+ try {
+ callable.call();
+ } catch (final InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException(ex);
+ // @checkstyle IllegalCatch (1 line)
+ } catch (final Exception ex) {
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return callable.toString();
+ }
+ },
+ swallow,
+ vrbs
+ );
+ }
+
+ /**
+ * Default constructor, with configurable behavior for exceptions.
+ *
+ * @param runnable Runnable to wrap
+ * @param swallow Shall we swallow exceptions ({@code TRUE}) or re-throw ({@code FALSE})? Exception swallowing means that {@link #run()} will never throw any exceptions (in any case all exceptions are logged using {@link Logger}.
+ * @since 0.1.4
+ */
+ public VerboseRunnable(final Runnable runnable, final boolean swallow) {
+ this(runnable, swallow, true);
+ }
+
+ /**
+ * Default constructor, with fully configurable behavior.
+ *
+ * @param runnable Runnable to wrap
+ * @param swallow Shall we swallow exceptions ({@code TRUE}) or re-throw ({@code FALSE})? Exception swallowing means that {@link #run()} will never throw any exceptions (in any case all exceptions are logged using {@link Logger}.
+ * @param vrbs Shall we report the entire stacktrace of the exception ({@code TRUE}) or just its message in one line ({@code FALSE})
+ * @since 0.7.17
+ */
+ @SuppressWarnings("PMD.BooleanInversion")
+ public VerboseRunnable(final Runnable runnable,
+ final boolean swallow, final boolean vrbs) {
+ this.origin = runnable;
+ this.rethrow = !swallow;
+ this.verbose = vrbs;
+ }
+
+ /**
+ * {@inheritDoc}
+ * <p>
+ * <p>We catch {@link RuntimeException} and {@link Error} here. All other
+ * types of exceptions are "checked exceptions" and won't be thrown out
+ * of {@code Runnable#run()} method.
+ */
+ @Override
+ @SuppressWarnings("PMD.AvoidCatchingGenericException")
+ public void run() {
+ try {
+ this.origin.run();
+ // @checkstyle IllegalCatch (1 line)
+ } catch (final RuntimeException ex) {
+ if (this.rethrow) {
+ logger.warn("escalated exception: %s", this.tail(ex));
+ throw ex;
+ }
+ logger.warn("swallowed exception: %s", this.tail(ex));
+ // @checkstyle IllegalCatch (1 line)
+ } catch (final Error error) {
+ if (this.rethrow) {
+ logger.error("escalated error: %s", this.tail(error));
+ throw error;
+ }
+ logger.error("swallowed error: %s", this.tail(error));
+ }
+ try {
+ TimeUnit.MICROSECONDS.sleep(1L);
+ } catch (final InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ throw new IllegalStateException(ex);
+ }
+ }
+
+ /**
+ * Make a tail of the error/warning message, using the exception thrown.
+ *
+ * @param throwable The exception/error caught
+ * @return The message to show in logs
+ */
+ private String tail(final Throwable throwable) {
+ final String tail;
+ if (this.verbose) {
+ tail = String.format("%[exception]s", throwable);
+ } else {
+ tail = String.format(
+ "%[type]s('%s')",
+ throwable,
+ throwable.getMessage()
+ );
+ }
+ return tail;
+ }
+
+ public boolean equals(Object o) {
+ if (o == this) return true;
+ if (!(o instanceof VerboseRunnable)) return false;
+ final VerboseRunnable other = (VerboseRunnable) o;
+ if (!other.canEqual((Object) this)) return false;
+ return true;
+ }
+
+ public int hashCode() {
+ int result = 1;
+ return result;
+ }
+
+ /**
+ * Can equal boolean.
+ *
+ * @param other the other
+ * @return the boolean
+ */
+ protected boolean canEqual(Object other) {
+ return other instanceof VerboseRunnable;
+ }
+
+ public String toString() {
+ return "br.ufrgs.inf.prosoft.adaptivecaching.monitoring.util.VerboseRunnable(logger=" + this.logger + ", origin=" + this.origin + ", rethrow=" + this.rethrow + ", verbose=" + this.verbose + ")";
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/FrequencyDataSet.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/FrequencyDataSet.java
new file mode 100644
index 0000000..ea080d0
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/FrequencyDataSet.java
@@ -0,0 +1,43 @@
+package br.ufrgs.inf.prosoft.tigris.sampling;
+
+import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Set;
+
+public class FrequencyDataSet {
+
+ private Map<Granularity, Integer> granularityPopulation = new HashMap<>();
+ private long n;
+
+ public void addItem(Granularity item) {
+ if (granularityPopulation.containsKey(item)){
+ granularityPopulation.put(item, granularityPopulation.get(item) + 1);
+ }
+ else {
+ granularityPopulation.put(item, 1);
+ }
+ n++;
+ }
+
+ public long getTotalItems(){
+ return n;
+ }
+
+ public double getProportion(Granularity granularity){
+ return granularityPopulation.get(granularity) / getTotalItems();
+ }
+
+ public Set<Granularity> getGranularities(){
+ return granularityPopulation.keySet();
+ }
+
+ public SummaryStatistics getAsDescriptiveStatistics(){
+ SummaryStatistics summaryStatistics = new SummaryStatistics();
+ for (Map.Entry<Granularity, Integer> populationEntry : granularityPopulation.entrySet()) {
+ summaryStatistics.addValue(populationEntry.getValue());
+ }
+ return summaryStatistics;
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Granularity.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Granularity.java
new file mode 100644
index 0000000..49e6881
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Granularity.java
@@ -0,0 +1,28 @@
+package br.ufrgs.inf.prosoft.tigris.sampling;
+
+import java.util.Objects;
+
+public class Granularity {
+
+ public GranularityType granularityType;
+ public String name;
+
+ public Granularity(GranularityType granularityType, String name) {
+ this.granularityType = granularityType;
+ this.name = name;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+ Granularity that = (Granularity) o;
+ return granularityType == that.granularityType &&
+ Objects.equals(name, that.name);
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(granularityType, name);
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/GranularityType.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/GranularityType.java
new file mode 100644
index 0000000..e3a39d4
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/GranularityType.java
@@ -0,0 +1,8 @@
+package br.ufrgs.inf.prosoft.tigris.sampling;
+
+public enum GranularityType {
+ PACKAGE,
+ CLASS,
+ METHOD,
+ CALL;
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/PerformanceBaselineDataSet.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/PerformanceBaselineDataSet.java
new file mode 100644
index 0000000..e35963a
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/PerformanceBaselineDataSet.java
@@ -0,0 +1,32 @@
+package br.ufrgs.inf.prosoft.tigris.sampling;
+
+import org.apache.commons.math3.stat.descriptive.SummaryStatistics;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+public class PerformanceBaselineDataSet {
+
+ private Map<Granularity, SummaryStatistics> granularityBaseline = new HashMap<>();
+ private long n;
+
+ public void addItem(Granularity item, long executionTime) {
+ SummaryStatistics statistics = new SummaryStatistics();
+ if (granularityBaseline.containsKey(item)){
+ statistics = granularityBaseline.get(item);
+ }
+ statistics.addValue(executionTime);
+ granularityBaseline.put(item, statistics);
+ n++;
+ }
+
+ public long getTotalItems(){
+ return n;
+ }
+
+ public Set<Granularity> getGranularities(){
+ return granularityBaseline.keySet();
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Sampling.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Sampling.java
new file mode 100644
index 0000000..2a00830
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/Sampling.java
@@ -0,0 +1,187 @@
+package br.ufrgs.inf.prosoft.tigris.sampling;
+
+import org.apache.commons.collections4.queue.CircularFifoQueue;
+import org.apache.commons.math3.distribution.BinomialDistribution;
+import org.apache.commons.math3.ml.neuralnet.sofm.util.ExponentialDecayFunction;
+import org.apache.commons.math3.stat.inference.TestUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.SecureRandom;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Queue;
+
+/**
+ * The type Sampling decision.
+ */
+public class Sampling implements Runnable {
+
+ private boolean samplingEnabled = true;
+ private boolean performanceBaselineEnabled = false;
+ private double samplingRate = 0.5; // in percentage, 0 to 1
+ private FrequencyDataSet population = new FrequencyDataSet(), sample = new FrequencyDataSet();
+ private PerformanceBaselineDataSet performanceBaselineDataSet = new PerformanceBaselineDataSet();
+ private Queue<PerformanceBaselineDataSet> lastFourPerformanceBaselineDataSets = new CircularFifoQueue<>(4);
+ private ExponentialDecayFunction decayingPrecision;
+
+ Logger logger = LoggerFactory.getLogger(Sampling.class);
+
+ /**
+ * z confidence value, ex: 1.96 for 95%
+ * p proportion of the population, 0.5 is default
+ * e margin of error, ex: 0.05 for 5%
+ */
+ private double z = 1.96, p = 0.5, e = 0.05;
+
+ public Sampling(double initialSamplingRate, int cycleLengthInSeconds) {
+
+ samplingRate = initialSamplingRate;
+ //3600 seconds = 1h
+ decayingPrecision = new ExponentialDecayFunction(100, 0.01, cycleLengthInSeconds);
+ }
+
+ public boolean samplingDecision(Granularity granularity) {
+ population.addItem(granularity);
+
+ if(performanceBaselineEnabled) {
+ return false;
+ }
+
+ boolean decision = samplingEnabled
+ && new BinomialDistribution(1, samplingRate).sample() == 1 // sampling rate evaluation
+ && population.getProportion(granularity) > sample.getProportion(granularity); // sample has not enough items of that granularity compared to the population
+
+ if (decision)
+ sample.addItem(granularity);
+
+ return decision;
+ }
+
+ public void setSamplingRate(double samplingRate){
+ this.samplingRate = samplingRate;
+ }
+
+ public boolean isReady() {
+ return
+ // margin of error is lower than threshold
+ getSampleSizeErrorMargin() < e
+ // the sample has the min sample size based on the population
+ && sample.getTotalItems() > getMinimumSampleSize()
+ // proportion test
+ && isSameProportion()
+ // t-test
+ && tTestEvaluation();
+ }
+
+ private double decayingConfidence(int timeInSeconds){
+ return decayingPrecision.value(timeInSeconds);
+ }
+
+ private boolean tTestEvaluation() {
+ //To test the (one-sample t-test - compare with the population mean)
+ // hypothesis sample mean = mu at the 95% level
+ return TestUtils.tTest(population.getAsDescriptiveStatistics().getMean(),
+ sample.getAsDescriptiveStatistics(),
+ 0.05);
+ }
+
+ //sample proportion is the same as population
+ public boolean isSameProportion() {
+ return population.getGranularities().stream().allMatch(granularity -> population.getProportion(granularity) == sample.getProportion(granularity));
+ }
+
+ /**
+ * @return the minimum sample size for the population
+ */
+ public long getMinimumSampleSize() {
+ long n_inf = (long) ((Math.pow(z, 2) * p * (1 - p)) / Math.pow(e, 2));
+ return n_inf / (1 + ((n_inf - 1) / population.getTotalItems()));
+ }
+
+ public long getMinimumSampleSize(long n) {
+ long n_inf = (long) ((Math.pow(z, 2) * p * (1 - p)) / Math.pow(e, 2));
+ return n_inf / (1 + ((n_inf - 1) / n));
+ }
+
+ public double getSampleSizeErrorMargin() {
+ double e_n_inf = Math.sqrt((Math.pow(z, 2) * p * (1 - p)) / sample.getTotalItems());
+ return e_n_inf * Math.sqrt((population.getTotalItems() - sample.getTotalItems()) / (population.getTotalItems() - 1));
+ }
+
+ public void disable() {
+ samplingEnabled = false;
+ }
+
+ public void enable() {
+ samplingEnabled = true;
+ }
+
+ public boolean isSamplingEnabled() {
+ return samplingEnabled;
+ }
+
+ public double getSamplingRate() {
+ return samplingRate;
+ }
+
+ public void startMonitoringCycle() {
+
+ }
+
+ public void endMonitoringCycle() {
+
+ }
+
+ public boolean shouldCollectPerformanceBaseline() {
+ return new BinomialDistribution(1, 0.1).sample() == 1;
+ }
+
+ public void adaptSamplingRate() {
+ //TODO
+ }
+
+ @Override
+ public void run() {
+ logger.info("Running sampling adaptation.");
+
+ if (isReady()) {
+ logger.info("Sample is ready, releasing for analysis and resetting...");
+ //TODO
+ releaseForAnalysis();
+ reset();
+ return;
+ }
+ if (shouldCollectPerformanceBaseline()) {
+ enablePerformanceBaseline();
+ return;
+ }
+ adaptSamplingRate();
+ }
+
+ private void enablePerformanceBaseline() {
+ performanceBaselineEnabled = true;
+ }
+
+ public void releaseForAnalysis() {
+ }
+
+ public void reset() {
+ }
+
+ public boolean isPerformanceBaselineEnabled() {
+ return performanceBaselineEnabled;
+ }
+
+ public void addPerformanceBaselineItem(Granularity granularity, long executionTime) {
+ this.performanceBaselineDataSet.addItem(granularity, executionTime);
+
+ if(this.performanceBaselineDataSet.getTotalItems() >=
+ getMinimumSampleSize(this.performanceBaselineDataSet.getTotalItems())) {
+ enable();
+ this.performanceBaselineEnabled = false;
+ lastFourPerformanceBaselineDataSets.add(this.performanceBaselineDataSet);
+ this.performanceBaselineDataSet = new PerformanceBaselineDataSet();
+ }
+ }
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/SamplingConfiguration.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/SamplingConfiguration.java
new file mode 100644
index 0000000..bb71c4b
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/tigris/sampling/SamplingConfiguration.java
@@ -0,0 +1,23 @@
+package br.ufrgs.inf.prosoft.tigris.sampling;
+
+import java.lang.annotation.*;
+
+/**
+ * The interface Criteria.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@Documented
+public @interface SamplingConfiguration {
+
+ GranularityType granularity() default GranularityType.CALL;
+
+ /**
+ * Sampling percentage int.
+ *
+ * @return the int
+ */
+ int samplingPercentage() default 50;
+
+ int cycleTimeInSeconds() default 3600; //3600 seconds = 1h
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/utils/ConfigurationUtils.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/utils/ConfigurationUtils.java
new file mode 100644
index 0000000..d0b385a
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/utils/ConfigurationUtils.java
@@ -0,0 +1,39 @@
+package br.ufrgs.inf.prosoft.utils;
+
+import br.ufrgs.inf.prosoft.tigris.exceptions.ConfigurationException;
+import br.ufrgs.inf.prosoft.tigris.monitoring.aspects.CustomMonitoring;
+import org.reflections.Reflections;
+import org.reflections.scanners.ResourcesScanner;
+import org.reflections.scanners.SubTypesScanner;
+import org.reflections.scanners.TypeAnnotationsScanner;
+import org.reflections.util.ClasspathHelper;
+import org.reflections.util.ConfigurationBuilder;
+
+import java.util.Set;
+
+public final class ConfigurationUtils {
+
+ private static Reflections reflections = new Reflections(
+ new ConfigurationBuilder()
+ .setUrls(ClasspathHelper.forClassLoader())
+ .setScanners(new SubTypesScanner(false), new ResourcesScanner(), new TypeAnnotationsScanner()));
+
+ public static Class<?> getAvailableConfigurationClass(Class annotation) {
+ Set<Class<?>> configurations =
+ reflections.getTypesAnnotatedWith(annotation);
+ if (configurations.isEmpty())
+ throw new ConfigurationException(annotation.getName() + " not found.");
+ if (configurations.size() > 1)
+ throw new ConfigurationException(annotation.getName() + " has too many definitions.");
+ return configurations.iterator().next();
+ }
+
+ public static Class<?> getAvailableCustomMonitoringClass() {
+ Set<Class<? extends CustomMonitoring>> configurations =
+ reflections.getSubTypesOf(CustomMonitoring.class);
+ if (configurations.size() > 1)
+ throw new ConfigurationException("Multiple implementations of CustomMonitoring.class.");
+ return configurations.iterator().next();
+ }
+
+}
diff --git a/tigris/src/main/java/br/ufrgs/inf/prosoft/utils/StatisticalTest.java b/tigris/src/main/java/br/ufrgs/inf/prosoft/utils/StatisticalTest.java
new file mode 100644
index 0000000..53896a9
--- /dev/null
+++ b/tigris/src/main/java/br/ufrgs/inf/prosoft/utils/StatisticalTest.java
@@ -0,0 +1,30 @@
+
+package br.ufrgs.inf.prosoft.utils;
+
+import org.apache.commons.math3.distribution.NormalDistribution;
+import org.apache.commons.math3.stat.inference.TestUtils;
+
+/**
+ * The type Statistical test.
+ */
+public class StatisticalTest {
+
+ /**
+ * Is normal distribution boolean.
+ *
+ * @param sample the sample
+ * @param significanceLevel the significance level
+ * @return the boolean
+ */
+ public static boolean isNormalDistribution(double[] sample, double significanceLevel){
+ final NormalDistribution unitNormal = new NormalDistribution(0d, 1d);
+
+ //alpha significance level (equiv. 100 * (1-alpha)% confidence) where 0 < alpha < 1 use
+ //Significance level of 95%: 100 * (1-0.05)
+
+ //the test below compares p-value < alpha = 0.05
+ //As a rule of thumb, we reject the null hypothesis if p < 0.05.
+ //So if p < 0.05, we don't believe that our variable follows a normal distribution in our population.
+ return !TestUtils.kolmogorovSmirnovTest(unitNormal, sample, significanceLevel);
+ }
+}
tigris/src/main/resources/logback.xml 40(+40 -0)
diff --git a/tigris/src/main/resources/logback.xml b/tigris/src/main/resources/logback.xml
new file mode 100644
index 0000000..8ba33ed
--- /dev/null
+++ b/tigris/src/main/resources/logback.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration debug="false" status="trace">
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d{yyyy-MM-dd_HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <!--<property name="DEV_HOME" value="/home/jhonnymertz"/>-->
+
+ <!--<appender name="FILE" class="ch.qos.logback.core.FileAppender">-->
+ <!--<file>${DEV_HOME}/adaptivecaching.log</file>-->
+ <!--<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">-->
+ <!--<pattern>%d{yyyy-MM-dd_HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>-->
+ <!--</encoder>-->
+ <!--</appender>-->
+
+ <logger name="br.ufrgs.inf.prosoft.tigris" level="info" additivity="false">
+ <appender-ref ref="STDOUT"/>
+ <!--<appender-ref ref="FILE"/>-->
+ </logger>
+
+ <logger name="org.springframework" level="info"/>
+ <logger name="org.hibernate" level="info"/>
+
+ <logger name="org.thymeleaf.TemplateEngine" level="info"/>
+ <logger name="com.mysema" level="info"/>
+ <logger name="com.salesmanager.web" level="error"/>
+
+ <logger name="org.mongodb.driver" level="INFO"/>
+
+ <logger name="org.reflections.Reflections" level="INFO"/>
+
+ <!-- By default, the level of the root level is set to DEBUG -->
+ <root level="debug">
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
diff --git a/tigris/src/test/java/br/ufrgs/inf/prosoft/tigris/sampling/StaticMetricsTest.java b/tigris/src/test/java/br/ufrgs/inf/prosoft/tigris/sampling/StaticMetricsTest.java
new file mode 100644
index 0000000..e9f9094
--- /dev/null
+++ b/tigris/src/test/java/br/ufrgs/inf/prosoft/tigris/sampling/StaticMetricsTest.java
@@ -0,0 +1,14 @@
+package br.ufrgs.inf.prosoft.tigris.sampling;
+
+import br.ufrgs.inf.prosoft.tigris.metrics.StaticMetrics;
+
+import java.io.IOException;
+
+public class StaticMetricsTest {
+
+// @Test
+ public void loadMetrics() throws IOException {
+ StaticMetrics staticMetrics = new StaticMetrics("/home/jhonnymertz/workspace/adaptive-caching-framework/understand/shopizer.csv");
+ System.out.println(staticMetrics.getAllMetrics());
+ }
+}
diff --git a/tigris/src/test/java/br/ufrgs/inf/prosoft/tigris/sampling/Statistics.java b/tigris/src/test/java/br/ufrgs/inf/prosoft/tigris/sampling/Statistics.java
new file mode 100644
index 0000000..37d15da
--- /dev/null
+++ b/tigris/src/test/java/br/ufrgs/inf/prosoft/tigris/sampling/Statistics.java
@@ -0,0 +1,104 @@
+package br.ufrgs.inf.prosoft.tigris.sampling;
+
+import br.ufrgs.inf.prosoft.utils.StatisticalTest;
+import org.apache.commons.math3.distribution.BinomialDistribution;
+import org.apache.commons.math3.ml.neuralnet.sofm.util.ExponentialDecayFunction;
+import org.apache.commons.math3.random.EmpiricalDistribution;
+import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;
+import org.junit.Assert;
+import org.junit.Test;
+import umontreal.ssj.gof.GofStat;
+import umontreal.ssj.probdist.NormalDist;
+
+import java.util.Arrays;
+
+public class Statistics {
+
+ public void test(double[] sample){
+ DescriptiveStatistics frequencies = new DescriptiveStatistics();
+ for(double d : sample){
+ frequencies.addValue(d);
+ }
+ System.out.println("KS pvalue should be higher than significance level (0.05): "
+ + ((StatisticalTest.isNormalDistribution(frequencies.getValues(), 0.5)) ? " yes" : " no"));
+
+ Arrays.sort(sample);
+ double[] adresult = GofStat.andersonDarling(sample, new NormalDist());
+ System.out.println("Dstat: " + adresult[0] + " pvalue:" + adresult[1]);
+ }
+
+ @Test
+ public void normalization(){
+
+ double[] sample = {4.50e+1, 5.20e+1, 4.30e+1,4.90e+1,4.80e+1,5.40e+1,5.30e+1,4.90e+1,4.90e+1};
+ test(sample);
+// double d_stat = TestUtils.kolmogorovSmirnovStatistic(unitNormal, sample);
+
+ test(new double[]{2, 3, 3, 4, 3, 3, 2});
+ test(new double[]{93.0, 103.0, 95.0, 101.0, 91.0, 105.0, 96.0, 94.0, 101.0});
+ test(new double[]{26, 33, 65, 28, 34, 25, 55, 44, 50, 36, 26, 37, 43, 62, 35, 38, 45, 32, 28, 34});
+ test(new double[]{20, 15, 26, 32, 18, 28, 35, 14, 26, 22, 17});
+ test(new double[]{565, 116, 69, 78, 319, 501, 633, 655, 848, 1226, 1633, 2459, 3375, 4669, 6152, 7436, 9526, 12619, 12455, 7113, 2104, 241});
+ test(new double[]{9.0964868007346, 9.05841203941304, 10.4561973049786, 11.4290519020928, 9.80406201402498, 8.00495612940127, 10.9560511244096, 10.1628617100704, 9.5404880778431, 8.31409730387256, 10.5850756498228, 8.94573786127721, 9.67884319773862, 10.2674043676357, 8.88672394683865, 12.3582450296069, 11.3323127708954, 8.98248290920661, 10.0195219450004, 10.2978738217737, 8.12677492317708, 8.89950454463237, 9.77696513465945, 10.514898975753, 10.672289112785, 10.694498534934, 10.1821924686095, 11.142573152688, 9.77887037458247, 11.0049010075665, 10.2154096657666, 9.85591747299326, 8.42526783048421, 9.81110530928476, 10.8197900519108, 10.3297258106185, 10.0227636883243, 11.2987975587751, 8.44486064977259, 11.5225576269198, 8.12224293685105, 10.0007857247433, 11.5138891839899, 9.3831006575966, 8.12476347171477, 9.09856941554296, 8.62142101694361, 9.46470997205525, 9.41790826247466, 8.48703712461685, 10.2715646747326, 9.33049825280273, 10.6699012172013, 9.18072916828602, 8.41649117636405, 9.33567958667778, 10.049876040196, 11.3127990745231, 11.6586896309607, 8.89584835227498, 9.70752934278868, 8.86246838225148, 10.2161331491117, 9.85382278972802, 9.05752306126498, 11.3505257827677, 8.44762586593948, 9.15864805742191, 9.57567977055168, 11.7688636824855, 10.2024057615904, 10.3104037114343, 10.1580229817262, 10.0680455332437, 9.91423812485741, 9.94504630463698, 8.52395458741078, 10.2521461941422, 10.1036308255006, 9.18956474594286, 9.96954494398682, 9.78104199181701, 9.92382325277581, 8.63597929467619, 9.21347593192941, 9.89855218854907, 11.0121033672495, 9.63264753881509, 10.6248219653335, 10.486189745896, 10.5244954307719, 8.53373914749441, 9.3983432649672, 11.0903843560089, 9.44459585712476, 10.0859955061661, 11.3387705764577, 9.84586163010232, 11.4741429769762, 10.441848372811, 9.55045713422817, 10.6267816577505, 10.4443547888383, 9.89379735185878, 9.72354469139489, 9.12066768498717, 10.0661011719204, 10.3323960556678, 11.505082984854, 10.7859172636304, 11.8287650643979, 10.4116817822928, 10.1463678500856, 9.25763259285018, 8.95707586672983, 10.2875032181518, 9.73858400501329, 8.70348547219759, 9.14762353313967, 9.82731108963226, 10.849859681053, 10.5423597494633, 8.58089709606474, 8.47543607758568, 9.15931239135863, 9.27560590704311, 10.5368346988892, 9.83172309690772, 9.16056782666665, 9.2012213506938, 9.66210982564607, 10.5190510051426, 8.62151894187291, 9.90524992792067, 10.138710665855, 9.30829258153867, 10.8290286694516, 12.1178308609245, 9.50553095064575, 9.95398394895215, 12.0245206821336, 10.2477642478284, 8.55456942605116, 11.6070473804305, 10.1562361056329, 9.43630469486672, 10.3170511798909, 9.01652208335814, 9.86807032119238, 10.8489404774946, 10.7826270556231, 10.0870496269544, 10.9833771671752, 10.117870221528, 10.5996195058974, 11.0329621602774, 10.4132314334195, 9.74158685858501, 9.48294737415442, 9.86849961215289, 9.91013610376275, 7.83468897764531, 10.514842063025, 11.4804302009497, 10.1196554587607, 10.39118899319, 9.62353860043821, 8.97417942332151, 9.35318610423095, 10.3556147738602, 11.9155451973464, 8.27736636882948, 9.32257441690336, 10.252826149316, 9.7535116214245, 10.9162334195511, 10.1678750592301, 10.9559458131311, 8.60102009638715, 9.2628210057096, 9.78873072091305, 8.35114511028973, 10.1637620813489, 9.46509546070039, 8.29088640463265, 11.1023522365736, 8.23070146296037, 9.21646018542163, 8.78784331889502, 10.2988093157657, 10.537263338189, 10.8732424762337, 10.0350589477381, 8.64559077573426, 9.82216826151432, 12.9407607539763, 9.58702405409422, 8.68540299123893, 8.90868676037665, 9.83588804978778, 9.45456213634599, 9.88659569326315, 9.46423529809919, 8.51323166422944, 9.06114471768126, 7.0763024483504, 11.3048132198129, 10.9310615911962, 10.243499074504, 12.2494232010316, 9.92165717330645, 11.1055496127494, 10.3824281515872, 10.9363315824212, 9.47366362946216, 10.5074206502971, 9.92574082866448, 9.98656454297378, 10.3434126760638, 9.16080972535168, 10.6397501919665, 8.88350130241863, 10.2657096522122, 9.29382344383002, 10.2707284719202, 10.6950720070684, 10.5671180659654, 8.58795244344749, 9.50269445287456, 10.5319945577608, 10.8884770667036, 10.2324575999004, 9.32891854565115, 8.9740321510626, 10.4126653257714, 9.98364553876901, 10.0039606824373, 11.373614936858, 8.33792326220959, 8.42278194538867, 8.92005049489281, 9.56511841862538, 10.6829314510294, 10.6412977717579, 11.1075024003369, 10.5483699986234, 10.3218235881083, 9.91439811222411, 10.0027837642839, 9.50565960381905});
+
+
+ //https://github.com/umontreal-simul/ssj
+
+ //http://jdistlib.sourceforge.net/
+// NormalityTest.xxx_statistic(double[] x);
+// NormalityTest.xxx_p_value(double stat, int df);
+ }
+
+ @Test
+ public void testCalculatePercentile() {
+ //given
+ double[] values = new double[]{1,2,3,4,5};
+
+ EmpiricalDistribution distribution = new EmpiricalDistribution(values.length);
+ distribution.load(values);
+
+ //when
+ double percentile = distribution.cumulativeProbability(4);
+
+ //then
+ Assert.assertEquals(percentile, 0.8, 0.1);
+ }
+
+ @Test
+ public void testQuantiles(){
+ DescriptiveStatistics frequencies = new DescriptiveStatistics();
+ double[] values = new double[]{7.1, 7.4, 7.5, 7.7, 7.8, 7.9};
+ for(double d : values)
+ frequencies.addValue(d);
+
+ //then
+ Assert.assertEquals(frequencies.getPercentile(25), 7.325, 0.1);
+ Assert.assertEquals(frequencies.getPercentile(50), 7.6, 0.1);
+ Assert.assertEquals(frequencies.getPercentile(75), 7.825, 0.1);
+ }
+
+ @Test
+ public void test2Quantiles(){
+ DescriptiveStatistics frequencies = new DescriptiveStatistics();
+ double[] values = new double[]{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,2,2,4};
+ for(double d : values)
+ frequencies.addValue(d);
+
+ //then
+ System.out.println(frequencies.getPercentile(25));
+ System.out.println(frequencies.getPercentile(50));
+ System.out.println(frequencies.getPercentile(75));
+ }
+
+ @Test
+ public void testExponentialDecay(){
+ ExponentialDecayFunction decay = new ExponentialDecayFunction(100, 0.01, 3600);
+ System.out.println(decay.value(0));
+ System.out.println(decay.value(1));
+ System.out.println(decay.value(2));
+ System.out.println(decay.value(3));
+ System.out.println(decay.value(4));
+
+ }
+
+}
tigris/src/test/resources/logback-test.xml 35(+35 -0)
diff --git a/tigris/src/test/resources/logback-test.xml b/tigris/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..78fcc96
--- /dev/null
+++ b/tigris/src/test/resources/logback-test.xml
@@ -0,0 +1,35 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<configuration debug="false" status="trace">
+
+ <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
+ <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
+ <pattern>%d{yyyy-MM-dd_HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
+ </encoder>
+ </appender>
+
+ <!--<property name="DEV_HOME" value="/home/jhonnymertz"/>-->
+
+ <!--<appender name="FILE" class="ch.qos.logback.core.FileAppender">-->
+ <!--<file>${DEV_HOME}/adaptivecaching.log</file>-->
+ <!--<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">-->
+ <!--<pattern>%d{yyyy-MM-dd_HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>-->
+ <!--</encoder>-->
+ <!--</appender>-->
+
+ <logger name="br.ufrgs.inf.prosoft.tigris" level="debug" additivity="false">
+ <appender-ref ref="STDOUT"/>
+ <!--<appender-ref ref="FILE"/>-->
+ </logger>
+
+ <!--<logger name="org.springframework" level="OFF"/>-->
+
+ <logger name="org.mongodb.driver" level="info"/>
+
+ <logger name="org.reflections.Reflections" level="info"/>
+
+ <!-- By default, the level of the root level is set to DEBUG -->
+ <root level="DEBUG">
+ <appender-ref ref="STDOUT"/>
+ </root>
+
+</configuration>
diff --git a/tigris/src/test/resources/tigris.properties b/tigris/src/test/resources/tigris.properties
new file mode 100644
index 0000000..d316c57
--- /dev/null
+++ b/tigris/src/test/resources/tigris.properties
@@ -0,0 +1,3 @@
+frequency = tigris.metrics.Method.INVOCATION_FREQUENCY
+expensiveness = tigris.metrics.Method.EXECUTION_TIME
+changeability = tigris.metrics.Method.COMPUTATION_PATTERN
\ No newline at end of file