Skip to content

Commit

Permalink
Merge branch 'release/0.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
zagyi committed Sep 30, 2013
2 parents 4eeea9b + aa8491d commit 22737a7
Show file tree
Hide file tree
Showing 85 changed files with 3,183 additions and 1,235 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
_*
/unversioned/
/.gradle/
/*/.gradle/
/build/
/*/build/
/out/
*.iml
*.ipr
*.iws
**/*.iml
**/*.ipr
**/*.iws
Expand Down
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
language: java
jdk: oraclejdk7

script:
- TERM=dumb ./gradlew check
41 changes: 9 additions & 32 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,39 +1,16 @@
## ADSync4J - Active Directory Synchronization for Java

ADSync4J is a lightweight Java library that can greatly simplify the task of synchronizing contents from Active Directory to your database, and keeping that content in sync with incremental updates.
[![Build Status](https://travis-ci.org/zagyi/adsync4j.png?branch=develop)](https://travis-ci.org/zagyi/adsync4j)

### 1. Quick intro
#### What can this library do for you?
#### What is ADSync4J?
ADSync4J is a lightweight Java library that greatly simplifies the task of creating and maintaining a replica of objects living in Active Directory.

The core functionality is basically made up by two operations:
The library implements a protocol that enables users to download an arbitrary set of data from Active Directory into a local storage *and* to keep it up-to-date with the source by performing periodic incremental synchronization. In short, ADSync4J helps **replicating data from Active Directory**.

1. Retrieve entries from Active Directory.
2. Retrieve changes made to these entries in incremental updates.

Both of these operations use the LDAP protocol to talk to the Active Directory server.

#### What won't id do for you?

1. The synchronization is one-way only, it does not issue any updates to Active Directory.
2. It cannot read data from Active Directory that is not available through LDAP. In particular, it won't synchronize **user passwords**.
3. It won't persist the data retrieved. Storing the content is out of scope for this library.

#### How does it work?

You need to give a few parameters to specify **where** you want to synchronize **from**:

1. The Active Directory server's address.
2. User credentials to authenticate with.
3. The distinguished name of the directory's root entry. (e.g. `DC=example,DC=com`)

And a few others to specify **what** you want to synchronize:

1. Distinguished name of the base entry for synchronization (e.g. `CN=Users,DC=example,DC=com`).
2. LDAP filter defining the entries you are interested in (the scope of synchronization).
3. List of attributes you want to retrieve.

After having all this information, the first thing to do is a full synchronization. Once the initial set of entries are stored in database, you can poll for changes with a frequency suitable for your application. Incremental synchronization will report any changes that has been made in Active Directory to the entries within the defined scope (including updates, inserts and deletes).

## 2. Detailed user guide
#### Why would you want to replicate data from Active Directory?
There can be many use-cases. For example, a typical problem you might face when interacting with Active Directory through LDAP is the poor querying interface that LDAP offers. This becomes an issue if you frequently need to perform queries that go beyond what is feasible using LDAP filters. With ADSync4J it's easy to replicate directory objects you are interested in into a relational or graph database, and leverage the more expressive query language they offer.

ADSync4J can also be used to solve integration problems. If you need to deploy your application into an environment where user accounts come from a diverse set of sources including Active Directory, then ADSync4J will be of great help in keeping the central identity store of the application in sync with Active Directory.

#### How to use this library?
Please refer to the detailed documentation on the [project wiki pages](https://github.com/zagyi/adsync4j/wiki).
184 changes: 70 additions & 114 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -11,150 +11,106 @@
* Contributors:
* Balazs Zagyvai
***************************************************************************** */
import org.adsync4j.gradle.IdeaUtils
import org.adsync4j.gradle.Libs


import org.adsync4j.gradle.Projects
import org.gradle.plugins.ide.idea.model.IdeaModel
import org.gradle.plugins.ide.idea.model.Module
import org.gradle.plugins.ide.idea.model.ModuleLibrary
import org.gradle.plugins.ide.idea.model.Path

import static org.adsync4j.gradle.GradleUtils.addDependencies
import static org.adsync4j.gradle.GradleIdeaIntegrationHelper.addGradleAsDependency
import static org.adsync4j.gradle.GradleUtils.javaPlugin
import static org.adsync4j.gradle.GradleUtils.runUserScript
import static org.adsync4j.gradle.IdeaUtils.sortDependenciesOfIntelliJModules
import static org.adsync4j.gradle.Projects.*

Project rootPrj = project

Libs.init(rootPrj)
Projects.init(rootPrj)

