aplcache

Details

.gitignore 34(+34 -0)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..4a482b8
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,34 @@
+# ignore eclipse project files
+.project
+.classpath
+
+# ignore Intellij Idea project files
+.idea
+*.iml
+
+# Netbeans
+/nbproject/
+/nbactions.xml
+
+# Netbeans deploy
+/build/
+
+# Maven deploy
+/target/
+
+# Ant deploy
+/dist/
+
+# Class files
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*

pom.xml 73(+73 -0)

diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..bbae100
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,73 @@
+<?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>
+
+    <name>APLCache</name>
+    <groupId>br.ufrgs.inf.prosoft.adaptivecaching</groupId>
+    <artifactId>APLCache</artifactId>
+    <version>1.0</version>
+
+    <properties>
+        <aspectj.version>1.8.10</aspectj.version>
+        <java.version>1.8</java.version>
+    </properties>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.7.21</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>
+        <!-- https://mvnrepository.com/artifact/com.google.code.gson/gson -->
+        <dependency>
+            <groupId>com.google.code.gson</groupId>
+            <artifactId>gson</artifactId>
+            <version>2.8.5</version>
+        </dependency>
+        <dependency>
+            <groupId>br.ufrgs.inf.prosoft.trace</groupId>
+            <artifactId>Trace</artifactId>
+            <version>1.0</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.apache.maven.plugins</groupId>
+                <artifactId>maven-jar-plugin</artifactId>
+                <version>3.1.0</version>
+                <configuration>
+                    <archive>
+                        <manifest>
+                            <addClasspath>true</addClasspath>
+                            <mainClass>br.ufrgs.inf.prosoft.approachescomparison.adapter.Main</mainClass>
+                        </manifest>
+                    </archive>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+</project>
\ No newline at end of file
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/CacheDecider.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/CacheDecider.java
new file mode 100644
index 0000000..3e96c30
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/CacheDecider.java
@@ -0,0 +1,10 @@
+package br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision;
+
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodStats;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfo;
+
+public interface CacheDecider {
+
+    boolean isCacheable(MethodInfo methodInfo, MethodStats methodStats);
+
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/FlowchartWorkFlow.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/FlowchartWorkFlow.java
new file mode 100644
index 0000000..3849488
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/FlowchartWorkFlow.java
@@ -0,0 +1,377 @@
+package br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart;
+
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.CacheDecider;
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodEntry;
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodStats;
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats.CacheabilityMetrics;
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats.CacheabilityPatternDecider;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.LogTrace;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfo;
+import org.apache.commons.lang3.builder.EqualsBuilder;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.PrintWriter;
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class FlowchartWorkFlow {
+
+    private final long population;
+    protected HashMap<MethodInfo, MethodStats> methodsInfoMap;
+    Logger logger = LoggerFactory.getLogger(FlowchartWorkFlow.class);
+
+    private CacheDecider decider;
+
+    private double sumMissRatio;
+    private double sumHitRatio;
+    private double sumExecutionTime;
+    private double sumShareability;
+    private double sumFrequency;
+    private List<Double> hitRatios = new ArrayList<>();
+    private List<Double> missRatios = new ArrayList<>();
+    private List<Long> executionTimes = new ArrayList<>();
+    private List<Double> shareabilities = new ArrayList<>();
+    private List<Long> frequencies = new ArrayList<>();
+
+    public FlowchartWorkFlow(List<LogTrace> logList) {
+        this.decider = new CacheabilityPatternDecider(logList.size(), this);
+        this.methodsInfoMap = countOccurrences(logList);
+        this.population = logList.size();
+
+        logger.debug(methodsInfoMap.size() + " unique method calls identified from " + logList.size() + " original traces");
+
+        //sorting to get threshold, also excluding duplicates
+//        Collections.sort(executionTimes);
+//        hitRatios = hitRatios.stream().distinct().collect(Collectors.toList());
+//        Collections.sort(hitRatios);
+//        missRatios = missRatios.stream().distinct().collect(Collectors.toList());
+//        Collections.sort(missRatios);
+//        shareabilities = shareabilities.stream().distinct().collect(Collectors.toList());
+//        Collections.sort(shareabilities);
+        logger.debug("Average ExecutionTime: " + getAverageExecutionTime());
+        logger.debug("Average HitRatio: " + getAverageHitRatio());
+        logger.debug("Average MissRatio: " + getAverageMissRatio());
+        logger.debug("Average shareability: " + getAverageShareability());
+        logger.debug("StdDv ExecutionTime: " + getStdDevExecutionTimeRatio());
+        logger.debug("StdDv HitRatio: " + getStdDevHitRatio());
+        logger.debug("StdDv MissRatio: " + getStdDevMissRatio());
+        logger.debug("StdDv shareability: " + getStdDevShareability());
+        logger.debug("StdDv frequency: " + getStdDevFrequency());
+
+        int k = 0;
+        logger.debug("Using " + k + " stdDev to calculate thresholds...");
+        logger.debug("Threshold ExecutionTime: " + expensivenessThreshold(k));
+        logger.debug("Threshold HitRatio: " + hitThreshold(k));
+        logger.debug("Threshold MissRatio: " + missThreshold(k));
+        logger.debug("Threshold Shareability: " + shareabilityThreshold(k));
+        logger.debug("Threshold frequency: " + frequencyThreshold(k));
+    }
+
+    public Set<MethodEntry> filterCacheableMethods(long expiryTime) {
+        logger.debug("Deciding if methods are cacheable...");
+
+        Set<MethodEntry> cacheableMethods = getMethodsInfoMap().keySet().stream()
+                .filter(mi -> decider.isCacheable(mi, getMethodsInfoMap().get(mi)))
+                .map(mi -> new MethodEntry(mi, getMethodsInfoMap().get(mi), System.currentTimeMillis() + expiryTime))
+                .collect(Collectors.toSet());
+
+        logger.info(cacheableMethods.size() + " cacheable methods detected. Printing files...");
+
+        //TODO remove: print all unique methods and metrics to csv file
+        try {
+            final PrintWriter pw = new PrintWriter(new File("allmethods.csv"));
+            pw.write("isStaticData,changeMoreThanUsed,usedByManyRequests,isUserSpecific,isCacheSizeLarge,isDataSizeLarge,isExpensive,signature,numberOfSameOccurrences,numberOfDifferentReturnOccurrences,totalOccurrences,sameOccurrencesAverageExecutionTime,sameOccurrencesTotalExecutionTime,hitRatio,missRatio\n");
+            getMethodsInfoMap().keySet().stream().forEach(mi -> pw.write(CacheabilityMetrics.allMetricsToString(mi, getMethodsInfoMap().get(mi), this, getMethodsInfoMap().size()) + "," + new MethodEntry(mi, getMethodsInfoMap().get(mi), System.currentTimeMillis() + expiryTime).getMethodInfo().getSignature() + "," + new MethodEntry(mi, getMethodsInfoMap().get(mi), System.currentTimeMillis() + expiryTime).getMethodStats().toCSV() + '\n'));
+            pw.close();
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        }
+
+        //TODO remove: print cacheable methods to csv file
+        try {
+            final PrintWriter pw = new PrintWriter(new File("cacheablemethods.csv"));
+            pw.write("isStaticData,changeMoreThanUsed,usedByManyRequests,isUserSpecific,isCacheSizeLarge,isDataSizeLarge,isExpensive,signature,numberOfSameOccurrences,numberOfDifferentReturnOccurrences,totalOccurrences,sameOccurrencesAverageExecutionTime,sameOccurrencesTotalExecutionTime,hitRatio,missRatio\n");
+            cacheableMethods.stream().forEach(ma -> pw.write(CacheabilityMetrics.allMetricsToString(ma.getMethodInfo(), ma.getMethodStats(), this, getMethodsInfoMap().size()) + "," + ma.getMethodInfo().getSignature() + "," + ma.getMethodStats().toCSV() + '\n'));
+            pw.close();
+        } catch (FileNotFoundException e) {
+            e.printStackTrace();
+        }
+        return cacheableMethods;
+    }
+
+    public HashMap<MethodInfo, MethodStats> countOccurrences(List<LogTrace> logs) {
+
+        sumExecutionTime = 0;
+        sumHitRatio = 0;
+        sumMissRatio = 0;
+        sumShareability = 0;
+        sumFrequency = 0;
+
+        HashMap<MethodInfo, MethodStats> methodInfoMap = new HashMap<>();
+
+        for (int i = 0; i < logs.size(); i++) {
+            LogTrace logTrace = logs.get(i);
+
+            if (methodInfoMap.containsKey(logTrace.getMethodInfo())) {
+                continue;
+            }
+
+            MethodStats methodStats = new MethodStats(logTrace);
+
+            for (int j = 0; j < logs.size(); j++) {
+                LogTrace traceCompare = logs.get(j);
+
+                if (i == j) {
+                    continue;
+                }
+
+                //if similar methods: same signature and params, different return
+//                if(logTrace.getMethodInfo().getHashedArguments())
+                if (traceCompare.getMethodInfo().equalsWithoutReturnedValue(logTrace.getMethodInfo()) //                      || traceCompare.getMethodInfo().equalsHashedWithoutReturnedValue(logTrace.getMethodInfo())
+                        ) {
+
+                    //if identical methods
+                    if (EqualsBuilder.reflectionEquals(traceCompare.getMethodInfo().getReturnedValue(), logTrace.getMethodInfo().getReturnedValue()) //                            || Objects.equals(traceCompare.getMethodInfo().getHashedReturnedValue(), logTrace.getMethodInfo().getHashedReturnedValue())
+                            ) {
+                        methodStats.addSameOccurrence(traceCompare);
+                    } else {
+                        methodStats.addDifferentReturnOccurrence();
+                    }
+                }
+            }
+
+            methodInfoMap.put(logTrace.getMethodInfo(), methodStats);
+
+            sumExecutionTime += methodStats.getSameOccurrencesTotalExecutionTime();
+            executionTimes.add(methodStats.getSameOccurrencesTotalExecutionTime());
+
+            sumHitRatio += methodStats.hitRatio();
+            hitRatios.add(methodStats.hitRatio());
+
+            sumMissRatio += methodStats.missRatio();
+            missRatios.add(methodStats.missRatio());
+
+            sumShareability += methodStats.shareability();
+            shareabilities.add(methodStats.shareability());
+
+            sumFrequency += methodStats.getNumberOfSameOccurrences();
+            frequencies.add(methodStats.getNumberOfSameOccurrences());
+        }
+
+        return methodInfoMap;
+    }
+
+    /**
+     * General mean hit ratio of all calls
+     *
+     * @return
+     */
+    public double getAverageHitRatio() {
+        if (population == 0) {
+            return 0;
+        }
+        return new BigDecimal(sumHitRatio).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
+    }
+
+    public double getAverageMissRatio() {
+        if (population == 0) {
+            return 0;
+        }
+        return new BigDecimal(sumMissRatio).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
+    }
+
+    public double getAverageExecutionTime() {
+        if (population == 0) {
+            return 0;
+        }
+        return new BigDecimal(sumExecutionTime).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
+    }
+
+    public double getAverageShareability() {
+        if (population == 0) {
+            return 0;
+        }
+        return new BigDecimal(sumShareability).divide(new BigDecimal(population), 5, RoundingMode.HALF_UP).doubleValue();
+    }
+
+    private Double StdDevHitRatio;
+
+    public double getStdDevHitRatio() {
+        if (population == 0) {
+            return 0;
+        }
+        if (StdDevHitRatio != null) {
+            return StdDevHitRatio;
+        }
+
+        double mean = getAverageHitRatio();
+        double temp = 0;
+        for (double a : hitRatios) {
+            temp += (a - mean) * (a - mean);
+        }
+        StdDevHitRatio = Math.sqrt(temp / population);
+        return StdDevHitRatio;
+    }
+
+    private Double StdDevMissRatio;
+
+    public double getStdDevMissRatio() {
+        if (population == 0) {
+            return 0;
+        }
+        if (StdDevMissRatio != null) {
+            return StdDevMissRatio;
+        }
+
+        double mean = getAverageMissRatio();
+        double temp = 0;
+        for (double a : missRatios) {
+            temp += (a - mean) * (a - mean);
+        }
+        StdDevMissRatio = Math.sqrt(temp / population);
+        return StdDevMissRatio;
+    }
+
+    private Double StdDevExecutionTimeRatio;
+
+    public double getStdDevExecutionTimeRatio() {
+        if (population == 0) {
+            return 0;
+        }
+        if (StdDevExecutionTimeRatio != null) {
+            return StdDevExecutionTimeRatio;
+        }
+
+        double mean = getAverageExecutionTime();
+        double temp = 0;
+        for (Long a : executionTimes) {
+            temp += (a - mean) * (a - mean);
+        }
+        StdDevExecutionTimeRatio = Math.sqrt(temp / population);
+        return StdDevExecutionTimeRatio;
+    }
+
+    private Double StdDevFrequency;
+
+    public double getStdDevFrequency() {
+        if (population == 0) {
+            return 0;
+        }
+        if (StdDevFrequency != null) {
+            return StdDevFrequency;
+        }
+
+        double mean = getAverageFrequency();
+        double temp = 0;
+        for (Long a : frequencies) {
+            temp += (a - mean) * (a - mean);
+        }
+        StdDevFrequency = Math.sqrt(temp / population);
+        return StdDevFrequency;
+    }
+
+    private Double StdDevShareability;
+
+    public double getStdDevShareability() {
+        if (population == 0) {
+            return 0;
+        }
+        if (StdDevShareability != null) {
+            return StdDevShareability;
+        }
+
+        double mean = getAverageShareability();
+        double temp = 0;
+        for (Double a : shareabilities) {
+            temp += (a - mean) * (a - mean);
+        }
+        StdDevShareability = Math.sqrt(temp / population);
+        return StdDevShareability;
+    }
+
+    public HashMap<MethodInfo, MethodStats> getMethodsInfoMap() {
+        return methodsInfoMap;
+    }
+
+    //getting X% with most hits
+    public double hitThreshold(int kStdDev) {
+        return getAverageHitRatio() + (kStdDev * getStdDevHitRatio());
+    }
+
+    //getting X% with most misses
+    public double missThreshold(int kStdDev) {
+        return getAverageMissRatio() + (kStdDev * getStdDevMissRatio());
+    }
+
+    //getting X% most expensive methods
+    public double expensivenessThreshold(int kStdDev) {
+        return getAverageExecutionTime() + (kStdDev * getStdDevExecutionTimeRatio());
+    }
+
+    public double shareabilityThreshold(int kStdDev) {
+        return getAverageShareability() + (kStdDev * getStdDevShareability());
+    }
+
+    //getting X% most frenquent
+    public double frequencyThreshold(int kStdDev) {
+        return getAverageFrequency() + (kStdDev * getStdDevFrequency());
+    }
+
+    /**
+     * General miss ratio from a signature
+     *
+     * @param signature
+     * @return
+     */
+    public double getMissRatio(String signature) {
+        long occurrences = 0;
+        long methods = 0;
+        for (MethodInfo mi : methodsInfoMap.keySet()) {
+            if (mi.getSignature().equals(signature)) {
+                occurrences += methodsInfoMap.get(mi).getNumberOfSameOccurrences();
+                methods++;
+            }
+        }
+        return occurrences / methods;
+    }
+
+    /**
+     * General hit ratio from a signature
+     *
+     * @param signature
+     * @return
+     */
+    public double getHitRatio(String signature) {
+        long occurrences = 0;
+        long methods = 0;
+        for (MethodInfo mi : methodsInfoMap.keySet()) {
+            if (mi.getSignature().equals(signature)) {
+                occurrences += methodsInfoMap.get(mi).getNumberOfSameOccurrences();
+                methods++;
+            }
+        }
+        return occurrences / methods;
+    }
+
+    private Double AverageFrequency;
+
+    public double getAverageFrequency() {
+        if (frequencies.isEmpty()) {
+            return 0;
+        }
+        if (AverageFrequency != null) {
+            return AverageFrequency;
+        }
+
+        AverageFrequency = new BigDecimal(sumFrequency).divide(new BigDecimal(frequencies.size()), 5, RoundingMode.HALF_UP).doubleValue();
+        return AverageFrequency;
+    }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/model/MethodEntry.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/model/MethodEntry.java
new file mode 100644
index 0000000..3ce0440
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/model/MethodEntry.java
@@ -0,0 +1,60 @@
+package br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model;
+
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfo;
+
+public class MethodEntry {
+
+    private MethodInfo methodInfo;
+    private MethodStats methodStats;
+    private long lifetime;
+
+    public MethodEntry(){
+
+    }
+
+    public MethodEntry(MethodInfo methodInfo, MethodStats methodStats, long lifetime) {
+        this.methodInfo = methodInfo;
+        this.methodStats = methodStats;
+        this.lifetime = lifetime;
+    }
+
+    public MethodInfo getMethodInfo() {
+        return methodInfo;
+    }
+
+    public MethodStats getMethodStats() {
+        return methodStats;
+    }
+
+    @Override
+    public String toString() {
+        return "MethodEntry{" +
+                "methodInfo=" + methodInfo +
+                ", methodStats=" + methodStats +
+                '}';
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) return true;
+        if (!(o instanceof MethodEntry)) return false;
+
+        MethodEntry that = (MethodEntry) o;
+
+        if (!methodInfo.equals(that.methodInfo)) return false;
+        if (!methodStats.equals(that.methodStats)) return false;
+
+        return true;
+    }
+
+    @Override
+    public int hashCode() {
+        int result = methodInfo.hashCode();
+        result = 31 * result + methodStats.hashCode();
+        return result;
+    }
+
+    public boolean expired() {
+        return lifetime < System.currentTimeMillis();
+    }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/model/MethodStats.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/model/MethodStats.java
new file mode 100644
index 0000000..4287ad5
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/model/MethodStats.java
@@ -0,0 +1,151 @@
+package br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model;
+
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.LogTrace;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.HashSet;
+import java.util.Set;
+
+public class MethodStats {
+
+    //exactly same method calls
+    private Long sameOccurrences;
+
+    //number of same method calls with different return
+    private Long differentReturnOccurrences;
+    private Long sameOccurrencesExecutionTime;
+    private Long sameOccurrencesHigherExecutionTime;
+
+    private Long amountOfIdentifiedSameOccurences;
+    private Set<String> uniqueUsers;
+
+    public MethodStats() {
+        sameOccurrences = 0L;
+        differentReturnOccurrences = 0L;
+        sameOccurrencesExecutionTime = 0L;
+        sameOccurrencesHigherExecutionTime = 0L;
+        amountOfIdentifiedSameOccurences = 0L;
+        uniqueUsers = new HashSet<>();
+    }
+
+    public MethodStats(LogTrace logTrace) {
+        this();
+        addSameOccurrence(logTrace);
+    }
+
+    public void addSameOccurrence(LogTrace logTrace) {
+        sameOccurrences++;
+        addSameOccurrencesTotalExecutionTime(logTrace.totalTime());
+
+        //todo cannot be based on string
+        if (logTrace.getUserId() != null && !logTrace.getUserId().equals("Anonymous")) {
+            uniqueUsers.add(logTrace.getUserId());
+            addIdentifiedSameOccurence();
+        }
+    }
+
+    public void addDifferentReturnOccurrence() {
+        //todo if the request was identified, it should counts
+        //addIdentifiedSameOccurence();
+        differentReturnOccurrences += 1;
+    }
+
+    private void addSameOccurrencesTotalExecutionTime(Long executionTime) {
+        sameOccurrencesExecutionTime += executionTime;
+        if (executionTime > sameOccurrencesHigherExecutionTime)
+            sameOccurrencesHigherExecutionTime = executionTime;
+    }
+
+    public Long getSameOccurrencesTotalExecutionTime() {
+        return sameOccurrencesExecutionTime;
+    }
+
+    public double getSameOccurrencesAverageExecutionTime() {
+        return new BigDecimal(getSameOccurrencesTotalExecutionTime())
+                .divide(new BigDecimal(sameOccurrences), 5, RoundingMode.HALF_UP)
+                .doubleValue();
+    }
+
+    public long getSameOccurrencesHigherExecutionTime() {
+        return sameOccurrencesHigherExecutionTime;
+    }
+
+    /**
+     * @return All occurrences of a method, i.e. with different params and return or not
+     */
+    public Long getNumberOfOccurrences() {
+        return sameOccurrences + differentReturnOccurrences;
+    }
+
+    public Long getNumberOfSameOccurrences() {
+        return sameOccurrences;
+    }
+
+    public Long getNumberOfDifferentReturnOccurrences() {
+        return differentReturnOccurrences;
+    }
+
+    //from 0% to 100%
+    public double hitRatio() {
+        BigDecimal bd = new BigDecimal(getNumberOfSameOccurrences() * 100);
+        return bd.divide(new BigDecimal(getNumberOfOccurrences()), 5, RoundingMode.HALF_UP).doubleValue();
+    }
+
+    //from 0% to 100%
+    public double missRatio() {
+        BigDecimal bd = new BigDecimal(getNumberOfDifferentReturnOccurrences() * 100);
+        return bd.divide(new BigDecimal(getNumberOfOccurrences()), 5, RoundingMode.HALF_UP).doubleValue();
+    }
+
+    public double shareability() {
+
+        Long amountOfIdentifiedSameOccurences = getAmountOfIdentifiedSameOccurences();
+        if (amountOfIdentifiedSameOccurences == 0)
+            return 100;
+
+        BigDecimal bd = new BigDecimal(getAmountOfUniqueIdentifiedSameOccurences() * 100);
+        return bd.divide(new BigDecimal(amountOfIdentifiedSameOccurences), 5, RoundingMode.HALF_UP).doubleValue();
+    }
+
+    public String toCSV() {
+        //numberOfSameOccurrences,numberOfDifferentReturnOccurrences,totalOccurrences,sameOccurrencesAverageExecutionTime,sameOccurrencesTotalExecutionTime,hitRatio,missRatio
+        return getNumberOfSameOccurrences() +
+                "," + getNumberOfDifferentReturnOccurrences() +
+                "," + getNumberOfOccurrences() +
+                "," + getSameOccurrencesAverageExecutionTime() +
+                "," + getSameOccurrencesTotalExecutionTime() +
+                "," + hitRatio() +
+                "," + missRatio();
+    }
+
+    @Override
+    public String toString() {
+        return
+                "MethodStats{" +
+                        "numberOfSameOccurrences=" + getNumberOfSameOccurrences() +
+                        ", numberOfDifferentReturnOccurrences=" + getNumberOfDifferentReturnOccurrences() +
+                        ", totalOccurrences =" + getNumberOfOccurrences() +
+                        ", sameOccurrencesAverageExecutionTime =" + getSameOccurrencesAverageExecutionTime() +
+                        ", sameOccurrencesTotalExecutionTime =" + getSameOccurrencesTotalExecutionTime() +
+                        ", hitRatio =" + hitRatio() +
+                        ", missRatio =" + missRatio() +
+                        '}';
+    }
+
+    public Long getAmountOfIdentifiedSameOccurences() {
+        return amountOfIdentifiedSameOccurences;
+    }
+
+    public int getAmountOfUniqueIdentifiedSameOccurences() {
+        return uniqueUsers.size();
+    }
+
+    public Long getAmountOfAnonymousSameOccurences() {
+        return sameOccurrences - amountOfIdentifiedSameOccurences;
+    }
+
+    private void addIdentifiedSameOccurence() {
+        amountOfIdentifiedSameOccurences++;
+    }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityMetrics.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityMetrics.java
new file mode 100644
index 0000000..c90f88b
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityMetrics.java
@@ -0,0 +1,124 @@
+package br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats;
+
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.FlowchartWorkFlow;
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodStats;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfo;
+
+import java.math.BigDecimal;
+import java.math.RoundingMode;
+import java.util.Optional;
+
+public class CacheabilityMetrics {
+
+    //total da população
+    public static long sampleSize(long N, double Z, double e) {
+        //Nível de confiança 90% -> Z=1.645
+        //Nível de confiança 95% -> Z=1.96
+        //Nível de confiança 99% -> Z=2.575
+        //Qual o nível de certeza que precisa ter de que a amostra retrata com precisão a sua população?
+        //double Z = 2.575;
+
+        //e = É a margem de erro máximo que eu quero admitir (p.e. 5%)
+        //Qual o nível de certeza que precisa ter de que os traces refletem as execuções da sua aplicação?
+        //double e = 0.03;
+        //p = É a proporção que esperamos encontrar. Este parâmetro tende confundir bastante à primeira vista:
+        double p = 0.5;
+
+        long n = (long) ((N * Math.pow(Z, 2) * p * (1 - p))
+                / ((N - 1) * Math.pow(e, 2) + Math.pow(Z, 2) * p * (1 - p)));
+
+        //simplified without the population
+        //int nn = (int) ((Math.pow(Z,2) * p * (1-p)) / Math.pow(e,2));
+        return n;
+    }
+
+    public static Optional<Boolean> isStaticData(MethodStats methodStats, FlowchartWorkFlow workflow, int population) {
+
+        //executions of a method should represent a good portion of total logs in order to avoid 1 occur == 100% hit
+//        if (!(methodStats.getNumberOfSameOccurrences() >= sampleSize(methodStats.getNumberOfOccurrences(), 1.645, 0.05)
+//                && methodStats.getNumberOfOccurrences() >= sampleSize(population, 1.645, 0.05))) {
+//            return Optional.empty();
+//        }
+        if (methodStats.getNumberOfSameOccurrences() < workflow.frequencyThreshold(0)) {
+            return Optional.empty();
+        }
+        if (methodStats.hitRatio() == 100.0) {
+            return Optional.of(true);
+        } else {
+            return Optional.of(false);
+        }
+    }
+
+    public static Optional<Boolean> changeMoreThanUsed(MethodStats methodStats, FlowchartWorkFlow workflow) {
+        //+/- k sds
+        if (methodStats.missRatio() > workflow.missThreshold(0)) {
+            return Optional.of(true);
+        } else {
+            return Optional.of(false);
+        }
+    }
+
+    public static Optional<Boolean> usedByManyRequests(MethodStats methodStats, FlowchartWorkFlow workflow, int population) {
+
+        //same executions of a method should represent a good portion of total execution of such method
+        if (methodStats.getNumberOfSameOccurrences() >= workflow.frequencyThreshold(0)) {
+//        if (methodStats.getNumberOfOccurrences() >= sampleSize(population, 1.645, 0.05)) {
+            return Optional.of(true);
+        } else {
+            return Optional.of(false);
+        }
+    }
+
+    public static Optional<Boolean> isUserSpecific(MethodStats methodStats, FlowchartWorkFlow workflow) {
+
+        if (methodStats.getAmountOfIdentifiedSameOccurences() == 0) {
+            return Optional.empty();
+        }
+
+        //the less shareable, the more user specific
+        if (methodStats.shareability() < workflow.shareabilityThreshold(0)) {
+            return Optional.of(true);
+        } else {
+            return Optional.of(false);
+        }
+    }
+
+    public static Optional<Boolean> isDataSizeLarge(MethodInfo methodInfo) {
+
+        //TODO concept considered while caching
+        if (true) {
+            return Optional.of(false);
+        }
+
+        long shallowSize = methodInfo.getSizeOfReturnedValue(); // modified to interface
+
+        BigDecimal bd = new BigDecimal(shallowSize)
+                .multiply(new BigDecimal(100))
+                .divide(new BigDecimal(Long.MAX_VALUE), 5, RoundingMode.HALF_UP); //cache free space replaced to infinity
+        double dataSizePercent = bd.doubleValue();
+
+        if (dataSizePercent <= 2.0) {
+            return Optional.of(false);
+        } else {
+            return Optional.of(true);
+        }
+    }
+
+    public static Optional<Boolean> isExpensive(MethodStats methodStats, FlowchartWorkFlow workflow) {
+        if (methodStats.getSameOccurrencesAverageExecutionTime() >= workflow.expensivenessThreshold(0)) {
+            return Optional.of(true);
+        } else {
+            return Optional.of(false);
+        }
+    }
+
+    public static String allMetricsToString(MethodInfo methodInfo, MethodStats methodStats, FlowchartWorkFlow workflow, int population) {
+        //isStaticData,changeMoreThanUsed,usedByManyRequests,isUserSpecific,isCacheSizeLarge,isDataSizeLarge,isExpensive
+        return isStaticData(methodStats, workflow, population)
+                + "," + changeMoreThanUsed(methodStats, workflow)
+                + "," + usedByManyRequests(methodStats, workflow, population)
+                + "," + isUserSpecific(methodStats, workflow)
+                + "," + isDataSizeLarge(methodInfo)
+                + "," + isExpensive(methodStats, workflow);
+    }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityPatternDecider.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityPatternDecider.java
new file mode 100644
index 0000000..ff9644f
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/analysis/decision/flowchart/stats/CacheabilityPatternDecider.java
@@ -0,0 +1,93 @@
+package br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.stats;
+
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.CacheDecider;
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.FlowchartWorkFlow;
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodStats;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfo;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Optional;
+
+/**
+ * TODO flowchart paper link
+ *
+ * @see
+ */
+public class CacheabilityPatternDecider implements CacheDecider {
+
+    private final int population;
+    Logger logger = LoggerFactory.getLogger(CacheabilityPatternDecider.class);
+
+    FlowchartWorkFlow workflow;
+
+    public CacheabilityPatternDecider(int size, FlowchartWorkFlow workflow) {
+        this.workflow = workflow;
+        this.population = size;
+    }
+
+    /**
+     * Flowchart definition
+     *
+     * @param methodStats
+     * @return
+     */
+    @Override
+    public boolean isCacheable(MethodInfo methodInfo, MethodStats methodStats) {
+
+        //used more than once, in many parts of the reasoning
+        Optional<Boolean> isCacheSizeLarge = Optional.of(true); // the cache is always large enough
+
+        //Is the data completely static?
+        Optional<Boolean> isStaticData = CacheabilityMetrics.isStaticData(methodStats, workflow, population);
+        if (isStaticData.isPresent() && isStaticData.get()) { // staticity yes
+
+            Optional<Boolean> isDataSizeLarge = CacheabilityMetrics.isDataSizeLarge(methodInfo);
+            if (isDataSizeLarge.isPresent() && !isDataSizeLarge.get()) {
+                return true;
+            } else {
+                if (isCacheSizeLarge.isPresent() && isCacheSizeLarge.get()) {
+                    return true;
+                } else {
+                    return false;
+                }
+            }
+        } else { // staticity no/not sure
+
+            Optional<Boolean> changeMoreThanUsed = CacheabilityMetrics.changeMoreThanUsed(methodStats, workflow);
+            if (changeMoreThanUsed.isPresent() && changeMoreThanUsed.get()) { //changeMoreThanUsed true
+                return false;
+            } else { //changeMoreThanUsed not/not sure
+                Optional<Boolean> usedByManyRequests = CacheabilityMetrics.usedByManyRequests(methodStats, workflow, population);
+                if (usedByManyRequests.isPresent() && !usedByManyRequests.get()) { //useByManyRequests no
+                    return false;
+                } else {
+                    Optional<Boolean> isUserSpecific = CacheabilityMetrics.isUserSpecific(methodStats, workflow);
+                    if (isUserSpecific.isPresent() && isUserSpecific.get()) {
+                        if (isCacheSizeLarge.isPresent() && isCacheSizeLarge.get()) {
+                            return true;
+                        } else {
+                            return false;
+                        }
+                    } else {
+                        Optional<Boolean> isExpensive = CacheabilityMetrics.isExpensive(methodStats, workflow);
+                        if (!isExpensive.isPresent()) {
+
+                            if (isCacheSizeLarge.isPresent() && isCacheSizeLarge.get()) {
+                                return true;
+                            } else {
+                                return false;
+                            }
+                        } else {
+                            if (isExpensive.get()) {
+                                return true;
+                            } else {
+                                return false;
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/LogTrace.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/LogTrace.java
new file mode 100644
index 0000000..635c4b0
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/LogTrace.java
@@ -0,0 +1,95 @@
+package br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata;
+
+public class LogTrace {
+
+    private MethodInfo methodInfo;
+    private long startTime;
+    private long endTime;
+    private String userId;
+
+    public MethodInfo getMethodInfo() {
+        return this.methodInfo;
+    }
+
+    public LogTrace setMethodInfo(MethodInfo methodInfo) {
+        this.methodInfo = methodInfo;
+        return this;
+    }
+
+    public long totalTime() {
+        return endTime - startTime;
+    }
+
+    public long getStartTime() {
+        return this.startTime;
+    }
+
+    public LogTrace setStartTime(long startTime) {
+        this.startTime = startTime;
+        return this;
+    }
+
+    public long getEndTime() {
+        return this.endTime;
+    }
+
+    public LogTrace setEndTime(long endTime) {
+        this.endTime = endTime;
+        return this;
+    }
+
+    public String getUserId() {
+        return userId;
+    }
+
+    public LogTrace setUserId(String userId) {
+        this.userId = userId;
+        return this;
+    }
+
+    @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/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/MethodInfo.java b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/MethodInfo.java
new file mode 100644
index 0000000..11a4384
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/adaptivecaching/monitoring/application/metadata/MethodInfo.java
@@ -0,0 +1,150 @@
+package br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata;
+
+import org.apache.commons.lang3.builder.EqualsBuilder;
+
+import java.util.Objects;
+import org.ehcache.sizeof.SizeOf;
+
+public class MethodInfo {
+
+    private String signature;
+
+    private transient Object[] arguments;
+
+    private transient Object returnedValue;
+
+    
+    private long sizeOfReturnedValue;
+
+//    private Integer hashedReturnedValue;
+//    private Integer hashedArguments;
+
+    public MethodInfo(String signature, Object[] arguments) {
+        this.signature = signature;
+        this.arguments = arguments;
+
+//        try {
+//            //TODO hash or not???
+//            this.hashedArguments = HashCodeBuilder.reflectionHashCode(arguments);
+//        } catch (Exception e) {
+//        }
+    }
+
+    public MethodInfo(String signature, Object[] arguments, Object returnedValue) {
+        this(signature, arguments);
+        this.returnedValue = returnedValue;
+        SizeOf sizeOf = SizeOf.newInstance();
+        this.sizeOfReturnedValue = sizeOf.sizeOf(this.returnedValue);
+
+//        try {
+//            //TODO hash or not???
+//            this.hashedArguments = HashCodeBuilder.reflectionHashCode(arguments);
+//            this.hashedReturnedValue = HashCodeBuilder.reflectionHashCode(returnedValue);
+//        } catch (Exception e) {
+//        }
+    }
+    
+    public String getSignature() {
+        return this.signature;
+    }
+
+    public Object[] getArguments() {
+        return this.arguments;
+    }
+
+    public Object getReturnedValue() {
+        return this.returnedValue;
+    }
+    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;
+        //return EqualsBuilder.reflectionEquals(this, o, "returnedValue", "key");
+    }
+
+    @Override
+    public boolean equals(Object o) {
+//        return EqualsBuilder.reflectionEquals(this, o, "key");
+
+        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);
+    }
+
+//    public boolean equalsHashed(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 (!Objects.deepEquals(hashedArguments, that.hashedArguments)) return false;
+//        return hashedReturnedValue != null ? Objects.deepEquals(hashedReturnedValue, that.hashedReturnedValue) : that.hashedReturnedValue == null;
+//    }
+//
+//    public boolean equalsHashedWithoutReturnedValue(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 (!Objects.deepEquals(hashedArguments, that.hashedArguments)) return false;
+//        return true;
+//    }
+//    public Integer getHashedReturnedValue() {
+//        return hashedReturnedValue;
+//    }
+//
+//    public Integer getHashedArguments() {
+//        return hashedArguments;
+//    }
+//
+//    public void setArguments(Object[] arguments) {
+//        this.arguments = arguments;
+//    }
+//
+//    public void setReturnedValue(Object returnedValue) {
+//        this.returnedValue = 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.adaptivecaching.monitoring.application.metadata.MethodInfo(signature=" + this.signature + ", arguments=" + this.arguments + ", returnedValue=" + this.returnedValue + ")";
+    }
+
+    public long getSizeOfReturnedValue() {
+        return this.sizeOfReturnedValue;
+    }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/Main.java b/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/Main.java
new file mode 100644
index 0000000..fff69c4
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/Main.java
@@ -0,0 +1,42 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package br.ufrgs.inf.prosoft.approachescomparison.adapter;
+
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.FlowchartWorkFlow;
+import br.ufrgs.inf.prosoft.adaptivecaching.analysis.decision.flowchart.model.MethodEntry;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.LogTrace;
+import br.ufrgs.inf.prosoft.trace.Trace;
+import java.util.List;
+import java.util.Set;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ *
+ * @author romulo
+ */
+public class Main {
+
+    private static final Logger logger = Logger.getLogger(Main.class.getName());
+
+    public static void main(String[] args) {
+        System.setProperty("java.util.logging.SimpleFormatter.format", "[%1$tF %1$tT+%1$tL] [%4$-7s] [APLCache] %5$s %n");
+
+        String path = null;
+        if (args.length < 1) {
+            System.err.println("No path provided");
+            System.exit(1);
+        } else {
+            path = args[0];
+        }
+        logger.log(Level.INFO, "Reading traces");
+        List<Trace> traces = TraceReader.partiallyParseFile(path);
+        List<LogTrace> logList = TraceReader.getLogTraces(traces);
+        FlowchartWorkFlow flowchartWorkFlow = new FlowchartWorkFlow(logList);
+        Set<MethodEntry> process = flowchartWorkFlow.filterCacheableMethods(300000); // default value provided by the framework
+        logger.log(Level.INFO, "{0} cacheable methods identified", process.size());
+    }
+}
diff --git a/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/TraceReader.java b/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/TraceReader.java
new file mode 100644
index 0000000..c285686
--- /dev/null
+++ b/src/main/java/br/ufrgs/inf/prosoft/approachescomparison/adapter/TraceReader.java
@@ -0,0 +1,53 @@
+/*
+ * To change this license header, choose License Headers in Project Properties.
+ * To change this template file, choose Tools | Templates
+ * and open the template in the editor.
+ */
+package br.ufrgs.inf.prosoft.approachescomparison.adapter;
+
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.LogTrace;
+import br.ufrgs.inf.prosoft.adaptivecaching.monitoring.application.metadata.MethodInfo;
+import br.ufrgs.inf.prosoft.trace.Parameter;
+import br.ufrgs.inf.prosoft.trace.Trace;
+import br.ufrgs.inf.prosoft.trace.TraceReference;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * @author romulo
+ */
+public class TraceReader extends br.ufrgs.inf.prosoft.trace.TraceReader {
+
+    private static final Logger logger = Logger.getLogger(TraceReader.class.getName());
+
+    public static List<LogTrace> getLogTraces(List<Trace> traces) {
+        List<LogTrace> logTraces = new ArrayList<>();
+        while (!traces.isEmpty()) {
+            Trace trace = traces.remove(0);
+            try {
+                LogTrace logTrace;
+                if (trace instanceof TraceReference) {
+                    TraceReference traceReference = (TraceReference) trace;
+                    logTrace = new LogTrace().setStartTime(traceReference.getStartTime())
+                            .setEndTime(traceReference.getEndTime());
+                } else {
+                    logTrace = new LogTrace().setStartTime(trace.getStartTime())
+                            .setEndTime(trace.getEndTime())
+                            .setMethodInfo(new MethodInfo(trace.getName(),
+                                    trace.getParameters().stream().map(Parameter::getData)
+                                            .collect(Collectors.toList()).toArray(),
+                                    trace.getReturn().getData()));
+                }
+                logTraces.add(logTrace);
+            } catch (Exception e) {
+                logger.log(Level.INFO, "Trace discarted: {0}", trace);
+            }
+        }
+        return logTraces;
+    }
+
+}