Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add package-to-scan feature #786

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3906,6 +3906,15 @@ public class PersistenceUnitProperties {
*/
public static final String NAMING_INTO_INDEXED = "eclipselink.jpa.naming_into_indexed";

/**
* The "<code>eclipselink.packages-to-scan</code>" property
* allows you to specify a comma separated list of packages you allow to be scanned.
* This could help reducing startup time.
* <p>
* By default, no restriction will be applied and classes from every packages will be considered.
*/
public static final String PACKAGES_TO_SCAN = "eclipselink.packages-to-scan";

/**
* INTERNAL: The following properties will not be displayed through logging
* but instead have an alternate value shown in the log.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import junit.framework.Test;
import junit.framework.TestSuite;

import org.eclipse.persistence.config.PersistenceUnitProperties;
import org.eclipse.persistence.config.SystemProperties;
import org.eclipse.persistence.exceptions.ValidationException;
import org.eclipse.persistence.internal.jpa.deployment.ArchiveFactoryImpl;
Expand All @@ -32,8 +33,7 @@
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.HashMap;
import java.util.Map;
import java.util.*;

import jakarta.persistence.EntityManagerFactory;

Expand Down Expand Up @@ -222,4 +222,35 @@ public void testGetArchiveFactoryOverride() {
public static class AF1 extends ArchiveFactoryImpl {}
public static class AF2 extends ArchiveFactoryImpl {}

public void testIsEligibleToScan() {
Assert.assertTrue(PersistenceUnitProcessor.isEligibleToScan(Collections.emptyList(), "foo/bar/MyClass.class"));

List<String> pathsToScan = new ArrayList<>();
pathsToScan.add("foo/bar/");
pathsToScan.add("com/test/");

Assert.assertTrue(PersistenceUnitProcessor.isEligibleToScan(pathsToScan, "foo/bar/MyClass.class"));
Assert.assertTrue(PersistenceUnitProcessor.isEligibleToScan(pathsToScan, "foo/bar/inner/MyClass.class"));
Assert.assertFalse(PersistenceUnitProcessor.isEligibleToScan(pathsToScan, "foo/MyClass.class"));
Assert.assertFalse(PersistenceUnitProcessor.isEligibleToScan(pathsToScan, "foo/barMyClass.class"));

Assert.assertTrue(PersistenceUnitProcessor.isEligibleToScan(pathsToScan, "com/test/MyClass.class"));

Assert.assertFalse(PersistenceUnitProcessor.isEligibleToScan(pathsToScan, "org/apache/SomeClass.class"));
}

public void testPathsToScan() {
Assert.assertTrue(PersistenceUnitProcessor.pathsToScan(null).isEmpty());
Assert.assertTrue(PersistenceUnitProcessor.pathsToScan("").isEmpty());
Assert.assertTrue(PersistenceUnitProcessor.pathsToScan(" ").isEmpty());

List<String> pathsToScan = PersistenceUnitProcessor.pathsToScan("foo.bar,com.test");
Assert.assertEquals(2, pathsToScan.size());
Assert.assertTrue(pathsToScan.contains("foo/bar/"));
Assert.assertTrue(pathsToScan.contains("com/test/"));

List<String> pathsToScan2 = PersistenceUnitProcessor.pathsToScan("foo.bar, ");
Assert.assertEquals(1, pathsToScan2.size());
Assert.assertTrue(pathsToScan2.contains("foo/bar/"));
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
/*
* Copyright (c) 1998, 2020 Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 1998, 2018 IBM Corporation. All rights reserved.
* Copyright (c) 1998, 2020 IBM Corporation. All rights reserved.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
Expand Down Expand Up @@ -42,6 +42,7 @@
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
Expand Down Expand Up @@ -479,22 +480,22 @@ public static ArchiveFactory getArchiveFactory(ClassLoader loader, Map propertie
}

public static Set<String> getClassNamesFromURL(URL url, ClassLoader loader, Map properties) {
Set<String> classNames = new HashSet<String>();
Set<String> classNames = new HashSet<>();
Archive archive = null;
try {
archive = PersistenceUnitProcessor.getArchiveFactory(loader, properties).createArchive(url, properties);

List<String> pathsToScan = pathsToScan(properties != null ? (String) properties.get(PersistenceUnitProperties.PACKAGES_TO_SCAN) : null);

if (archive != null) {
for (Iterator<String> entries = archive.getEntries(); entries.hasNext();) {
String entry = entries.next();
if (entry.endsWith(".class")){ // NOI18N
if (entry.endsWith(".class") && isEligibleToScan(pathsToScan, entry)) { // NOI18N
classNames.add(buildClassNameFromEntryString(entry));
}
}
}
} catch (URISyntaxException e) {
throw new RuntimeException("url = [" + url + "]", e); // NOI18N
} catch (IOException e) {
} catch (IOException | URISyntaxException e) {
throw new RuntimeException("url = [" + url + "]", e); // NOI18N
} finally {
if (archive != null) {
Expand All @@ -504,6 +505,55 @@ public static Set<String> getClassNamesFromURL(URL url, ClassLoader loader, Map
return classNames;
}

/**
* Returns true if a class entry is eligible to scan.
* A class entry is eligible if:
* - pathsToScan is empty (not configured)
* - it resides in one of {@code pathsToScan} list
*
* @param pathsToScan list of paths where the class entry must be. Can be empty
* @param classEntry path of the class we want to check in the form : foo/bar/MyClass.class
* @return true is the class entry is eligible
*/
public static boolean isEligibleToScan(List<String> pathsToScan, String classEntry) {
if (pathsToScan.isEmpty()) {
return true;
}

for (String pathToScan : pathsToScan) {
if (classEntry.startsWith(pathToScan)) {
return true;
}
}

return false;
}

/**
* Takes a comma separated list of packages to scan and returns a list of
* paths.
*
* @param packagesToScanProperty list of packages to scan (comma-separated)
* @return list of paths to scans
*/
public static List<String> pathsToScan(String packagesToScanProperty) {
if (packagesToScanProperty == null || packagesToScanProperty.isEmpty()) {
return Collections.emptyList();
}

List<String> packagesToScan = new ArrayList<>();

for (String packageToScan : packagesToScanProperty.split(",")) {
String trimmedPackageToScan = packageToScan.trim();

if (!trimmedPackageToScan.isEmpty()) {
packagesToScan.add(trimmedPackageToScan.replace('.', '/') + "/");
}
}

return packagesToScan;
}

/**
* Return if a given class is annotated with @Embeddable.
*/
Expand Down