Skip to content

Commit

Permalink
Detect split packages at build time
Browse files Browse the repository at this point in the history
Split packages are a well-known anti-pattern for OSGi and a blocker for
Eclipse Virgo (which prevents split packages being accessed via its
Import-Library construct).

Split packages are also unhelpful with a traditional linear classpath
as a split package name does not uniquely identify the Spring framework
JAR from which it came, thus complicating problem diagnosis and
maintenance.

Juergen Hoeller supports this position in the following comment in
SPR-9990:

>FWIW, I generally find split packages a bad practice, even without
>OSGi in the mix. For the Spring Framework codebase, I consider a
>split-package arrangement a design accident that we want to detect
>in any case - and that we're willing to fix if it happened.
>
>I'm actually equally concerned about the source perspective: After
>all, we want a package to be comprehensible from a single glance
>at the project, not requiring the developer to jump into several
>source modules to understand the overall layout of a package.

Split packages have crept into Spring framework twice in recent months
- see SPR-9811 and SPR-9988. Currently, they are only detected once
the Spring framework has been converted to OSGi bundles and these
bundles have been tested with Eclipse Virgo.

This commit adds a build-time check for split packages to the Spring
framework build.

Issue: SPR-9990

Conflicts:

	build.gradle
  • Loading branch information
Glyn Normington committed Jan 7, 2013
1 parent 895feda commit 00a86c3
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 0 deletions.
6 changes: 6 additions & 0 deletions build.gradle
Expand Up @@ -8,6 +8,11 @@ buildscript {
}
}

configure(rootProject) {
def splitFound = new org.springframework.build.gradle.SplitPackageDetector('.', logger).diagnoseSplitPackages();
assert !splitFound // see error log messages for details of split packages
}

configure(allprojects) { project ->
group = "org.springframework"
version = qualifyVersionIfNecessary(version)
Expand Down Expand Up @@ -954,6 +959,7 @@ configure(rootProject) {
"set GRADLE_OPTS=$gradleBatOpts %GRADLE_OPTS%\nset DEFAULT_JVM_OPTS=")
}
}

}

/*
Expand Down
@@ -0,0 +1,85 @@
/*
* Copyright 2013 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.springframework.build.gradle

class SplitPackageDetector {

private static final String HIDDEN_DIRECTORY_PREFIX = "."

private static final String JAVA_FILE_SUFFIX = ".java"

private static final String SRC_MAIN_JAVA = "src" + File.separator + "main" + File.separator + "java"

private final Map<File, Set<String>> pkgMap = [:]

private final logger

SplitPackageDetector(baseDir, logger) {
this.logger = logger
dirList(baseDir).each { File dir ->
def packages = getPackagesInDirectory(dir)
if (!packages.isEmpty()) {
pkgMap.put(dir, packages)
}
}
}

private File[] dirList(String dir) {
dirList(new File(dir))
}

private File[] dirList(File dir) {
dir.listFiles({ file -> file.isDirectory() && !file.getName().startsWith(HIDDEN_DIRECTORY_PREFIX) } as FileFilter)
}

private Set<String> getPackagesInDirectory(File dir) {
def pkgs = new HashSet<String>()
addPackagesInDirectory(pkgs, new File(dir, SRC_MAIN_JAVA), "")
return pkgs;
}

boolean diagnoseSplitPackages() {
def splitFound = false;
def dirs = pkgMap.keySet().toArray()
def numDirs = dirs.length
for (int i = 0; i < numDirs - 1; i++) {
for (int j = i + 1; j < numDirs - 1; j++) {
def di = dirs[i]
def pi = new HashSet(pkgMap.get(di))
def dj = dirs[j]
def pj = pkgMap.get(dj)
pi.retainAll(pj)
if (!pi.isEmpty()) {
logger.error("Packages $pi are split between directories '$di' and '$dj'")
splitFound = true
}
}
}
return splitFound
}

private void addPackagesInDirectory(HashSet<String> packages, File dir, String pkg) {
def scanDir = new File(dir, pkg)
def File[] javaFiles = scanDir.listFiles({ file -> !file.isDirectory() && file.getName().endsWith(JAVA_FILE_SUFFIX) } as FileFilter)
if (javaFiles != null && javaFiles.length != 0) {
packages.add(pkg)
}
dirList(scanDir).each { File subDir ->
addPackagesInDirectory(packages, dir, pkg + File.separator + subDir.getName())
}
}
}

0 comments on commit 00a86c3

Please sign in to comment.