مقارنة المجلدات java – دليل باستخدام GroupDocs.Comparison
هل قضيت ساعات في فحص الملفات التي تغيرت يدويًا بين إصداري مشروع؟ لست وحدك. GroupDocs.Comparison for Java يجعل هذه المهمة المملة سهلة من خلال السماح لك بمقارنة مجلدين باستدعاء API واحد. في هذا البرنامج التعليمي ستتعلم كيفية مقارنة المجلدات java بفعالية، من الإعداد الأولي إلى تحسين الأداء المتقدم للقاعدة الضخمة من الشيفرات.
GroupDocs.Comparison for Java هي مكتبة تمكّن المقارنة البرمجية للمستندات والدلائل. تدعم أكثر من 70 تنسيق إدخال وإخراج ويمكنها معالجة دلائل تحتوي على ما يصل إلى 10,000 ملف دون تحميل مجموعة الملفات بالكامل في الذاكرة، مما يجعلها خيارًا قويًا للمراجعات على مستوى المؤسسات.
إجابات سريعة
- ما هي المكتبة الأساسية؟
groupdocs comparison java - إصدار Java المدعوم؟ Java 8 أو أعلى
- الوقت المعتاد للإعداد؟ 10–15 دقيقة للمقارنة الأساسية
- متطلبات الترخيص؟ نعم – يلزم ترخيص تجريبي أو تجاري
- تنسيقات الإخراج؟ HTML (افتراضي) أو PDF
ما هي مقارنة المجلدات java؟
تشير عبارة “compare folders java” إلى استخدام API مبني على Java لاكتشاف الاختلافات — ملفات مضافة، محذوفة أو معدلة — بين شجرتي دليل. يوفر GroupDocs.Comparison طريقة عالية المستوى وغير معتمدة على نظام الملفات لأداء هذه العملية، مع إرجاع تقرير HTML أو PDF مفصل يبرز كل تغيير.
لماذا مقارنة المجلدات java مهمة (أكثر مما تتصور)
المقارنة بين الأدلة ليست مجرد اكتشاف ملفات مفقودة؛ إنها نقطة تحكم حاسمة لسلامة البيانات، الامتثال التنظيمي، واستقرار الإصدارات. من خلال أتمتة العملية، تلغي الأخطاء البشرية، تسرّع عمليات التدقيق، وتحصل على مصدر واحد للحقائق يمكن أرشفته للرجوع إليه مستقبلاً.
الفوائد المرقمة
- السرعة: يعالج دلائل بـ 5,000 ملف في أقل من 30 ثانية على خادم عادي بـ 8 نوى.
- التغطية: يكتشف التغييرات عبر أكثر من 70 نوع مستند، من DOCX إلى PNG.
- القابلية للتوسع: يتعامل مع ملفات تصل إلى 2 GB لكل منها دون استنفاد ذاكرة JVM عند تفعيل وضع البث.
- الدقة: يرفع الفروقات بدقة 99.9 %، مع الحفاظ على التخطيط والجداول والصور.
المتطلبات المسبقة وإعداد البيئة
قبل أن نبدأ بالبرمجة، تأكد من جاهزية بيئتك. إليك ما ستحتاجه (ولماذا):
المتطلبات الأساسية
- Java 8 أو أعلى – يستخدم GroupDocs.Comparison ميزات لغة حديثة وواجهات برمجة تطبيقات متطورة.
- Maven 3.6+ – لضمان حل الاعتمادات بشكل موثوق؛ التعامل اليدوي مع JAR عرضة للأخطاء.
- IDE يدعم Java جيدًا – يُنصح بـ IntelliJ IDEA أو Eclipse لتسهيل التصحيح وإعادة الهيكلة.
- على الأقل 2 GB RAM – قد تستهلك مقارنات الأدلة الكبيرة ذاكرةً ملحوظة، خاصةً عند إنشاء تقارير HTML.
المعرفة المسبقة
- أساسيات لغة Java (الحلقات، معالجة الاستثناءات، try‑with‑resources).
- الإلمام بملفات I/O (
java.nio.file.Path، APIFiles). - فهم أقسام
<dependency>و<repository>في Maven.
اختياري لكن مفيد
- خبرة في SLF4J/Logback لتسجيل الأحداث.
- معرفة بمفاهيم الـ multi‑threading إذا كنت تخطط لتوازي المقارنات.
- معرفة أساسية بـ 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>
نصيحة احترافية: تحقق دائمًا من رقم الإصدار على صفحة تنزيل المنتج؛ الإصدارات الأحدث تتضمن تصحيحات أداء ودعم صيغ إضافية.
إعداد الترخيص (لا تتخطاه)
GroupDocs ليس مجانيًا، لكنهم يقدمون عدة خيارات للترخيص:
- تجربة مجانية: تجربة لمدة 30 يومًا مع جميع الميزات — مثالية للتقييم.
- ترخيص مؤقت: تجربة ممتدة لبيئات التطوير والاختبار.
- ترخيص تجاري: مطلوب للنشر في بيئات الإنتاج.
احصل على الترخيص من:
- Purchase a license للإنتاج
- Get a temporary 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.
التنفيذ الأساسي: مقارنة الأدلة
الآن إلى الجزء الرئيسي — مقارنة الأدلة فعليًا. سنبدأ بتنفيذ بسيط ثم نضيف ميزات متقدمة.
كيف تقارن المجلدات java؟
حمّل مسارين للدليل، اضبط خيارات المقارنة، واستدعِ الـ API. في ثلاث أسطر فقط يمكنك توليد تقرير HTML كامل يوضح كل ملف مضاف، محذوف أو معدل.
Comparison comparison = new Comparison();
comparison.compare("C:/Project/v1", "C:/Project/v2", "C:/Reports/diff.html");
طريقة compare تفحص كلا المجلدين بشكل متكرر، تطابق الملفات بالاسم، وتكتب تقرير HTML بصري إلى الموقع المستهدف. التقرير يبرز التغييرات سطرًا بسطر للملفات النصية ويعرض معاينات جنبًا إلى جنب للصور وPDF.
فئة Comparison هي نقطة الدخول الأساسية للـ API التي تُجري مقارنة الأدلة وتُنتج التقرير.
احيط الاستدعاء بكتلة try‑with‑resources (أو استخدم طريقة close لكائن Comparison) لضمان تحرير جميع مقابض الملفات بسرعة، خاصةً عند معالجة آلاف الملفات.
خيارات التكوين المتقدمة
الإعداد الأساسي يكفي لمعظم السيناريوهات، لكن المشاريع الواقعية غالبًا ما تحتاج سلوكًا مُحسّنًا.
تخصيص تنسيقات الإخراج
يمكن لـ GroupDocs.Comparison تصدير التقارير كـ PDF أو DOCX أو HTML بسيط. تغيير التنسيق يكون ببساطة عبر تعديل امتداد الملف في استدعاء compare.
تصفية الملفات والأدلة
إذا كنت تهتم بأنواع ملفات محددة (مثل .java و .xml)، قدم دالة تصفية لتخطي الملفات غير ذات الصلة وتحسين الأداء بشكل كبير.
comparison.setFileFilter(path -> path.toString().endsWith(".java") || path.toString().endsWith(".xml"));
المشكلات الشائعة والحلول
نستعرض الآن المشكلات التي قد تواجهها (قانون مورفي ينطبق على البرمجة أيضًا).
المشكلة 1: OutOfMemoryError مع أدلة كبيرة
الإجابة المباشرة: زد حجم heap الخاص بـ JVM (-Xmx4g أو أعلى) وفعل وضع البث في خيارات Comparison لمعالجة الملفات بشكل متسلسل بدلاً من تحميلها كلها في الذاكرة.
عند التعامل مع أدلة تحتوي على عشرات الآلاف من الملفات، قد يتجاوز النهج الافتراضي في الذاكرة الحد المسموح. وضع البث يقرأ كل ملف عند الحاجة، محافظًا على استهلاك الذاكرة تحت 200 MB حتى في تشغيلات بـ 10,000 ملف.
المشكلة 2: FileNotFoundException رغم صحة المسارات
الإجابة المباشرة: تأكد من أن عملية Java لديها صلاحيات قراءة للمجلدات المصدر وصلاحيات كتابة للمجلد الهدف؛ كما يجب التأكد من أن أي مسافات أو أحرف خاصة في المسار مُهربة بشكل صحيح.
الأسباب الشائعة تشمل قيود ACL على مستوى النظام، مشاركات الشبكة التي تتطلب مصادقة، وأحرف Unicode التي تحتاج إلى معالجة صريحة عبر java.nio.file.Paths.
المشكلة 3: المقارنة تستغرق وقتًا طويلاً
الإجابة المباشرة: طبّق فلاتر ملفات لاستبعاد الأصول الثنائية الكبيرة، فعّل المعالجة المتعددة الخيوط للأدلة الفرعية المستقلة، واستخدم مستمع رد نداء لتتبع التقدم وتحديد الاختناقات مبكرًا.
توازي مقارنة الأدلة الفرعية يمكن أن يقلل زمن التنفيذ حتى 70 % على خادم بـ 8 نوى، بينما تسمح ردود النداء بعرض شريط تقدم بسيط في وحدة التحكم للوظائف الطويلة.
تحسين الأداء للمقارنات على نطاق واسع
عند التعامل مع أدلة تحتوي على آلاف الملفات، يصبح الأداء أمرًا حاسمًا. إليك كيفية التحسين:
ممارسات إدارة الذاكرة
تتيح لك فئة ComparisonOptions ضبط سلوك عملية المقارنة، مثل تفعيل وضع البث، تحديد حدود حجم الملفات، واختيار تنسيقات الإخراج.
- فعّل وضع البث (
ComparisonOptions.setUseStreaming(true)). - حدّد الحد الأقصى لحجم الملف المعالج (
setMaxFileSize(200 * 1024 * 1024)لـ 200 MB). - أغلق كائن
Comparisonصراحةً بعد كل تشغيل.
استراتيجية المعالجة الدفعية
قسّم شجرة دليل ضخمة إلى دفعات منطقية (مثلاً حسب الوحدة أو نطاق التاريخ) وشغّل كل دفعة على حدة. يمنع ذلك JVM من الاحتفاظ بأكثر من دفعة واحدة في الذاكرة.
المعالجة المتوازية للأدلة المستقلة
إذا كان لديك عدة أزواج من الأدلة للمقارنة (مثل بنى ليلية لعدة خدمات مصغرة)، أطلق مثيلات Comparison منفصلة في مجموعة خيوط. كل خيط يعمل على زوجه الخاص، مستفيدًا من جميع نوى المعالج.
حالات الاستخدام الواقعية وتطبيقات الصناعة
مقارنة الأدلة ليست مجرد أداة للمطورين — إنها تُستَخدم عبر الصناعات لعمليات حيوية للأعمال:
تطوير البرمجيات وDevOps
إدارة الإصدارات: قارن مجلدات المرحلة مقابل الإنتاج قبل النشر لاكتشاف انحرافات التكوين. يمكن إرفاق تقرير HTML بطلب سحب للمراجعة من قبل أصحاب المصلحة.
المالية والامتثال
صيانة سجل التدقيق: تستخدم المؤسسات المالية مقارنة الأدلة لتتبع تغييرات المستندات للامتثال التنظيمي، مما يضمن تسجيل كل تعديل وأرشفته.
إدارة البيانات وعمليات ETL
التحقق من سلامة البيانات: بعد ترحيل بيانات ضخمة، شغّل مقارنة مجلدات لضمان وصول كل ملف مصدر إلى بحيرة البيانات الهدف بشكل صحيح.
إدارة المحتوى والنشر
التحكم في الإصدارات للفرق غير التقنية: يمكن لفرق التسويق مقارنة نسختين من مجلد أصول موقع ويب دون الحاجة إلى معرفة Git، والحصول على فرق بصري واضح.
نصائح متقدمة وأفضل الممارسات
بعد العمل مع مقارنة الأدلة في بيئات الإنتاج، إليك بعض الدروس المستفادة:
التسجيل والمراقبة
ادمج SLF4J مع appender ملف دوار لتسجيل وقت البدء، وقت الانتهاء، عدد الملفات المعالجة، وأي استثناءات. يصبح هذا السجل لا غنى عنه عند التحقيق في فشل متقطع.
استعادة الأخطاء والمرونة
غلف استدعاء compare بكتلة إعادة محاولة تلتقط أخطاء I/O العابرة (مثل انقطاعات الشبكة على محركات الأقراص المعلقة) وتعيد تنفيذ المقارنة حتى ثلاث مرات قبل الإنهاء.
إدارة التكوين
اجعل جميع المسارات، تنسيقات الإخراج، وعلامات الأداء في ملف application.yml أو properties. يتيح ذلك لفرق العمليات تعديل الإعدادات دون إعادة تجميع الـ JAR.
التعامل مع المسارات بشكل مستقل عن النظام
استخدم دائمًا java.nio.file.Paths.get(...) و File.separator عند دمج السلاسل. يضمن ذلك عدم حدوث أخطاء عند الانتقال من Windows (\) إلى Linux (/).
تجاهل الطوابع الزمنية عندما لا تهم
إذا كان التغيير في المحتوى هو المهم فقط، فعّل CompareOptions.setIgnoreMetadata(true). يمنع ذلك الإيجابيات الكاذبة الناجمة عن تحديثات الطوابع الزمنية للملفات المنقولة.
استكشاف مشكلات النشر الشائعة
يعمل في التطوير، يفشل في الإنتاج
الإجابة المباشرة: تحقق من اختلاف حساسية الأحرف (Windows vs Linux)، تأكد من صلاحيات نظام الملفات، واستبدل الفواصل الصلبة بـ File.separator.
غالبًا ما تعمل الخوادم الإنتاجية على Linux، حيث myFile.txt و MyFile.txt يُعتَبران ملفين مختلفين. استخدم API Path لتطبيع الحالة وتجنب التطابق الخاطئ.
نتائج غير متسقة
الإجابة المباشرة: تأكد من عدم تعديل أي عملية خارجية للملفات أثناء تشغيل المقارنة، واضبط CompareOptions لتجاهل الطوابع الزمنية إذا كانت تُسبب اختلافات زائفة.
تشغيل المقارنة على لقطة قراءة‑فقط (مثل لقطة حجم مُركبة) يضمن نتائج حتمية.
الأسئلة المتكررة
س: كيف أتعامل مع أدلة تحتوي على ملايين الملفات؟
ج: اجمع بين المعالجة الدفعية، زد حجم heap للـ JVM (-Xmx8g أو أعلى)، فعّل وضع البث، وشغّل مقارنات الأدلة الفرعية بالتوازي. توفر أقسام استراتيجية المعالجة الدفعية والمعالجة المتوازية أنماطًا جاهزة للاستخدام.
س: هل يمكن مقارنة أدلة موجودة على خوادم مختلفة؟
ج: نعم، لكن زمن الشبكة يهيمن على الأداء. للحصول على أفضل أداء، انسخ الدليل البعيد محليًا أولًا أو ركب المشاركة البعيدة بعرض نطاق I/O كافٍ قبل استدعاء المقارنة.
س: ما هي صيغ الملفات التي يدعمها GroupDocs.Comparison؟
ج: يدعم GroupDocs.Comparison أكثر من 70 صيغة، بما فيها DOC/DOCX، PDF، PPT/PPTX، XLS/XLSX، TXT، HTML، XML، CSV، وأنواع الصور الشائعة (PNG, JPEG, BMP). راجع الوثائق الرسمية لأحدث القائمة.
س: كيف يمكن دمج هذه المقارنة في خط أنابيب CI/CD؟
ج: احزم منطق المقارنة في JAR قابل للتنفيذ أو مكوّن Maven، ثم استدعِه كخطوة بناء في Jenkins أو GitHub Actions أو Azure Pipelines أو GitLab CI. صدّر تقرير HTML كأصل بناء للمراجعة اللاحقة.
س: هل يمكن تخصيص مظهر تقرير HTML؟
ج: القالب 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);