rootPrj.allprojects.each { prj ->
def repo = prj.repositories
// run an optional user script to allow developers to add build logic that is
// specific to their environment and that is executed before the main build script
runUserScript(rootPrj, 'init')

prj.apply plugin: 'groovy'
prj.apply plugin: 'idea'
prj.apply from: "$rootPrj.rootDir/gradle/providedConfiguration.gradle"
rootPrj.apply from: "$rootPrj.rootDir/gradle/pom.gradle"

prj.group = 'org.adsync4j'
prj.version = '0.1'
rootPrj.with {
allprojects.each { prj ->
prj.with {
apply plugin: 'groovy'
apply plugin: 'idea'
apply from: "$rootPrj.rootDir/gradle/providedConfiguration.gradle"

prj.configurations.all { Configuration configuration ->
configuration.exclude group: 'commons-logging'
}
group = 'org.adsync4j'
version = "0.2"

prj.repositories {
repo.mavenLocal()
repo.mavenCentral()
}
javaPlugin(prj).sourceCompatibility = 1.7
javaPlugin(prj).targetCompatibility = 1.7

addDependencies(prj, [provided: [Libs.gradle]])

if (!'buildSrc'.equals(prj.name)) {
addDependencies(prj, [provided: [Projects.buildSrc]])
}

sortIdeaDependencies(prj)
attachGradleSourcesInIdea(prj)
propagateSystemPropertiesToTestJVMs(prj)
}
if (prj != buildSrc) {
prj.dependencies.add('provided', buildSrc)
}

def sortIdeaDependencies(Project prj) {
IdeaModel idea = prj.idea
idea.module.iml.whenMerged { Module module ->
def sortedDependencies = new TreeSet(IdeaUtils.dependenciesOrdering)
sortedDependencies.addAll(module.dependencies)
module.dependencies = sortedDependencies
}
}
configurations.all {
it.exclude group: 'commons-logging'
}

def attachGradleSourcesInIdea(Project prj) {
IdeaModel idea = prj.idea
def gradleHome = System.getenv('GRADLE_HOME')
if (checkIfGradleSourcesAreAvailable(prj, gradleHome)) {
def gradleSrcPath = new Path("file://${gradleHome}/src")
Set gradleJarPaths = Libs.gradle.collect { File gradleJar -> gradleJar.getAbsolutePath() } as Set

idea.module.iml.whenMerged { Module module ->
module.dependencies.each { dependency ->
def isExternalDependency = dependency instanceof ModuleLibrary
if (isExternalDependency) {
def pathsOfDependentLib = extractPaths(dependency)
def isAGradleJarDependency = gradleJarPaths.intersect(pathsOfDependentLib)
if (isAGradleJarDependency) {
dependency.sources.add(gradleSrcPath)
}
}
repositories.with {
add(mavenLocal())
add(mavenCentral())
}
}
}
}

def propagateSystemPropertiesToTestJVMs(Project prj) {
prj.tasks.withType(Test) { Test test ->
// propagate system properties from Gradle JVM to the forked test JVM
test.systemProperties = System.getProperties()
if (System.getProperty('attachDebugger') != null) {
test.jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005'
}
sortDependenciesOfIntelliJModules(prj)
addGradleAsDependency(prj)
customizeTestTasks(prj)
}
}

def checkIfGradleSourcesAreAvailable(Project prj, gradleHome) {
def hasSources = false
if (gradleHome) {
hasSources = new File("${gradleHome}/src").exists()
if (!hasSources) {
prj.logger.warn(
"Cannot attach Gradle sources to prject $prj.name. Sources not " +
"found under \$GRADLE_HOME/src ($gradleHome/src).")
}
} else {
prj.logger.warn(
"Cannot attach Gradle sources to prject $prj.name, because the " +
"GRADLE_HOME environment variable is unset.")
}
return hasSources
}
// configure maven artifacts
[core, unboundidClient].each { mavenPrj ->
mavenPrj.with {
apply plugin: 'maven'

Set extractPaths(ModuleLibrary ml) {
if (!ml.classes.isEmpty()) {
ml.classes.collect { Path path ->
def match = (path.url =~ /.+:\/\/(.*\.jar).*/)
if (match) {
match[0][1]
}
} as Set
} else {
[] as Set
}
}
// by default the install task depends on the jar task, which won't trigger test execution
tasks['install'].dependsOn(tasks['build'])

// maven plugin is selectively applied in sub-projects, so we need to evaluate them before doing maven specific settings
rootPrj.evaluationDependsOnChildren()
def createSourcesJarTask =
tasks.create(name: 'createSourcesJar', type: Jar) { Jar jarTask ->
jarTask.classifier = 'sources'
def mainSourceSet = javaPlugin(mavenPrj).sourceSets['main']
jarTask.from(mainSourceSet.allSource)
}

rootPrj.subprojects.each { subPrj ->
def java = subPrj.convention.getPlugin(JavaPluginConvention)
java.sourceCompatibility = 1.7
java.targetCompatibility = 1.7
def createJavadocJarTask =
tasks.create(name: 'createJavadocJar', type: Jar) { Jar jarTask ->
jarTask.dependsOn(tasks['javadoc'])
jarTask.classifier = 'javadoc'
jarTask.from('build/docs/javadoc')
}

if (subPrj.plugins.hasPlugin('maven')) {
// by default the install task depends on the jar task, which won't trigger test execution
subPrj.tasks['install'].dependsOn = [subPrj.tasks['build']]
artifacts.add('archives', createSourcesJarTask)
artifacts.add('archives', createJavadocJarTask)
}
}

// create install task in root project that depends on the install tasks of sub-projects where maven plugin is applied
rootPrj.tasks.create('install') { Task install ->
rootPrj.subprojects.each { subPrj ->
if (subPrj.plugins.hasPlugin('maven')) {
install.dependsOn += subPrj.tasks['install']
def customizeTestTasks(Project prj) {
prj.tasks.withType(Test) { Test test ->
// optionally enable log messages emitted by tests to appear in Gradle's output
if (System.getProperty('testLogs') != null) {
test.testLogging.showStandardStreams = true
}

// optionally start the test jvm with a debugger attached
if (System.getProperty('debug') != null) {
test.jvmArgs '-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005'
}

// propagate system properties from Gradle JVM to the forked test JVM
test.systemProperties = System.getProperties() as Map<String, ?>
}
}

rootPrj.tasks.create(name: 'wrapper', type: Wrapper) { Wrapper wrapper ->
wrapper.gradleVersion = '1.6'
wrapper.gradleVersion = '1.7'
}

def userGradleScript = rootPrj.file("gradle/userScripts/${System.getProperty("user.name")}.gradle")
if (userGradleScript.exists()) {
rootPrj.apply from: userGradleScript
}
// run an optional user script to allow developers to add build logic that is
// specific to their environment and that is executed after the main build script
runUserScript(rootPrj)
41 changes: 25 additions & 16 deletions buildSrc/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,38 @@
***************************************************************************** */

/**
* WHY IS THERE A build.gradle, AND A buildSrc.gradle IN THIS PROJECT?
* WHY ARE THERE TWO BUILD SCRIPTS IN THIS PROJECT? (build.gradle and buildSrc.gradle)
*
* The buildSrc project is built by Gradle before any other project, and its output is automatically made available for build
* scripts in other projects. When executing the build of the buildSrc project, Gradle looks for a build script with the
* default file name ('build.gradle') . That's why we have 'build.gradle'.
* The buildSrc project has a distinguished role in Gradle. It is meant to hold code that is commonly used in build scripts
* of multiple subprojects. Instead of copying common logic and utility functions into each subproject that uses it,
* you can simply put common code in buildSrc, and Gradle will make sure it's compiled first and made available for build scripts
* in other projects. When building the buildSrc project, Gradle looks for a build script with the default file name
* ('build.gradle'). So that's why we have 'build.gradle', which is half of the story.
*
* This works perfectly fine, however the implicit dependency of "normal" projects on the buildSrc project is not known to the
* IDE. That becomes a problem, because we don't get proper IDE support (e.g. code completion) when writing build scripts
* that use code declared in the buildSrc project. In order to fix this we would need to make a normal IDE module out of the
* buildSrc project and declare an explicit dependency on it in every other module(project). Since the default naming
* convention of the projects' build scripts is overridden in settings.gradle, we need another build script with the projects
* name: 'buildSrc.gradle'. This script won't be used by Gradle when it builds the buildSrc project as a first step to make it
* available to other build scripts, but it will be used later, and the buildSrc project will be built again as a normal
* sub-project during the build process. This is kind of a weird workaround, but it doesn't make much harm, and we get the
* desired IDE support at the end, when 'gradle idea' is used to generate the proper IntelliJ IDEA modules.
* This works perfectly fine, however the implicit dependency of "normal" subprojects on the buildSrc project is not known to the
* IDE. That becomes a problem, when we use any code from buildSrc in other build scripts, because the IDE won't recognize where
* that code is supposed to come from, so proper IDE support (e.g. code completion) won't be available.
* In order to fix this, we would need to make a normal IDE module for the buildSrc project and declare an explicit dependency
* on it from every other project. To achieve this we have to elevate buildSrc to the level of an ordinary subproject, but how
* can we do that? Simply enlist buildSrc in the included projects in /settings.gradle, and create a second build script according
* to our own build script naming convention "<project's name>.gradle" (this convention is also established in /settings.gradle).
* So that's why we have 'buildSrc.gradle'.
*
* The result of this weird setup is that the buildSrc project is always built twice:
* 1. First, because (by convention) Gradle builds it to make common build logic available to build scrips in the entire
* project (build.gradle is used here).
* 2. Second, because it's explicitly included in the list of subprojects in settings.gradle (buildSrc.gradle is used here.)
*
* This _is_ a hack, but it's still better than to forgo proper IDE support when using common code from buildSrc.
* To reduce the overhead of the second build, buildSrc.gradle overrides all tasks with a no-op action except for 'idea' and
* 'cleanIdea' -- after all, we only want an IntelliJ IDEA module generated for the project!
*/

Project buildSrc = project
RepositoryHandler repo = buildSrc.repositories

buildSrc.repositories.with {
add(repo.mavenLocal())
add(repo.mavenCentral())
add(mavenLocal())
add(mavenCentral())
}

buildSrc.dependencies.add('compile', 'com.google.guava:guava:14.0.1')
Loading

0 comments on commit 22737a7

Please sign in to comment.