diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 119c5e1..b89ab28 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -1,4 +1,4 @@ -name: Build +name: SonarQube on: push: branches: @@ -7,30 +7,30 @@ on: types: [opened, synchronize, reopened] jobs: build: - name: Build + name: Build and analyze runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 with: fetch-depth: 0 # Shallow clones should be disabled for a better relevancy of analysis - - name: Set up JDK 11 - uses: actions/setup-java@v1 + - name: Set up JDK 17 + uses: actions/setup-java@v4 with: - java-version: 11 - - name: Cache SonarCloud packages - uses: actions/cache@v1 + java-version: 17 + distribution: 'zulu' # Alternative distribution options are available. + - name: Cache SonarQube packages + uses: actions/cache@v4 with: path: ~/.sonar/cache key: ${{ runner.os }}-sonar restore-keys: ${{ runner.os }}-sonar - name: Cache Maven packages - uses: actions/cache@v1 + uses: actions/cache@v4 with: path: ~/.m2 key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }} restore-keys: ${{ runner.os }}-m2 - name: Build and analyze env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} - run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=appform-io_databuilderframework + run: mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar -Dsonar.projectKey=appform-io_databuilderframework \ No newline at end of file diff --git a/.github/workflows/jacoco-coverage.yml b/.github/workflows/jacoco-coverage.yml new file mode 100644 index 0000000..4b101e9 --- /dev/null +++ b/.github/workflows/jacoco-coverage.yml @@ -0,0 +1,53 @@ +name: JaCoCo Coverage +on: + pull_request: + branches: [ master ] # Adjust branches as needed + +permissions: + # Required for checking out the code + contents: read + # Required for posting comments and status checks + pull-requests: write + # Required for reading report files + actions: read # Optional, often implicitly available + +jobs: + build-and-report: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Set up JDK 11 + uses: actions/setup-java@v4 + with: + java-version: 11 # Or your project's Java version + distribution: temurin + + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} # Needed to get PR information, if any + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + run: mvn -B verify + + - name: Check for JaCoCo Report File + run: | + echo "Searching for JaCoCo reports..." + find ${{ github.workspace }} -name 'jacoco*.xml' # General search + echo "--- Listing Maven default ---" + ls -l ${{ github.workspace }}/target/site/jacoco/jacoco.xml || echo "Maven default not found" + # --- Coverage Reporting Step --- + - name: JaCoCo Report to PR Comment + id: jacoco + uses: madrapps/jacoco-report@v1.7.1 # Use the latest version + with: + paths: | + ${{ github.workspace }}/**/target/site/jacoco/jacoco.xml + token: ${{ secrets.GITHUB_TOKEN }} + min-coverage-overall: 95 # Example value + min-coverage-changed-files: 95 # Example value + update-comment: true + title: "📊 JaCoCo Code Coverage" \ No newline at end of file diff --git a/pom.xml b/pom.xml index c1352f1..4d7cd24 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> io.appform.databuilderframework - 1.1.0 + 1.1.1 4.0.0 databuilderframework @@ -68,6 +68,11 @@ Vinay Varma vinay.varma@flipkart.com + + r0goyal + Rishabh Goyal + rgoyal2191@gmail.com + @@ -78,8 +83,7 @@ UTF-8 2.13.1 - 1.18.22 - 1.16.6.1 + 1.18.30 5.2.5.Final 4.13.2 1.2.10 @@ -137,21 +141,14 @@ - org.projectlombok - lombok-maven-plugin - - - generate-sources - - delombok - - - - ${lombok.maven.version} + org.sonarsource.scanner.maven + sonar-maven-plugin + 5.1.0.4751 org.apache.maven.plugins maven-compiler-plugin + 3.8.1 1.8 1.8 @@ -160,24 +157,26 @@ org.jacoco jacoco-maven-plugin - 0.8.6 + 0.8.13 + prepare-agent prepare-agent - report - test report + + + XML + + - - org.apache.maven.plugins diff --git a/src/main/java/io/appform/databuilderframework/engine/BuilderRunner.java b/src/main/java/io/appform/databuilderframework/engine/BuilderRunner.java index 2cad6f4..ed8464e 100644 --- a/src/main/java/io/appform/databuilderframework/engine/BuilderRunner.java +++ b/src/main/java/io/appform/databuilderframework/engine/BuilderRunner.java @@ -76,8 +76,8 @@ public DataContainer call() throws Exception { executePostListenersSuccess(response); if (null != response) { Preconditions.checkArgument(response.getData().equalsIgnoreCase(builderMeta.getProduces()), - String.format("Builder is supposed to produce %s but produces %s", - builderMeta.getProduces(), response.getData())); + "Builder is supposed to produce %s but produces %s", + builderMeta.getProduces(), response.getData()); response.setGeneratedBy(builderMeta.getName()); } return new DataContainer(builderMeta, response); diff --git a/src/main/java/io/appform/databuilderframework/engine/DataSetAccessor.java b/src/main/java/io/appform/databuilderframework/engine/DataSetAccessor.java index 04968dc..6da8a55 100644 --- a/src/main/java/io/appform/databuilderframework/engine/DataSetAccessor.java +++ b/src/main/java/io/appform/databuilderframework/engine/DataSetAccessor.java @@ -37,8 +37,8 @@ public T get(Class tClass) { public T get(String key, Class tClass) { val data = dataSet.get(key); return null == data - ? null - : tClass.cast(data); + ? null + : tClass.cast(data); } /** @@ -53,9 +53,9 @@ public T get(String key, Class tClass) { */ public T getAccessibleData(String key, B builder, Class tClass) { Preconditions.checkArgument(!builder.getDataBuilderMeta().getAccessibleDataSet().contains(key), - String.format("Builder %s can access only %s", - builder.getDataBuilderMeta().getName(), - builder.getDataBuilderMeta().getConsumes())); + "Builder %s can access only %s", + builder.getDataBuilderMeta().getName(), + builder.getDataBuilderMeta().getConsumes()); return get(key, tClass); } diff --git a/src/main/java/io/appform/databuilderframework/engine/ExecutionGraphGenerator.java b/src/main/java/io/appform/databuilderframework/engine/ExecutionGraphGenerator.java index 09d538b..22d4434 100644 --- a/src/main/java/io/appform/databuilderframework/engine/ExecutionGraphGenerator.java +++ b/src/main/java/io/appform/databuilderframework/engine/ExecutionGraphGenerator.java @@ -133,11 +133,13 @@ private int rankNodes(DependencyNode root, int currentNode, Set processe } val childNode = currentNode + 1; - return root.getIncoming() + val ret = root.getIncoming() .stream() .mapToInt(child -> Math.max(rankNodes(child, childNode, processedNodes), childNode)) .max() .orElse(childNode); + processedNodes.add(data); + return ret; } private DependencyNode generateDependencyTree( diff --git a/src/main/java/io/appform/databuilderframework/engine/MultiThreadedDataFlowExecutor.java b/src/main/java/io/appform/databuilderframework/engine/MultiThreadedDataFlowExecutor.java index 0020651..ade2e44 100644 --- a/src/main/java/io/appform/databuilderframework/engine/MultiThreadedDataFlowExecutor.java +++ b/src/main/java/io/appform/databuilderframework/engine/MultiThreadedDataFlowExecutor.java @@ -86,10 +86,9 @@ protected DataExecutionResponse run( } if (null != response) { Preconditions.checkArgument(response.getData().equalsIgnoreCase(data), - String.format( "Builder is supposed to produce %s but produces %s", data, - response.getData())); + response.getData()); dataSetAccessor.merge(response); responseData.put(response.getData(), response); activeDataSet.add(response.getData()); diff --git a/src/main/java/io/appform/databuilderframework/engine/OptimizedMultiThreadedDataFlowExecutor.java b/src/main/java/io/appform/databuilderframework/engine/OptimizedMultiThreadedDataFlowExecutor.java index b77c639..b89dcff 100644 --- a/src/main/java/io/appform/databuilderframework/engine/OptimizedMultiThreadedDataFlowExecutor.java +++ b/src/main/java/io/appform/databuilderframework/engine/OptimizedMultiThreadedDataFlowExecutor.java @@ -114,10 +114,9 @@ protected DataExecutionResponse run( } if (null != response) { Preconditions.checkArgument(response.getData().equalsIgnoreCase(data), - String.format( "Builder is supposed to produce %s but produces %s", data, - response.getData())); + response.getData()); dataSetAccessor.merge(response); responseData.put(response.getData(), response); activeDataSet.add(response.getData()); diff --git a/src/main/java/io/appform/databuilderframework/engine/SimpleDataFlowExecutor.java b/src/main/java/io/appform/databuilderframework/engine/SimpleDataFlowExecutor.java index d01a56f..bd61382 100644 --- a/src/main/java/io/appform/databuilderframework/engine/SimpleDataFlowExecutor.java +++ b/src/main/java/io/appform/databuilderframework/engine/SimpleDataFlowExecutor.java @@ -10,6 +10,7 @@ import java.util.HashSet; import java.util.Map; import java.util.TreeMap; +import java.util.function.Supplier; /** * The executor for a {@link io.appform.databuilderframework.model.DataFlow}. @@ -42,7 +43,7 @@ protected DataExecutionResponse run( val activeDataSet = new HashSet(); val dependencyHierarchy = executionGraph.getDependencyHierarchy(); val newlyGeneratedData = new HashSet(); - val processedBuilders = Collections.synchronizedSet(new HashSet()); + val processedBuilders = new HashSet(); dataSetAccessor.merge(dataDelta); dataDelta.getDelta().forEach(data -> activeDataSet.add(data.getData())); @@ -80,10 +81,9 @@ protected DataExecutionResponse run( dataSet.accessor().getAccesibleDataSetFor(builder))); if (null != response) { Preconditions.checkArgument(response.getData().equalsIgnoreCase(builderMeta.getProduces()), - String.format( - "Builder is supposed to produce %s but produces %s", - builderMeta.getProduces(), - response.getData())); + "Builder is supposed to produce %s but produces %s", + builderMeta.getProduces(), + response.getData()); dataSetAccessor.merge(response); responseData.put(response.getData(), response); response.setGeneratedBy(builderMeta.getName()); @@ -93,7 +93,7 @@ protected DataExecutionResponse run( newlyGeneratedData.add(response.getData()); } } - log.trace("Ran " + builderMeta.getName()); + log.trace("Ran {}", builderMeta.getName()); processedBuilders.add(builderMeta); for (DataBuilderExecutionListener listener : dataBuilderExecutionListener) { try { diff --git a/src/main/java/io/appform/databuilderframework/engine/Utils.java b/src/main/java/io/appform/databuilderframework/engine/Utils.java index fad6d17..68beff0 100644 --- a/src/main/java/io/appform/databuilderframework/engine/Utils.java +++ b/src/main/java/io/appform/databuilderframework/engine/Utils.java @@ -10,6 +10,7 @@ import io.appform.databuilderframework.model.DataBuilderMeta; import lombok.experimental.UtilityClass; import lombok.extern.slf4j.Slf4j; +import java.util.concurrent.ConcurrentHashMap; import java.util.*; @@ -20,12 +21,15 @@ @Slf4j public final class Utils { + private static final Map, String> CLASS_TO_NAME_MAPPING = new ConcurrentHashMap<>(); + public static String name(Object object) { return name(object.getClass()); } public static String name(Class clazz) { - return CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, clazz.getSimpleName()); + return CLASS_TO_NAME_MAPPING.computeIfAbsent(clazz, + aClass -> CaseFormat.UPPER_CAMEL.to(CaseFormat.UPPER_UNDERSCORE, clazz.getSimpleName())); } public static boolean isEmpty(Collection collection) { diff --git a/src/main/java/io/appform/databuilderframework/engine/impl/MixedDataBuilderFactory.java b/src/main/java/io/appform/databuilderframework/engine/impl/MixedDataBuilderFactory.java index 1407aaa..56a6c29 100644 --- a/src/main/java/io/appform/databuilderframework/engine/impl/MixedDataBuilderFactory.java +++ b/src/main/java/io/appform/databuilderframework/engine/impl/MixedDataBuilderFactory.java @@ -42,8 +42,9 @@ public void register(DataBuilder dataBuilder) { public DataBuilder create(DataBuilderMeta dataBuilderMeta) throws DataBuilderFrameworkException { val builderName = dataBuilderMeta.getName(); - if (builderInstances.containsKey(builderName)) { - return builderInstances.get(builderName); + val builderInstance = builderInstances.get(builderName); + if (builderInstance != null) { + return builderInstance; } val dataBuilderClass = dataBuilderMetadataManager.getDataBuilderClass(builderName); if (null == dataBuilderClass) { diff --git a/src/main/java/io/appform/databuilderframework/model/DataSet.java b/src/main/java/io/appform/databuilderframework/model/DataSet.java index 26b6768..1ce200a 100644 --- a/src/main/java/io/appform/databuilderframework/model/DataSet.java +++ b/src/main/java/io/appform/databuilderframework/model/DataSet.java @@ -6,6 +6,8 @@ import com.google.common.collect.Maps; import io.appform.databuilderframework.engine.DataSetAccessor; import io.appform.databuilderframework.engine.Utils; +import lombok.Getter; +import lombok.Setter; import lombok.val; import org.hibernate.validator.constraints.NotEmpty; @@ -39,25 +41,17 @@ public DataSet(Map availableData) { } public DataSet add(String dataName, Data data) { - val stamp = lock.writeLock(); - try { - this.availableData.put(dataName, data); - return this; - } - finally { - lock.unlockWrite(stamp); - } + return safeWriteOp(() -> { + availableData.put(dataName, data); + return DataSet.this; + }); } public DataSet add(final Collection data) { - val stamp = lock.writeLock(); - try { + return safeWriteOp(() -> { data.forEach(d -> availableData.put(d.getData(), d)); - return this; - } - finally { - lock.unlockWrite(stamp); - } + return DataSet.this; + }); } public DataSet add(T data) { @@ -118,4 +112,14 @@ private T safeOp(Supplier operation) { lock.unlockRead(stamp); } } + + private T safeWriteOp(Supplier operation) { + val stamp = lock.writeLock(); + try { + return operation.get(); + } + finally { + lock.unlockWrite(stamp); + } + } }