diff --git a/utilities/anttasks/build.xml b/utilities/anttasks/build.xml
new file mode 100644
index 000000000..ab1cf855a
--- /dev/null
+++ b/utilities/anttasks/build.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/utilities/anttasks/classes/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection$DirectoryFilter.class b/utilities/anttasks/classes/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection$DirectoryFilter.class
new file mode 100644
index 000000000..610bbcfa7
Binary files /dev/null and b/utilities/anttasks/classes/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection$DirectoryFilter.class differ
diff --git a/utilities/anttasks/classes/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection$NonDirectoryFilter.class b/utilities/anttasks/classes/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection$NonDirectoryFilter.class
new file mode 100644
index 000000000..b9537fcc5
Binary files /dev/null and b/utilities/anttasks/classes/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection$NonDirectoryFilter.class differ
diff --git a/utilities/anttasks/classes/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection.class b/utilities/anttasks/classes/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection.class
new file mode 100644
index 000000000..25048a683
Binary files /dev/null and b/utilities/anttasks/classes/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection.class differ
diff --git a/utilities/anttasks/lib/ant.jar b/utilities/anttasks/lib/ant.jar
new file mode 100644
index 000000000..704717779
Binary files /dev/null and b/utilities/anttasks/lib/ant.jar differ
diff --git a/utilities/anttasks/src/edu/cornell/mannlib/vitro/utilities/anttasks/AbstractDirResourceCollection.java b/utilities/anttasks/src/edu/cornell/mannlib/vitro/utilities/anttasks/AbstractDirResourceCollection.java
new file mode 100644
index 000000000..b461fba32
--- /dev/null
+++ b/utilities/anttasks/src/edu/cornell/mannlib/vitro/utilities/anttasks/AbstractDirResourceCollection.java
@@ -0,0 +1,126 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.utilities.anttasks;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileResource;
+
+/**
+ * Include all files that are in the primary directory, but do not have matching
+ * files in the blocking directory.
+ */
+public abstract class AbstractDirResourceCollection extends DataType implements
+ ResourceCollection {
+ protected List files = null;
+ protected File primaryDir;
+
+ @Override
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+ /**
+ * Insure that the list has been filled and return an iterator to the list.
+ */
+ @Override
+ public Iterator extends Resource> iterator() {
+ fillFilesList();
+ return files.iterator();
+ }
+
+ /**
+ * Insure that the list has been filled and return the size of the list.
+ */
+ @Override
+ public int size() {
+ fillFilesList();
+ return files.size();
+ }
+
+ public void setPrimary(File primaryDir) {
+ this.primaryDir = primaryDir;
+ }
+
+ protected abstract void fillFilesList();
+
+ /**
+ * The directory path must be provided, and unless the optional flag is set,
+ * must point to an existing, readable directory.
+ */
+ protected void confirmValidDirectory(File dir, String label,
+ boolean optional) {
+ if (dir == null) {
+ throw new BuildException(label + " directory not specified.");
+ }
+ if (!dir.exists()) {
+ if (optional) {
+ return;
+ } else {
+ throw new BuildException(label + " directory '" + dir.getPath()
+ + "' does not exist.");
+ }
+ }
+ if (!dir.isDirectory()) {
+ throw new BuildException(label + " directory '" + dir.getPath()
+ + "' is not a directory.");
+ }
+ if (!dir.canRead()) {
+ throw new BuildException(label + " directory '" + dir.getPath()
+ + "' is not readable.");
+ }
+ }
+
+ protected void includeAllFiles(File primary) {
+ for (File file : primary.listFiles(new NonDirectoryFilter())) {
+ files.add(buildResource(file));
+ }
+ for (File primarySubDir : primary.listFiles(new DirectoryFilter())) {
+ includeAllFiles(primarySubDir);
+ }
+ }
+
+ /**
+ * All file resources are based on the original primary directory.
+ */
+ protected FileResource buildResource(File file) {
+ String primaryBasePath = primaryDir.getAbsolutePath();
+ String filePath = file.getAbsolutePath();
+ if (!filePath.startsWith(primaryBasePath)) {
+ throw new IllegalStateException("File is not a descendant "
+ + "of the primary directory: file='" + file
+ + "', primary='" + primaryDir + "'");
+ }
+
+ String pathPart = filePath.substring(primaryBasePath.length());
+ if (pathPart.startsWith(File.separator)) {
+ pathPart = pathPart.substring(1);
+ }
+
+ // System.out.println("Resource: b='" + primaryDir + "', name='" +
+ // pathPart + "'");
+ return new FileResource(primaryDir, pathPart);
+ }
+
+ public class DirectoryFilter implements FileFilter {
+ @Override
+ public boolean accept(File file) {
+ return file.isDirectory();
+ }
+ }
+
+ public class NonDirectoryFilter implements FileFilter {
+ @Override
+ public boolean accept(File file) {
+ return !file.isDirectory();
+ }
+ }
+}
diff --git a/utilities/anttasks/src/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection.java b/utilities/anttasks/src/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection.java
new file mode 100644
index 000000000..4c9d15da5
--- /dev/null
+++ b/utilities/anttasks/src/edu/cornell/mannlib/vitro/utilities/anttasks/DirDifferenceResourceCollection.java
@@ -0,0 +1,196 @@
+/* $This file is distributed under the terms of the license in /doc/license.txt$ */
+
+package edu.cornell.mannlib.vitro.utilities.anttasks;
+
+import java.io.File;
+import java.io.FileFilter;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+
+import org.apache.tools.ant.BuildException;
+import org.apache.tools.ant.types.DataType;
+import org.apache.tools.ant.types.Resource;
+import org.apache.tools.ant.types.ResourceCollection;
+import org.apache.tools.ant.types.resources.FileResource;
+
+/**
+ * Include all files that are in the primary directory, but do not have matching
+ * files in the blocking directory.
+ */
+public class DirDifferenceResourceCollection extends DataType implements
+ ResourceCollection {
+ private List files = null;
+ private File primaryDir;
+ private File blockingDir;
+ private boolean blockingOptional;
+
+ @Override
+ public boolean isFilesystemOnly() {
+ return true;
+ }
+
+ /**
+ * Insure that the list has been filled and return an iterator to the list.
+ */
+ @Override
+ public Iterator extends Resource> iterator() {
+ fillFilesList();
+ return files.iterator();
+ }
+
+ /**
+ * Insure that the list has been filled and return the size of the list.
+ */
+ @Override
+ public int size() {
+ fillFilesList();
+ return files.size();
+ }
+
+ public void setPrimary(File primaryDir) {
+ this.primaryDir = primaryDir;
+ }
+
+ public void setBlocking(File blockingDir) {
+ this.blockingDir = blockingDir;
+ }
+
+ public void setBlockingOptional(boolean blockingOptional) {
+ this.blockingOptional = blockingOptional;
+ }
+
+ /**
+ * If the list hasn't already been filled, fill it with all files in the
+ * primary directory that are not blocked by files with the same path under
+ * the blocking directory.
+ */
+ private void fillFilesList() {
+ if (files != null) {
+ return;
+ }
+
+ confirmValidDirectory(primaryDir, "Primary", false);
+ confirmValidDirectory(blockingDir, "Blocking", blockingOptional);
+
+ files = new ArrayList();
+ includeUnblockedFiles(primaryDir, blockingDir);
+
+ }
+
+ /**
+ * The primary and blocking directory paths must be provided, and must point
+ * to existing, readable directories.
+ */
+ private void confirmValidDirectory(File dir, String label, boolean optional) {
+ if (dir == null) {
+ throw new BuildException(label + " directory not specified.");
+ }
+ if (!dir.exists()) {
+ if (optional) {
+ return;
+ } else {
+ throw new BuildException(label + " directory '" + dir.getPath()
+ + "' does not exist.");
+ }
+ }
+ if (!dir.isDirectory()) {
+ throw new BuildException(label + " directory '" + dir.getPath()
+ + "' is not a directory.");
+ }
+ if (!dir.canRead()) {
+ throw new BuildException(label + " directory '" + dir.getPath()
+ + "' is not readable.");
+ }
+ }
+
+ /**
+ * Include any file from the primary directory that does not match a file in
+ * the blocking directory.
+ *
+ * Include all files from any subdirectory that has no matching subdirectory
+ * in the blocking directory.
+ *
+ * Include all unblocked files from any subdirectory that has a matching
+ * subdirectory in the blocking directory.
+ *
+ * NOTE: if a file is matched by a subdirectory, the file is blocked. If a
+ * subdirectory is matched by a file, an exception is thrown.
+ */
+ private void includeUnblockedFiles(File primary, File blocking) {
+ for (File file : primary.listFiles(new NonDirectoryFilter())) {
+ if (!isBlocked(file, blocking)) {
+ files.add(buildResource(file));
+ }
+ }
+ for (File primarySubDir : primary.listFiles(new DirectoryFilter())) {
+ File blockingSubDir = findMatchingDir(primarySubDir, blocking);
+ if (blockingSubDir == null) {
+ includeAllFiles(primarySubDir);
+ } else {
+ includeUnblockedFiles(primarySubDir, blockingSubDir);
+ }
+ }
+ }
+
+ private boolean isBlocked(File file, File blocking) {
+ return new File(blocking, file.getName()).exists();
+ }
+
+ private File findMatchingDir(File primary, File blockingParent) {
+ File dir = new File(blockingParent, primary.getName());
+ if (!dir.exists()) {
+ return null;
+ }
+ if (!dir.isDirectory()) {
+ throw new BuildException("Directory '" + primary
+ + "' is blocked by a non-directory '" + dir.getPath() + "'");
+ }
+ return dir;
+ }
+
+ private void includeAllFiles(File primary) {
+ for (File file : primary.listFiles(new NonDirectoryFilter())) {
+ files.add(buildResource(file));
+ }
+ for (File primarySubDir : primary.listFiles(new DirectoryFilter())) {
+ includeAllFiles(primarySubDir);
+ }
+ }
+
+ /**
+ * All file resources are based on the original primary directory.
+ */
+ private FileResource buildResource(File file) {
+ String primaryBasePath = primaryDir.getAbsolutePath();
+ String filePath = file.getAbsolutePath();
+ if (!filePath.startsWith(primaryBasePath)) {
+ throw new IllegalStateException("File is not a descendant "
+ + "of the primary directory: file='" + file
+ + "', primary='" + primaryDir + "'");
+ }
+
+ String pathPart = filePath.substring(primaryBasePath.length());
+ if (pathPart.startsWith(File.separator)) {
+ pathPart = pathPart.substring(1);
+ }
+
+ // System.out.println("Resource: b='" + primaryDir + "', name='" +
+ // pathPart + "'");
+ return new FileResource(primaryDir, pathPart);
+ }
+
+ public class DirectoryFilter implements FileFilter {
+ @Override
+ public boolean accept(File file) {
+ return file.isDirectory();
+ }
+ }
+
+ public class NonDirectoryFilter implements FileFilter {
+ @Override
+ public boolean accept(File file) {
+ return !file.isDirectory();
+ }
+ }
+}
diff --git a/webapp/build.xml b/webapp/build.xml
index 83db044bf..938453455 100644
--- a/webapp/build.xml
+++ b/webapp/build.xml
@@ -13,29 +13,30 @@
+
+
+
+
-
+
-
-
-
-
-
-
-
+
@@ -46,9 +47,7 @@
-
-
-
+
@@ -77,12 +76,6 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
target: properties
- - - - - - - - - - - - - - - - - -->
-
-
-
@@ -118,7 +111,7 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
-
+
-
+
-
+
@@ -160,10 +153,10 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
-
-
+
@@ -177,19 +170,11 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
-
-
-
-
-
-
-
-
-
+
-
+
-
-
-
-
+
@@ -258,7 +243,7 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
${revisionInfo.timestamp}
-
-
+
-
+
-
+
-
-
-
+
+
+
@@ -303,7 +288,7 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
-
+
@@ -360,12 +345,12 @@ deploy - Deploy the application directly into the Tomcat webapps directory.
================================= -->
+ location="${appbase.dir}/config/licenser/licenser.properties" />
-
+
diff --git a/webapp/product-build.xml b/webapp/product-build.xml
index 3a3970123..285cd762f 100644
--- a/webapp/product-build.xml
+++ b/webapp/product-build.xml
@@ -29,56 +29,60 @@
Everything else hangs from the build directory.
-->
-
-
-
-
-
-
-
-
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
+
-
-
-
-
-
+
+
+
+
+
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -126,146 +245,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+