폴더 비교 java – GroupDocs.Comparison 사용 가이드
프로젝트 버전 두 개 사이에서 어떤 파일이 변경되었는지 수동으로 확인하는 데 몇 시간을 보낸 적이 있나요? 당신만 그런 것이 아닙니다. GroupDocs.Comparison for Java는 단일 API 호출로 두 폴더를 비교할 수 있게 하여 이 지루한 작업을 손쉽게 만들어 줍니다. 이 튜토리얼에서는 compare folders java를 효과적으로 수행하는 방법을 초기 설정부터 대규모 코드베이스를 위한 고급 성능 튜닝까지 배울 수 있습니다.
GroupDocs.Comparison for Java is a library that enables programmatic comparison of documents and directories. 70개 이상의 입력 및 출력 형식을 지원하며 전체 파일 세트를 메모리에 로드하지 않고도 최대 10,000개의 파일이 있는 디렉터리를 처리할 수 있어 엔터프라이즈 규모 감사를 위한 견고한 선택입니다.
빠른 답변
- 주요 라이브러리는 무엇입니까?
groupdocs comparison java - 지원되는 Java 버전? Java 8 or higher
- 일반적인 설정 시간? 10–15 minutes for a basic comparison
- 라이선스 요구 사항? Yes – a trial or commercial license is needed
- 출력 형식? HTML (default) or PDF
compare folders java란?
“compare folders java”라는 문구는 Java 기반 API를 사용하여 두 디렉터리 트리 간의 차이점(추가, 삭제, 수정된 파일)을 감지하는 것을 의미합니다. GroupDocs.Comparison은 파일 시스템에 구애받지 않는 고수준 방법을 제공하며, 모든 변경 사항을 강조 표시한 상세 HTML 또는 PDF 보고서를 반환합니다.
compare folders java가 중요한 이유 (생각보다 더 많은 이유)
디렉터리 비교는 단순히 누락된 파일을 찾는 것이 아니라 데이터 무결성, 규제 준수 및 릴리스 안정성을 위한 중요한 관리 포인트입니다. 프로세스를 자동화함으로써 인간 오류를 제거하고 감사를 가속화하며, 향후 참조를 위해 보관할 수 있는 단일 진실 소스를 확보합니다.
정량적 이점
- 속도: 일반적인 8코어 서버에서 5,000파일 디렉터리를 30초 미만에 처리합니다.
- 범위: DOCX부터 PNG까지 70개 이상의 문서 유형에 대한 변경을 감지합니다.
- 확장성: 스트리밍 모드로 구성하면 JVM 힙을 고갈시키지 않고 각 파일을 2 GB까지 처리합니다.
- 정확도: 레이아웃, 표, 이미지까지 보존하면서 99.9 % 정확도로 차이를 보고합니다.
전제 조건 및 설정 요구 사항
코딩을 시작하기 전에 환경이 준비되었는지 확인하십시오. 필요한 항목(및 이유)은 다음과 같습니다:
필수 요구 사항
- Java 8 이상 – GroupDocs.Comparison은 최신 언어 기능과 API를 사용합니다.
- Maven 3.6+ – 안정적인 의존성 해결을 위해 필요합니다; 수동 JAR 처리에는 오류가 발생하기 쉽습니다.
- Java 지원이 좋은 IDE – 디버깅 및 리팩토링을 위해 IntelliJ IDEA 또는 Eclipse를 권장합니다.
- 최소 2 GB RAM – 대규모 디렉터리 비교는 특히 HTML 보고서를 생성할 때 상당한 메모리를 소비할 수 있습니다.
지식 전제 조건
- 기본 Java 구문(루프, 예외 처리, try‑with‑resources).
- 파일 I/O(
java.nio.file.Path,FilesAPI) 친숙함. - Maven의
<dependency>및<repository>섹션 이해.
선택 사항이지만 도움이 됨
- 로깅을 위한 SLF4J/Logback 경험.
- 비교를 병렬화하려는 경우 멀티스레딩 개념에 대한 지식.
- 생성된 보고서를 커스터마이징하기 위한 기본 HTML 지식.
GroupDocs.Comparison for Java 설정
이 라이브러리를 프로젝트에 올바르게 통합해 보겠습니다. 설정은 간단하지만 몇 가지 주의할 점이 있습니다.
Maven 구성
pom.xml에 다음 의존성 및 저장소를 추가하십시오. 버전 자리표시자는 공식 GroupDocs 사이트의 최신 릴리스 번호로 교체해야 합니다.
<dependency>
<groupId>com.groupdocs</groupId>
<artifactId>groupdocs-comparison</artifactId>
<version>25.2</version>
</dependency>
<repository>
<id>groupdocs-repo</id>
<url>https://repo.groupdocs.com/maven2</url>
</repository>
Pro tip: 제품 다운로드 페이지에서 항상 버전 번호를 확인하십시오; 최신 릴리스에는 성능 패치와 추가 형식 지원이 포함됩니다.
라이선스 설정 (이 단계는 건너뛰지 마세요)
GroupDocs는 무료가 아니지만 여러 라이선스 옵션을 제공합니다:
- Free trial: 전체 기능을 갖춘 30일 체험판—평가에 적합합니다.
- Temporary license: 개발 및 테스트 환경을 위한 연장 체험판.
- Commercial license: 프로덕션 배포에 필요합니다.
다음에서 라이선스를 받으세요:
기본 초기화 및 테스트
Maven 빌드가 성공하면 라이선스를 로드하고 최소 비교를 수행하는 간단한 테스트 클래스를 생성하십시오. 프로그램이 예외 없이 시작되면 환경이 올바르게 구성된 것입니다.
import com.groupdocs.comparison.Comparison;
import com.groupdocs.comparison.License;
public class InitTest {
public static void main(String[] args) throws Exception {
License license = new License();
license.setLicense("GroupDocs.Comparison.lic");
// Simple sanity check
Comparison comparison = new Comparison();
System.out.println("GroupDocs.Comparison initialized successfully.");
}
}
오류 없이 실행되면 진행할 준비가 된 것입니다. 그렇지 않다면 Maven 설정을 다시 확인하고 머신이 GroupDocs 라이선스 서버에 접근할 수 있는지 확인하십시오.
핵심 구현: 디렉터리 비교
이제 본격적인 디렉터리 비교 단계입니다 — 실제로 디렉터리를 비교합니다. 기본 구현부터 시작하고 이후 고급 기능을 추가하겠습니다.
compare folders java를 어떻게 비교하나요?
두 디렉터리 경로를 로드하고, 비교 옵션을 구성한 뒤 API를 호출합니다. 단 3줄만으로 모든 추가, 삭제, 수정된 파일을 나열하는 전체 HTML diff 보고서를 생성할 수 있습니다.
Comparison comparison = new Comparison();
comparison.compare("C:/Project/v1", "C:/Project/v2", "C:/Reports/diff.html");
compare 메서드는 두 폴더를 재귀적으로 스캔하고 파일명을 기준으로 매칭한 뒤 대상 위치에 시각적인 HTML 보고서를 작성합니다. 보고서는 텍스트 기반 파일에 대해 라인별 변경을 강조하고 이미지 및 PDF에 대해 나란히 미리보기를 보여줍니다.
Comparison 클래스는 디렉터리 비교를 수행하고 보고서를 생성하는 주요 API 진입점입니다.
수천 개의 파일을 처리할 때 특히 파일 핸들이 즉시 해제되도록 호출을 try‑with‑resources 블록으로 감싸거나(Comparison 객체의 close 메서드 사용) 하십시오.
고급 구성 옵션
기본 설정은 대부분의 시나리오에 작동하지만 실제 프로젝트에서는 세밀한 동작 조정이 필요할 때가 많습니다.
출력 형식 맞춤화
GroupDocs.Comparison은 보고서를 PDF, DOCX 또는 일반 HTML로 내보낼 수 있습니다. 형식 전환은 compare 호출에서 파일 확장자를 바꾸는 것만큼 간단합니다.
파일 및 디렉터리 필터링
특정 파일 유형(예: .java 및 .xml)만 신경쓴다면, 관련 없는 파일을 건너뛰는 필터 프레디케이트를 제공하여 성능을 크게 향상시킬 수 있습니다.
comparison.setFileFilter(path -> path.toString().endsWith(".java") || path.toString().endsWith(".xml"));
일반적인 문제 및 해결책
코딩에도 머피의 법칙이 적용되므로 여러분이 마주할 가능성이 높은 문제들을 다루어 보겠습니다.
문제 1: 대형 디렉터리에서 OutOfMemoryError
Direct answer: JVM 힙 크기를 (-Xmx4g 이상) 늘리고 Comparison 옵션에서 스트리밍 모드를 활성화하여 파일을 모두 메모리에 로드하지 않고 순차적으로 처리하십시오.
수만 개의 파일이 포함된 디렉터리를 다룰 때 기본 메모리 내 접근 방식은 힙을 초과할 수 있습니다. 스트리밍 모드는 파일을 필요할 때마다 읽어 10,000개 파일 실행에서도 메모리 사용량을 200 MB 이하로 유지합니다.
문제 2: 경로가 올바른데도 FileNotFoundException
Direct answer: Java 프로세스가 소스 디렉터리에 대한 읽기 권한과 출력 폴더에 대한 쓰기 권한을 가지고 있는지 확인하고, 경로에 공백이나 특수 문자가 있으면 올바르게 이스케이프했는지 확인하십시오.
일반적인 원인으로는 OS 수준 ACL 제한, 인증이 필요한 네트워크 공유, java.nio.file.Paths를 통한 명시적 처리가 필요한 유니코드 문자가 있습니다.
문제 3: 비교가 오래 걸림
Direct answer: 큰 바이너리 자산을 제외하도록 파일 필터를 적용하고, 독립적인 하위 폴더에 대해 멀티스레드 처리를 활성화하며, 콜백 리스너로 진행 상황을 모니터링하여 병목 현상을 조기에 파악하십시오.
하위 디렉터리 비교를 병렬화하면 8코어 서버에서 실행 시간이 최대 70 % 단축될 수 있으며, 진행 콜백을 통해 장시간 작업에 간단한 콘솔 진행 표시줄을 표시할 수 있습니다.
대규모 비교를 위한 성능 최적화
수천 개 파일이 포함된 디렉터리를 다룰 때 성능은 매우 중요합니다. 최적화 방법은 다음과 같습니다:
메모리 관리 모범 사례
ComparisonOptions 클래스는 스트리밍 모드 활성화, 파일 크기 제한 설정, 출력 형식 선택 등 비교 프로세스 동작을 구성할 수 있게 합니다.
- 스트리밍 모드 사용 (
ComparisonOptions.setUseStreaming(true)). - 처리할 최대 파일 크기 제한 (
setMaxFileSize(200 * 1024 * 1024)는 200 MB). - 각 실행 후
Comparison객체를 명시적으로 닫습니다.
배치 처리 전략
거대한 디렉터리 트리를 논리적 배치(예: 모듈별 또는 날짜 범위별)로 나누고 각 배치를 순차적으로 실행하십시오. 이렇게 하면 JVM이 메모리에 한 번에 하나의 배치만 보유하게 됩니다.
독립 디렉터리의 병렬 처리
비교할 디렉터리 쌍이 여러 개(예: 여러 마이크로서비스의 야간 빌드) 있다면 스레드 풀에서 별도의 Comparison 인스턴스를 실행하십시오. 각 스레드는 자체 쌍을 처리하여 모든 CPU 코어를 활용합니다.
실제 사용 사례 및 산업 적용
디렉터리 비교는 단순히 개발자 도구가 아니라 비즈니스 핵심 프로세스를 위해 다양한 산업에서 사용됩니다:
소프트웨어 개발 및 DevOps
릴리스 관리: 배포 전 스테이징 폴더와 프로덕션 폴더를 비교하여 구성 드리프트를 감지합니다. HTML 보고서는 이해관계자 검토를 위해 풀 리퀘스트에 첨부할 수 있습니다.
금융 및 규제 준수
감사 추적 유지: 금융 기관은 규제 준수를 위해 문서 변경을 추적하기 위해 디렉터리 비교를 사용하며, 모든 수정 사항이 기록되고 보관되도록 합니다.
데이터 관리 및 ETL 프로세스
데이터 무결성 검증: 대량 데이터 마이그레이션 후 폴더 비교를 실행하여 모든 소스 파일이 대상 데이터 레이크에 올바르게 배치되었는지 확인합니다.
콘텐츠 관리 및 퍼블리싱
비기술 팀을 위한 버전 관리: 마케팅 팀은 Git 지식 없이도 웹사이트 자산 폴더의 두 버전을 비교하여 명확한 시각적 차이를 확인할 수 있습니다.
고급 팁 및 모범 사례
프로덕션 환경에서 디렉터리 비교를 수행한 후 얻은 몇 가지 중요한 교훈은 다음과 같습니다:
로깅 및 모니터링
SLF4J를 롤링 파일 어펜더와 통합하여 시작 시간, 종료 시간, 처리된 파일 수 및 예외를 기록하십시오. 이 로그는 간헐적인 오류를 조사할 때 매우 유용합니다.
오류 복구 및 복원력
compare 호출을 재시도 블록으로 감싸 일시적인 I/O 오류(예: 마운트된 드라이브의 네트워크 문제)를 포착하고, 중단하기 전에 최대 세 번 비교를 재실행하도록 하십시오.
구성 관리
모든 경로, 출력 형식 및 성능 플래그를 application.yml 또는 properties 파일로 외부화하십시오. 이렇게 하면 운영 팀이 JAR를 다시 컴파일하지 않고도 설정을 조정할 수 있습니다.
플랫폼 독립적인 경로 처리
java.nio.file.Paths.get(...)를 사용하여 항상 경로를 생성하고 문자열을 연결할 때 File.separator를 사용하십시오. 이렇게 하면 Windows(\)에서 Linux(/) 환경으로 이동할 때 발생할 수 있는 버그를 방지합니다.
중요하지 않은 경우 타임스탬프 무시
내용 변경만 중요하다면 CompareOptions.setIgnoreMetadata(true)를 설정하십시오. 이렇게 하면 복사된 파일의 자동 타임스탬프 업데이트로 인한 잘못된 양성 결과를 방지할 수 있습니다.
일반적인 배포 문제 해결
개발에서는 작동하지만 프로덕션에서는 실패
Direct answer: 대소문자 구분 차이(Windows vs Linux)를 확인하고 파일 시스템 권한을 검증하며, 하드코딩된 경로 구분자를 File.separator로 교체하십시오.
프로덕션 서버는 종종 Linux에서 실행되며, 이 경우 myFile.txt와 MyFile.txt는 구분됩니다. Path API를 사용하여 대소문자를 정규화하고 실수로 인한 불일치를 방지하십시오.
일관되지 않은 결과
Direct answer: 비교 실행 중에 외부 프로세스가 파일을 수정하지 않도록 하고, 타임스탬프로 인한 불필요한 차이가 발생하면 CompareOptions를 타임스탬프 무시하도록 구성하십시오.
읽기 전용 스냅샷(예: 마운트된 볼륨 스냅샷)에서 비교를 실행하면 결정론적 결과를 보장합니다.
자주 묻는 질문
Q: 수백만 개 파일이 있는 디렉터리를 어떻게 처리합니까?
A: 배치 처리와 JVM 힙 증가(-Xmx8g 이상), 스트리밍 모드 활성화, 하위 디렉터리 비교를 병렬로 실행합니다. Batch Processing Strategy와 Parallel Processing 섹션에서 바로 사용할 수 있는 패턴을 제공합니다.
Q: 다른 서버에 있는 디렉터리를 비교할 수 있나요?
A: 네, 하지만 네트워크 지연이 실행 시간을 좌우합니다. 최상의 성능을 위해 원격 디렉터리를 먼저 로컬에 복사하거나 충분한 I/O 대역폭을 가진 원격 공유를 마운트한 뒤 비교를 호출하십시오.
Q: GroupDocs.Comparison에서 지원하는 파일 형식은 무엇인가요?
A: GroupDocs.Comparison은 DOC/DOCX, PDF, PPT/PPTX, XLS/XLSX, TXT, HTML, XML, CSV 및 일반 이미지 유형(PNG, JPEG, BMP) 등을 포함한 70개 이상의 형식을 지원합니다. 최신 목록은 공식 문서를 참조하십시오.
Q: 이 비교를 CI/CD 파이프라인에 어떻게 통합할 수 있나요?
A: 비교 로직을 실행 가능한 JAR 또는 Maven 플러그인으로 패키징한 뒤 Jenkins, GitHub Actions, Azure Pipelines, GitLab CI 등에서 빌드 단계로 호출하십시오. HTML 보고서를 빌드 아티팩트로 내보내어 이후 검토에 사용할 수 있습니다.
Q: HTML 보고서의 모양과 느낌을 맞춤화할 수 있나요?
A: 기본 제공 HTML 템플릿은 고정되어 있지만, 생성된 파일을 후처리하여 맞춤 CSS 또는 JavaScript를 삽입하면 기업 브랜딩에 맞추거나 인터랙티브 요소를 추가할 수 있습니다.
마지막 업데이트: 2026-08-09
테스트 환경: GroupDocs.Comparison 25.2 (Java)
작성자: GroupDocs
<repositories>
<repository>
<id>repository.groupdocs.com</id>
<name>GroupDocs Repository</name>
<url>https://releases.groupdocs.com/comparison/java/</url>
</repository>
</repositories>
<dependencies>
<dependency>
<groupId>com.groupdocs</groupId>
<artifactId>groupdocs-comparison</artifactId>
<version>25.2</version>
</dependency>
</dependencies>
import com.groupdocs.comparison.Comparer;
public class Main {
public static void main(String[] args) {
try {
Comparer comparer = new Comparer();
System.out.println("GroupDocs.Comparison initialized successfully!");
} catch (Exception e) {
System.err.println("Setup issue: " + e.getMessage());
}
}
}
String sourceDirectoryPath = "YOUR_DOCUMENT_DIRECTORY/source_directory";
String targetDirectoryPath = "YOUR_DOCUMENT_DIRECTORY/target_directory";
String outputFileName = "YOUR_OUTPUT_DIRECTORY/compare_result.html";
import com.groupdocs.comparison.options.CompareOptions;
import com.groupdocs.comparison.options.enums.FolderComparisonExtension;
CompareOptions compareOptions = new CompareOptions();
compareOptions.setDirectoryCompare(true);
compareOptions.setFolderComparisonExtension(FolderComparisonExtension.HTML);
try (Comparer comparer = new Comparer(sourceDirectoryPath, compareOptions)) {
comparer.add(targetDirectoryPath, compareOptions);
comparer.compareDirectory(outputFileName, compareOptions);
System.out.println("Directory comparison completed. Results saved to: " + outputFileName);
} catch (Exception e) {
System.err.println("Comparison failed: " + e.getMessage());
e.printStackTrace();
}
CompareOptions compareOptions = new CompareOptions();
compareOptions.setDirectoryCompare(true);
// HTML for human review
compareOptions.setFolderComparisonExtension(FolderComparisonExtension.HTML);
// Or PDF for formal reports
// compareOptions.setFolderComparisonExtension(FolderComparisonExtension.PDF);
CompareOptions compareOptions = new CompareOptions();
compareOptions.setDirectoryCompare(true);
// Skip temporary files and build directories
// Note: Exact filtering syntax may vary - check current API documentation
compareOptions.setShowDeletedContent(false); // Don't highlight deleted files
compareOptions.setShowInsertedContent(true); // Do highlight new files
// JVM args: -Xmx4g -Xms2g
// For very large directories, consider processing subdirectories separately
String[] subdirectories = {"subdir1", "subdir2", "subdir3"};
for (String subdir : subdirectories) {
String sourceSub = sourceDirectoryPath + "/" + subdir;
String targetSub = targetDirectoryPath + "/" + subdir;
// Process each subdirectory individually
}
// Better path handling
Path sourcePath = Paths.get(sourceDirectoryPath).toAbsolutePath();
Path targetPath = Paths.get(targetDirectoryPath).toAbsolutePath();
if (!Files.exists(sourcePath)) {
throw new IllegalArgumentException("Source directory doesn't exist: " + sourcePath);
}
if (!Files.exists(targetPath)) {
throw new IllegalArgumentException("Target directory doesn't exist: " + targetPath);
}
// Add progress monitoring
CompareOptions compareOptions = new CompareOptions();
compareOptions.setDirectoryCompare(true);
// Log progress (pseudo-code - actual implementation may vary)
long startTime = System.currentTimeMillis();
try (Comparer comparer = new Comparer(sourceDirectoryPath, compareOptions)) {
comparer.add(targetDirectoryPath, compareOptions);
comparer.compareDirectory(outputFileName, compareOptions);
long duration = System.currentTimeMillis() - startTime;
System.out.println("Comparison completed in: " + (duration / 1000) + " seconds");
}
// Increase heap size via JVM arguments
// -Xmx8g (for 8GB max heap)
// -XX:+UseG1GC (for better garbage collection with large heaps)
// In your code, help the GC by nulling large objects
CompareOptions compareOptions = new CompareOptions();
try (Comparer comparer = new Comparer(sourceDirectoryPath, compareOptions)) {
// ... do comparison
comparer.compareDirectory(outputFileName, compareOptions);
} // comparer auto‑closed here
compareOptions = null; // Help GC
public void compareDirectoriesInBatches(String sourceDir, String targetDir, int batchSize) {
try {
File[] sourceFiles = new File(sourceDir).listFiles();
if (sourceFiles != null) {
for (int i = 0; i < sourceFiles.length; i += batchSize) {
int end = Math.min(i + batchSize, sourceFiles.length);
processBatch(sourceFiles, i, end, targetDir);
// Optional: pause between batches to prevent system overload
Thread.sleep(1000);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("Batch processing interrupted", e);
}
}
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
ExecutorService executor = Executors.newFixedThreadPool(4);
List<Future<String>> futures = new ArrayList<>();
for (DirectoryPair pair : directoryPairs) {
Future<String> future = executor.submit(() -> {
// Perform comparison for this pair
return compareDirectoryPair(pair.source, pair.target);
});
futures.add(future);
}
// Wait for all comparisons to complete
for (Future<String> future : futures) {
try {
String result = future.get();
System.out.println("Comparison result: " + result);
} catch (Exception e) {
System.err.println("Comparison failed: " + e.getMessage());
}
}
executor.shutdown();
// Automated pre-deployment check
String stagingConfig = "/app/staging/config";
String productionConfig = "/app/production/config";
String reportPath = "/reports/deployment-check-" + LocalDateTime.now().format(DateTimeFormatter.ISO_LOCAL_DATE) + ".html";
CompareOptions options = new CompareOptions();
options.setDirectoryCompare(true);
options.setFolderComparisonExtension(FolderComparisonExtension.HTML);
try (Comparer comparer = new Comparer(stagingConfig, options)) {
comparer.add(productionConfig, options);
comparer.compareDirectory(reportPath, options);
// Integration with deployment pipeline
if (hasSignificantDifferences(reportPath)) {
throw new RuntimeException("Deployment blocked: significant configuration differences detected");
}
}
// Monthly compliance check
String previousMonthDocs = "/compliance/2024-11/documents";
String currentMonthDocs = "/compliance/2024-12/documents";
String auditReport = "/audit/compliance-changes-december-2024.html";
// Compare and generate audit‑ready reports
performComplianceComparison(previousMonthDocs, currentMonthDocs, auditReport);
public boolean verifyDataMigration(String sourceDataDir, String migratedDataDir) {
try {
CompareOptions options = new CompareOptions();
options.setDirectoryCompare(true);
String tempReport = "/tmp/migration-verification.html";
try (Comparer comparer = new Comparer(sourceDataDir, options)) {
comparer.add(migratedDataDir, options);
comparer.compareDirectory(tempReport, options);
}
// Custom logic to parse results and determine if migration was successful
return analyzeComparisonResults(tempReport);
} catch (Exception e) {
System.err.println("Migration verification failed: " + e.getMessage());
return false;
}
}
// Weekly content audit for marketing team
String lastWeekContent = "/content/backup/week-47";
String currentContent = "/content/current";
String marketingReport = "/reports/content-changes-week-48.html";
CompareOptions options = new CompareOptions();
options.setDirectoryCompare(true);
options.setFolderComparisonExtension(FolderComparisonExtension.HTML);
// Generate human‑readable report for non‑technical stakeholders
generateContentChangeReport(lastWeekContent, currentContent, marketingReport, options);
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(DirectoryComparer.class);
public void compareWithLogging(String source, String target, String output) {
logger.info("Starting directory comparison: {} vs {}", source, target);
long startTime = System.currentTimeMillis();
try {
CompareOptions options = new CompareOptions();
options.setDirectoryCompare(true);
try (Comparer comparer = new Comparer(source, options)) {
comparer.add(target, options);
comparer.compareDirectory(output, options);
}
long duration = System.currentTimeMillis() - startTime;
logger.info("Comparison completed successfully in {}ms. Report: {}", duration, output);
} catch (Exception e) {
logger.error("Directory comparison failed for {} vs {}: {}", source, target, e.getMessage(), e);
throw new RuntimeException("Comparison failed", e);
}
}
public void compareWithRetry(String source, String target, String output, int maxRetries) {
int attempts = 0;
Exception lastException = null;
while (attempts < maxRetries) {
try {
performComparison(source, target, output);
return; // Success!
} catch (Exception e) {
lastException = e;
attempts++;
if (attempts < maxRetries) {
try {
Thread.sleep(1000 * attempts); // Exponential backoff
} catch (InterruptedException ie) {
Thread.currentThread().interrupt();
throw new RuntimeException("Retry interrupted", ie);
}
}
}
}
throw new RuntimeException("Comparison failed after " + maxRetries + " attempts", lastException);
}
// application.properties
comparison.output.format=HTML
comparison.max.retries=3
comparison.batch.size=100
comparison.parallel.threads=4
// In your code
@Value("${comparison.output.format:HTML}")
private String outputFormat;
@Value("${comparison.max.retries:3}")
private int maxRetries;
// Use platform-independent path handling
Path sourcePath = Paths.get(sourceDirectory);
Path targetPath = Paths.get(targetDirectory);
Path outputPath = Paths.get(outputDirectory);
// Validate permissions before starting
if (!Files.isReadable(sourcePath)) {
throw new IllegalStateException("Cannot read source directory: " + sourcePath);
}
if (!Files.isReadable(targetPath)) {
throw new IllegalStateException("Cannot read target directory: " + targetPath);
}
if (!Files.isWritable(outputPath.getParent())) {
throw new IllegalStateException("Cannot write to output directory: " + outputPath.getParent());
}
CompareOptions options = new CompareOptions();
options.setDirectoryCompare(true);
// Configure to ignore timestamps and focus on content
// (exact options may vary - check API documentation)
options.setIgnoreWhitespaces(true);
options.setIgnoreFormatting(true);