Maven migration (first draft)
This commit is contained in:
parent
5e0329908c
commit
e1ff94ccaf
2866 changed files with 1112 additions and 616 deletions
192
legacy/utilities/buildutils/jarUsageScanner.rb
Normal file
192
legacy/utilities/buildutils/jarUsageScanner.rb
Normal file
|
@ -0,0 +1,192 @@
|
|||
=begin
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
This script will try to show that the JARs in a list are not needed for VIVO
|
||||
(or Vitro).
|
||||
|
||||
Given a list of JARs, it will determine what packages the JAR contains, and will
|
||||
search the source files of the project for references to those packages.
|
||||
|
||||
The list of JARs should be created by running the jarlist target of the build file.
|
||||
That will use JarJar to create a list of JAR file that are not directly referenced
|
||||
by the classes in the build. But what about JARs that are needed by a call using
|
||||
reflection? Like Class.forName("this.that.theOther"). We know this is how the MySQL
|
||||
driver is loaded (so we need mysql-connector-java-5.1.16-bin.jar). Are there any
|
||||
others? This script will try to find them.
|
||||
|
||||
The jarlist target includes a list of JARs that we know are needed, such as the MySQL
|
||||
connector. If this script finds any JARs that are needed, they should be added to that
|
||||
list, and the target run again, in case they depend on other JARs in turn.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
One of the tricks that this script uses is to prune the list of packages to
|
||||
search for. If a JAR contains both "this.that.other" and "this.that", then we
|
||||
only need to search for "this.that", since it will reveal uses of "this.that.other"
|
||||
as well.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
pass the name of the file that contains the JAR names
|
||||
pass the root directory of the combined vitro/vivo distro
|
||||
(appbase)
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Search all of the *.java, *.jsp, *.js, *.tld, *.xml files for mention of these package names
|
||||
For each hit, print the file path, line number, the package name and the JAR name.
|
||||
|
||||
To search files:
|
||||
find -X . -name \*.db -or -name \*pot | xargs grep 'org\.package\.name'
|
||||
grep -H -n -f file_with_package_patterns
|
||||
|
||||
or can we do this all with grep on each string?
|
||||
grep -r --include=*.jsp
|
||||
|
||||
grep -H -n -r --include=*.javaxd --include=*.jsp org\.package\.name .
|
||||
and precede the output with a header that lists the package
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
=end
|
||||
|
||||
# Just say what we're doing.
|
||||
#
|
||||
def print_args
|
||||
puts "Scanning for JAR usage."
|
||||
puts "Base directory is #{@scan_base_directory}"
|
||||
puts "JAR list is in #{@jar_names_file}"
|
||||
puts
|
||||
end
|
||||
|
||||
# Build a Hash of JAR names mapped to (reduced) arrays of package names.
|
||||
#
|
||||
def figure_package_names_from_jars
|
||||
@packages_for_jars = {}
|
||||
File.open(@jar_names_file) do |file|
|
||||
file.each do |line|
|
||||
jar_name = line.strip
|
||||
@packages_for_jars[jar_name] = figure_package_names_for_jar(jar_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Figure out the reduced set of package names for this JAR
|
||||
#
|
||||
def figure_package_names_for_jar(jar_name)
|
||||
jar_path = "#{@scan_base_directory}/lib/#{jar_name}"
|
||||
jar_contents = `jar -tf '#{jar_path}'`
|
||||
packages = analyze_jar_contents(jar_contents)
|
||||
reduced = reduce_package_names(packages)
|
||||
end
|
||||
|
||||
# Ask the JAR what it contains. Keep only the names of populated packages.
|
||||
# Ignore packages that are not at least 2 levels deep.
|
||||
#
|
||||
def analyze_jar_contents(jar_contents)
|
||||
packages = []
|
||||
jar_contents.lines do |line|
|
||||
line.strip!
|
||||
if line.end_with?('/')
|
||||
elsif line.start_with?('META-INF')
|
||||
elsif line.count('/') < 2
|
||||
else
|
||||
package = line[0...line.rindex('/')].tr('/', '.')
|
||||
packages << package
|
||||
end
|
||||
end
|
||||
packages.uniq.sort!
|
||||
end
|
||||
|
||||
# Remove the names of any sub-packages. Searching for the parent package will be sufficient.
|
||||
#
|
||||
def reduce_package_names(packages)
|
||||
reduced = []
|
||||
packages.each do |candidate|
|
||||
redundant = FALSE
|
||||
reduced.each do |result|
|
||||
redundant = TRUE if candidate.start_with?(result)
|
||||
end
|
||||
reduced << candidate unless redundant
|
||||
end
|
||||
reduced
|
||||
end
|
||||
|
||||
# Show what packages we will search for, and for which JAR
|
||||
#
|
||||
def print_package_names_for_jars
|
||||
puts "Packages for each jar"
|
||||
@packages_for_jars.each do |jar_name, package_array|
|
||||
puts " #{jar_name}"
|
||||
package_array.each do |package_name|
|
||||
puts " #{package_name}"
|
||||
end
|
||||
puts
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
def show_packages_in_source_files
|
||||
@packages_for_jars.each do |jar_name, package_array|
|
||||
show_packages_for_jar_in_source_files(jar_name, package_array)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
def show_packages_for_jar_in_source_files(jar_name, package_array)
|
||||
puts "------------------------------- #{jar_name} ------------------------------"
|
||||
package_array.each do |package_name|
|
||||
show_package_in_source_files(package_name)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
def show_package_in_source_files(package_name)
|
||||
puts "#{package_name}"
|
||||
include_parms = build_include_parms(["*.java", "*.jsp", "*.xml", "*.tld", "*.js" ])
|
||||
package_name_pattern = package_name.sub(/\./, "\\.")
|
||||
system "grep -H -n -r #{include_parms} #{package_name_pattern} '#{@scan_base_directory}'"
|
||||
puts
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
def build_include_parms(file_specs)
|
||||
"--include=" + file_specs.join(" --include=")
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# ------------------------------------------------------------------------------------
|
||||
# Standalone calling.
|
||||
#
|
||||
# Do this if this program was called from the command line. That is, if the command
|
||||
# expands to the path of this file.
|
||||
# ------------------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
if File.expand_path($0) == File.expand_path(__FILE__)
|
||||
if ARGV.length != 2
|
||||
raise("usage is: ruby jarUsageScanner.rb <jar_names_file> <scan_base_directory>")
|
||||
end
|
||||
|
||||
@jar_names_file, @scan_base_directory = ARGV
|
||||
|
||||
if !File.file?(@jar_names_file)
|
||||
raise "File does not exist: '#{@jar_names_file}'."
|
||||
end
|
||||
|
||||
if !File.directory?(@scan_base_directory)
|
||||
raise "Directory does not exist: '#{@scan_base_directory}'."
|
||||
end
|
||||
|
||||
print_args
|
||||
|
||||
figure_package_names_from_jars
|
||||
print_package_names_for_jars
|
||||
|
||||
show_packages_in_source_files
|
||||
end
|
BIN
legacy/utilities/buildutils/lib/ant.jar
Normal file
BIN
legacy/utilities/buildutils/lib/ant.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/buildutils/lib/jarjar-1.1.jar
Normal file
BIN
legacy/utilities/buildutils/lib/jarjar-1.1.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/buildutils/lib/junit-4.8.1.jar
Normal file
BIN
legacy/utilities/buildutils/lib/junit-4.8.1.jar
Normal file
Binary file not shown.
|
@ -0,0 +1,93 @@
|
|||
/**
|
||||
* Copyright 2007 Google Inc.
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* This class is created to work around a known bug in JarJar which did not get fixed in release 1.1.
|
||||
* See the comments in edu.cornell.mannlib.vitro.utilities.jarlist.JarLister
|
||||
*/
|
||||
|
||||
package com.tonicsystems.jarjar;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import com.tonicsystems.jarjar.asm.ClassReader;
|
||||
import com.tonicsystems.jarjar.ext_util.ClassHeaderReader;
|
||||
import com.tonicsystems.jarjar.ext_util.ClassPathEntry;
|
||||
import com.tonicsystems.jarjar.ext_util.ClassPathIterator;
|
||||
import com.tonicsystems.jarjar.ext_util.RuntimeIOException;
|
||||
|
||||
public class KlugedDepFind {
|
||||
private File curDir = new File(System.getProperty("user.dir"));
|
||||
|
||||
public void setCurrentDirectory(File curDir) {
|
||||
this.curDir = curDir;
|
||||
}
|
||||
|
||||
public void run(String from, String to, DepHandler handler)
|
||||
throws IOException {
|
||||
try {
|
||||
ClassHeaderReader header = new ClassHeaderReader();
|
||||
Map<String, String> classes = new HashMap<String, String>();
|
||||
ClassPathIterator cp = new ClassPathIterator(curDir, to, null);
|
||||
try {
|
||||
while (cp.hasNext()) {
|
||||
ClassPathEntry entry = cp.next();
|
||||
InputStream in = entry.openStream();
|
||||
try {
|
||||
header.read(in);
|
||||
classes.put(header.getClassName(), entry.getSource());
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error reading " + entry.getName()
|
||||
+ ": " + e.getMessage());
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cp.close();
|
||||
}
|
||||
|
||||
handler.handleStart();
|
||||
cp = new ClassPathIterator(curDir, from, null);
|
||||
try {
|
||||
while (cp.hasNext()) {
|
||||
ClassPathEntry entry = cp.next();
|
||||
InputStream in = entry.openStream();
|
||||
try {
|
||||
new ClassReader(in).accept(new DepFindVisitor(classes,
|
||||
entry.getSource(), handler),
|
||||
ClassReader.SKIP_DEBUG
|
||||
| ClassReader.EXPAND_FRAMES);
|
||||
} catch (Exception e) {
|
||||
System.err.println("Error reading " + entry.getName()
|
||||
+ ": " + e.getMessage());
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
cp.close();
|
||||
}
|
||||
handler.handleEnd();
|
||||
} catch (RuntimeIOException e) {
|
||||
throw (IOException) e.getCause();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,435 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.containerneutral;
|
||||
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.Filter;
|
||||
import javax.servlet.ServletContextListener;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.xml.namespace.NamespaceContext;
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.xpath.XPath;
|
||||
import javax.xml.xpath.XPathConstants;
|
||||
import javax.xml.xpath.XPathExpression;
|
||||
import javax.xml.xpath.XPathExpressionException;
|
||||
import javax.xml.xpath.XPathFactory;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Node;
|
||||
import org.w3c.dom.NodeList;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
/**
|
||||
* Look at web.xml, and check for conditions that violate the Servlet 2.4 spec,
|
||||
* but that might not be noticed because Tomcat doesn't complain.
|
||||
*
|
||||
* ------
|
||||
*
|
||||
* Values of the <dispatcher/> tag:
|
||||
*
|
||||
* The spec permits only these values: "FORWARD", "REQUEST", "INCLUDE", "ERROR",
|
||||
* but Tomcat also allows the lower-case equivalents. GlassFish or WebLogic will
|
||||
* barf on lower-case.
|
||||
*
|
||||
* Check to see that only the upper-case values are used.
|
||||
*
|
||||
* ------
|
||||
*
|
||||
* Existence of Servlet classes:
|
||||
*
|
||||
* The spec allows the container to either load all servlets at startup, or to
|
||||
* load them when requested. Since Tomcat only loads servlet when requested, it
|
||||
* doesn't notice or complain if web.xml cites a <servlet-class/> that doesn't
|
||||
* exist, as long as it is never invoked. On the other hand, WebLogic loads all
|
||||
* serlvets at startup, and will barf if the class is not found.
|
||||
*
|
||||
* Check each <servlet-class/> to insure that the class can be loaded and
|
||||
* instantiated and assigned to HttpServlet.
|
||||
*
|
||||
* ------
|
||||
*
|
||||
* Embedded URIs in taglibs.
|
||||
*
|
||||
* I can't find this definitively in the JSP spec, but some containers complain
|
||||
* if web.xml specifies a <taglib-uri/> that conflicts with the <uri/> embedded
|
||||
* in the taglib itself. As far as I can see in the spec, the embedded <uri/>
|
||||
* tag is not required or referenced unless we are using
|
||||
* "Implicit Map Entries From TLDs", which in turn is only relevant for TLDs
|
||||
* packaged in JAR files. So, I can't find support for this complaint, but it
|
||||
* seems a reasonable one.
|
||||
*
|
||||
* Check each <taglib/> specified in web.xml. If the taglib has an embedded
|
||||
* <uri/> tag, it should match the <taglib-uri/> from web.xml.
|
||||
*
|
||||
* ------
|
||||
*
|
||||
* Existence of Listener and Filter classes.
|
||||
*
|
||||
* As far as I can tell, there is no ambiguity here, and every container will
|
||||
* complain if any of the <listener-class/> or <filter-class/> entries are
|
||||
* unsuitable. I check them anyway, since the mechanism was already assembled
|
||||
* for checking <servlet-class/> entries.
|
||||
*
|
||||
* Check each <listener-class/> to insure that the class can be loaded and
|
||||
* instantiated and assigned to ServletContextListener.
|
||||
*
|
||||
* Check each <filter-class/> to insure that the class can be loaded and
|
||||
* instantiated and assigned to Filter.
|
||||
*
|
||||
* ------
|
||||
*
|
||||
* A <servlet/> tag for every <servlet-mapping/> tag
|
||||
*
|
||||
* I can't find a mention of this in the spec, but Tomcat complains and refuses
|
||||
* to load the app if there is a <servlet-mapping/> tag whose <servlet-name/> is
|
||||
* not matched by a <servlet-name/> in a <servlet/> tag.
|
||||
*
|
||||
* Get sets of all <servlet-name/> tags that are specified in <servlet/> and
|
||||
* <servlet-mapping/> tags. There should not be any names in the
|
||||
* servlet-mappings that are not in the servlets.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Although this class is executed as a JUnit test, it doesn't have the usual
|
||||
* structure for a unit test.
|
||||
*
|
||||
* In order to produce the most diagnostic information, the test does not abort
|
||||
* on the first failure. Rather, failure messages are accumulated until all
|
||||
* checks have been performed, and the test list all such messages on failure.
|
||||
*
|
||||
* ---------------------------------------------------------------------
|
||||
*
|
||||
* Since this is not executed as part of the standard Vitro unit tests, it also
|
||||
* cannot use the standard logging mechanism. Log4J has not been initialized.
|
||||
*
|
||||
*/
|
||||
public class CheckContainerNeutrality {
|
||||
private static final String PROPERTY_WEBAPP_DIR = "CheckContainerNeutrality.webapp.dir";
|
||||
|
||||
private static DocumentBuilder docBuilder;
|
||||
private static XPath xpath;
|
||||
|
||||
@BeforeClass
|
||||
public static void createDocBuilder() {
|
||||
try {
|
||||
DocumentBuilderFactory factory = DocumentBuilderFactory
|
||||
.newInstance();
|
||||
factory.setNamespaceAware(true); // never forget this!
|
||||
docBuilder = factory.newDocumentBuilder();
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@BeforeClass
|
||||
public static void createXPath() {
|
||||
xpath = XPathFactory.newInstance().newXPath();
|
||||
xpath.setNamespaceContext(new StupidNamespaceContext());
|
||||
}
|
||||
|
||||
private File webappDir;
|
||||
private File webXmlFile;
|
||||
private Document webXmlDoc;
|
||||
private List<String> messages;
|
||||
|
||||
@Before
|
||||
public void setup() throws SAXException, IOException {
|
||||
String webappDirPath = System.getProperty(PROPERTY_WEBAPP_DIR);
|
||||
if (webappDirPath == null) {
|
||||
fail("System property '" + PROPERTY_WEBAPP_DIR
|
||||
+ "' was not provided.");
|
||||
}
|
||||
webappDir = new File(webappDirPath);
|
||||
if (!webappDir.isDirectory()) {
|
||||
fail("'" + webappDirPath + "' is not a directory");
|
||||
}
|
||||
webXmlFile = new File(webappDir, "WEB-INF/web.xml");
|
||||
if (!webXmlFile.isFile()) {
|
||||
fail("Can't find '" + webXmlFile.getAbsolutePath() + "'");
|
||||
}
|
||||
|
||||
webXmlDoc = docBuilder.parse(webXmlFile);
|
||||
|
||||
messages = new ArrayList<String>();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void checkAll() throws IOException {
|
||||
checkDispatcherValues();
|
||||
checkServletClasses();
|
||||
checkListenerClasses();
|
||||
checkFilterClasses();
|
||||
checkTaglibLocations();
|
||||
checkServletNames();
|
||||
|
||||
if (!messages.isEmpty()) {
|
||||
fail("Found these problems with '" + webXmlFile.getCanonicalPath()
|
||||
+ "'\n " + StringUtils.join(messages, "\n "));
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDispatcherValues() {
|
||||
List<String> okValues = Arrays.asList(new String[] { "FORWARD",
|
||||
"REQUEST", "INCLUDE", "ERROR" });
|
||||
for (Node n : findNodes("//j2ee:dispatcher")) {
|
||||
String text = n.getTextContent();
|
||||
if (!okValues.contains(text)) {
|
||||
messages.add("<dispatcher>" + text
|
||||
+ "</dispatcher> is not valid. Acceptable values are "
|
||||
+ okValues);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkServletClasses() {
|
||||
for (Node n : findNodes("//j2ee:servlet-class")) {
|
||||
String text = n.getTextContent();
|
||||
String problem = confirmClassNameIsValid(text, HttpServlet.class);
|
||||
if (problem != null) {
|
||||
messages.add("<servlet-class>" + text
|
||||
+ "</servlet-class> is not valid: " + problem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkListenerClasses() {
|
||||
for (Node n : findNodes("//j2ee:listener-class")) {
|
||||
String text = n.getTextContent();
|
||||
String problem = confirmClassNameIsValid(text,
|
||||
ServletContextListener.class);
|
||||
if (problem != null) {
|
||||
messages.add("<listener-class>" + text
|
||||
+ "</listener-class> is not valid: " + problem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkFilterClasses() {
|
||||
for (Node n : findNodes("//j2ee:filter-class")) {
|
||||
String text = n.getTextContent();
|
||||
String problem = confirmClassNameIsValid(text, Filter.class);
|
||||
if (problem != null) {
|
||||
messages.add("<filter-class>" + text
|
||||
+ "</filter-class> is not valid: " + problem);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkTaglibLocations() {
|
||||
for (Node n : findNodes("//j2ee:jsp-config/j2ee:taglib")) {
|
||||
String taglibUri = findNode("j2ee:taglib-uri", n).getTextContent();
|
||||
String taglibLocation = findNode("j2ee:taglib-location", n)
|
||||
.getTextContent();
|
||||
// System.out.println("taglibUri='" + taglibUri
|
||||
// + "', taglibLocation='" + taglibLocation + "'");
|
||||
String message = checkTaglibUri(taglibUri, taglibLocation);
|
||||
if (message != null) {
|
||||
messages.add(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void checkServletNames() {
|
||||
Set<String> servletNames = new HashSet<String>();
|
||||
for (Node n : findNodes("//j2ee:servlet/j2ee:servlet-name")) {
|
||||
servletNames.add(n.getTextContent());
|
||||
}
|
||||
|
||||
Set<String> servletMappingNames = new HashSet<String>();
|
||||
for (Node n : findNodes("//j2ee:servlet-mapping/j2ee:servlet-name")) {
|
||||
servletMappingNames.add(n.getTextContent());
|
||||
}
|
||||
|
||||
servletMappingNames.removeAll(servletNames);
|
||||
for (String name : servletMappingNames) {
|
||||
messages.add("There is a <servlet-mapping> tag for <servlet-name>"
|
||||
+ name + "</servlet-name>, but there is "
|
||||
+ "no matching <servlet> tag.");
|
||||
}
|
||||
}
|
||||
|
||||
private String checkTaglibUri(String taglibUri, String taglibLocation) {
|
||||
File taglibFile = new File(webappDir, taglibLocation);
|
||||
if (!taglibFile.isFile()) {
|
||||
return "File '" + taglibLocation + "' can't be found ('"
|
||||
+ taglibFile.getAbsolutePath() + "')";
|
||||
}
|
||||
|
||||
Document taglibDoc;
|
||||
try {
|
||||
taglibDoc = docBuilder.parse(taglibFile);
|
||||
} catch (SAXException e) {
|
||||
return "Failed to parse the taglib file '" + taglibFile + "': " + e;
|
||||
} catch (IOException e) {
|
||||
return "Failed to parse the taglib file '" + taglibFile + "': " + e;
|
||||
}
|
||||
|
||||
List<Node> uriNodes = findNodes("/j2ee:taglib/j2ee:uri",
|
||||
taglibDoc.getDocumentElement());
|
||||
// System.out.println("uriNodes: " + uriNodes);
|
||||
if (uriNodes.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
if (uriNodes.size() > 1) {
|
||||
return "taglib '" + taglibLocation + "' contains "
|
||||
+ uriNodes.size()
|
||||
+ " <uri> nodes. Expecting no more than 1";
|
||||
}
|
||||
|
||||
String embeddedUri = uriNodes.get(0).getTextContent();
|
||||
if (taglibUri.equals(embeddedUri)) {
|
||||
return null;
|
||||
} else {
|
||||
return "URI in taglib doesn't match the one in web.xml: taglib='"
|
||||
+ taglibLocation + "', internal URI='"
|
||||
+ uriNodes.get(0).getTextContent()
|
||||
+ "', URI from web.xml='" + taglibUri + "'";
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Search for an Xpath in web.xml, returning a handy list.
|
||||
*/
|
||||
private List<Node> findNodes(String pattern) {
|
||||
return findNodes(pattern, webXmlDoc.getDocumentElement());
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for an Xpath within a node of web.xml, returning a handy list.
|
||||
*/
|
||||
private List<Node> findNodes(String pattern, Node context) {
|
||||
try {
|
||||
XPathExpression xpe = xpath.compile(pattern);
|
||||
NodeList nodes = (NodeList) xpe.evaluate(context,
|
||||
XPathConstants.NODESET);
|
||||
List<Node> list = new ArrayList<Node>();
|
||||
for (int i = 0; i < nodes.getLength(); i++) {
|
||||
list.add(nodes.item(i));
|
||||
}
|
||||
return list;
|
||||
} catch (XPathExpressionException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for an Xpath within a node of web.xml, returning a single node or
|
||||
* throwing an exception.
|
||||
*/
|
||||
private Node findNode(String pattern, Node context) {
|
||||
List<Node> list = findNodes(pattern, context);
|
||||
if (list.size() != 1) {
|
||||
throw new RuntimeException("Expecting 1 node, but found "
|
||||
+ list.size() + " nodes using '" + pattern + "'");
|
||||
} else {
|
||||
return list.get(0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that the supplied className can be instantiated with a
|
||||
* zero-argument constructor, and assigned to a variable of the target
|
||||
* class.
|
||||
*/
|
||||
private String confirmClassNameIsValid(String className,
|
||||
Class<?> targetClass) {
|
||||
try {
|
||||
Class<?> specifiedClass = Class.forName(className);
|
||||
Object o = specifiedClass.newInstance();
|
||||
if (!targetClass.isInstance(o)) {
|
||||
return specifiedClass.getSimpleName()
|
||||
+ " is not a subclass of "
|
||||
+ targetClass.getSimpleName() + ".";
|
||||
}
|
||||
} catch (ClassNotFoundException e) {
|
||||
return "The class does not exist.";
|
||||
} catch (InstantiationException e) {
|
||||
return "The class does not have a public constructor "
|
||||
+ "that takes zero arguments.";
|
||||
} catch (IllegalAccessException e) {
|
||||
return "The class does not have a public constructor "
|
||||
+ "that takes zero arguments.";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dump the first 20 nodes of an XML context, excluding comments and blank
|
||||
* text nodes.
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
private int dumpXml(Node xmlNode, int... parms) {
|
||||
int remaining = (parms.length == 0) ? 20 : parms[0];
|
||||
int level = (parms.length < 2) ? 1 : parms[1];
|
||||
|
||||
Node n = xmlNode;
|
||||
|
||||
if (Node.COMMENT_NODE == n.getNodeType()) {
|
||||
return 0;
|
||||
}
|
||||
if (Node.TEXT_NODE == n.getNodeType()) {
|
||||
if (StringUtils.isBlank(n.getTextContent())) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
int used = 1;
|
||||
|
||||
System.out.println(StringUtils.repeat("-->", level) + n);
|
||||
NodeList nl = n.getChildNodes();
|
||||
for (int i = 0; (i < nl.getLength() && remaining > used); i++) {
|
||||
used += dumpXml(nl.item(i), remaining - used, level + 1);
|
||||
}
|
||||
return used;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper classes
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
private static class StupidNamespaceContext implements NamespaceContext {
|
||||
@Override
|
||||
public String getNamespaceURI(String prefix) {
|
||||
if ("j2ee".equals(prefix)) {
|
||||
return "http://java.sun.com/xml/ns/j2ee";
|
||||
} else {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getPrefix(String namespaceURI) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterator<?> getPrefixes(String namespaceURI) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,198 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.jarlist;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import com.tonicsystems.jarjar.AbstractDepHandler;
|
||||
import com.tonicsystems.jarjar.DepHandler;
|
||||
import com.tonicsystems.jarjar.KlugedDepFind;
|
||||
|
||||
/**
|
||||
* This takes the place of the JarJar main routine, in doing a Find operation.
|
||||
*
|
||||
* One thing this lets us do is to call KlugedDepFind instead of DepFind.
|
||||
* KlugedDepFind was created because JarJar had a known bug that wasn't fixed in
|
||||
* the latest release. (see http://code.google.com/p/jarjar/issues/detail?id=6).
|
||||
* I had to put KlugedDepFind into the com.tonicsystems.jarjar package so it
|
||||
* wauld have access to DepFindVisitor, which is package-private.
|
||||
*
|
||||
* The other thing we can do is to provide a custom DepHandler which records the
|
||||
* dependencies directly instead of writing them to a file which we would need
|
||||
* to parse. Since we have the dependencies in a data structure, it's easy to
|
||||
* walk the tree and find out what JARs are required, even through several
|
||||
* layers of dependency.
|
||||
*
|
||||
* When calling this, pass 2 arguments. The first is the path to the JAR which
|
||||
* contains the Vitro (or VIVO) classes. The second is the path to the directory
|
||||
* that contains the JARs. (shouldn't end with a slash)
|
||||
*
|
||||
* There is a list of JARs which we know we need but which aren't shown by the
|
||||
* analysis. For example, the MySQL driver is loaded dynamically by name, so an
|
||||
* analysis of the class files in the JARs won't show that it is used. For now,
|
||||
* these known dependencies are hard-coded, but it would be nice to read them
|
||||
* from a data file instead.
|
||||
*/
|
||||
public class JarLister {
|
||||
/**
|
||||
* <pre>
|
||||
*
|
||||
* What I originally wanted to do was this:
|
||||
*
|
||||
* <target name="jarlist" depends="jar" description="Figure out what JARs are needed">
|
||||
* <java classname="com.tonicsystems.jarjar.Main" fork="no" failonerror="true">
|
||||
* <classpath refid="utility.run.classpath" />
|
||||
* <arg value="find" />
|
||||
* <arg value="jar" />
|
||||
* <arg value="${build.dir}/${ant.project.name}.jar" />
|
||||
* <arg value="${appbase.dir}/lib/*" />
|
||||
* </java>
|
||||
* </target>
|
||||
*
|
||||
* I ended up with this instead:
|
||||
*
|
||||
* <target name="jarlist" depends="jar" description="Figure out what JARs are needed">
|
||||
* <java classname="edu.cornell.mannlib.vitro.utilities.jarlist.JarLister" fork="no" failonerror="true">
|
||||
* <classpath refid="utility.run.classpath" />
|
||||
* <arg value="${build.dir}/${ant.project.name}.jar" />
|
||||
* <arg value="${appbase.dir}/lib" />
|
||||
* <arg value="${appbase.dir}/config/jarlist/known_dependencies.txt" />
|
||||
* </java>
|
||||
* </target>
|
||||
*
|
||||
* </pre>
|
||||
*/
|
||||
|
||||
private final String topJar;
|
||||
private final String libDirectory;
|
||||
private final List<String> knownDependencies;
|
||||
|
||||
private final Map<String, Set<String>> dependencyMap = new HashMap<String, Set<String>>();
|
||||
private final Set<String> dependencySet = new TreeSet<String>();
|
||||
|
||||
public JarLister(String[] args) throws IOException {
|
||||
topJar = args[0];
|
||||
libDirectory = args[1];
|
||||
knownDependencies = Collections
|
||||
.unmodifiableList(readKnownDependencies(args[2]));
|
||||
}
|
||||
|
||||
private List<String> readKnownDependencies(String knownDependenciesFilename)
|
||||
throws IOException {
|
||||
List<String> list = new ArrayList<String>();
|
||||
|
||||
BufferedReader r = new BufferedReader(new FileReader(
|
||||
knownDependenciesFilename));
|
||||
|
||||
String line;
|
||||
while (null != (line = r.readLine())) {
|
||||
line = line.trim();
|
||||
if (!(line.startsWith("#") || line.isEmpty())) {
|
||||
list.add(line);
|
||||
}
|
||||
}
|
||||
return list;
|
||||
}
|
||||
|
||||
public void runDepFind() throws IOException {
|
||||
DepHandler handler = new AbstractDepHandler(DepHandler.LEVEL_JAR) {
|
||||
@Override
|
||||
public void handle(String from, String to) {
|
||||
addToMap(from, to);
|
||||
}
|
||||
};
|
||||
|
||||
String fullPath = topJar + ":" + libDirectory + "/*";
|
||||
|
||||
new KlugedDepFind().run(fullPath, fullPath, handler);
|
||||
}
|
||||
|
||||
private void addToMap(String fromPath, String toPath) {
|
||||
String fromName = new File(fromPath).getName();
|
||||
String toName = new File(toPath).getName();
|
||||
|
||||
if (!dependencyMap.containsKey(fromName)) {
|
||||
dependencyMap.put(fromName, new HashSet<String>());
|
||||
}
|
||||
dependencyMap.get(fromName).add(toName);
|
||||
// System.out.println("Adding " + fromName + " ==> " + toName);
|
||||
}
|
||||
|
||||
public void populateDependencySet() {
|
||||
String topJarName = new File(topJar).getName();
|
||||
addDependenciesFor(topJarName);
|
||||
|
||||
for (String known : knownDependencies) {
|
||||
dependencySet.add(known);
|
||||
addDependenciesFor(known);
|
||||
}
|
||||
}
|
||||
|
||||
private void addDependenciesFor(String name) {
|
||||
if (!dependencyMap.containsKey(name)) {
|
||||
return;
|
||||
}
|
||||
for (String depend : dependencyMap.get(name)) {
|
||||
if (!dependencySet.contains(depend)) {
|
||||
dependencySet.add(depend);
|
||||
// System.out.println("Depend: " + depend);
|
||||
addDependenciesFor(depend);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void dumpDependencySet(PrintWriter w) {
|
||||
w.println("--------------------");
|
||||
w.println("Known required JARs");
|
||||
w.println("--------------------");
|
||||
for (String d : knownDependencies) {
|
||||
w.println(" " + d);
|
||||
}
|
||||
w.println();
|
||||
|
||||
w.println("--------------------");
|
||||
w.println("Dependent JARs");
|
||||
w.println("--------------------");
|
||||
for (String d : dependencySet) {
|
||||
w.println(" " + d);
|
||||
}
|
||||
w.println();
|
||||
|
||||
File libDir = new File(libDirectory);
|
||||
SortedSet<String> unused = new TreeSet<String>(Arrays.asList(libDir
|
||||
.list()));
|
||||
unused.removeAll(dependencySet);
|
||||
|
||||
w.println("--------------------");
|
||||
w.println("Unused JARs");
|
||||
w.println("--------------------");
|
||||
for (String d : unused) {
|
||||
w.println(" " + d);
|
||||
}
|
||||
w.println();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws IOException {
|
||||
JarLister jl = new JarLister(args);
|
||||
jl.runDepFind();
|
||||
jl.populateDependencySet();
|
||||
|
||||
PrintWriter output = new PrintWriter(System.out, true);
|
||||
jl.dumpDependencySet(output);
|
||||
output.close();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,161 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.revisioninfo;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.StringWriter;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A harness that runs a system-level command.
|
||||
*
|
||||
* No provision is made for standard input.
|
||||
*
|
||||
* The standard output and standard error streams are asynchronously read, so
|
||||
* the sub-process will not block on full buffers. Warning: if either of these
|
||||
* streams contain more data than can fit into a String, then we will have a
|
||||
* problem.
|
||||
*/
|
||||
public class ProcessRunner {
|
||||
private int returnCode;
|
||||
private String stdOut = "";
|
||||
private String stdErr = "";
|
||||
private File workingDirectory;
|
||||
|
||||
private final Map<String, String> environmentAdditions = new HashMap<String, String>();
|
||||
|
||||
/** Set the directory that the command will run in. */
|
||||
public void setWorkingDirectory(File workingDirectory) {
|
||||
this.workingDirectory = workingDirectory;
|
||||
}
|
||||
|
||||
/** Add (or replace) any environment variable. */
|
||||
public void setEnvironmentVariable(String key, String value) {
|
||||
this.environmentAdditions.put(key, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Run the command.
|
||||
*
|
||||
* @param command
|
||||
* a list containing the operating system program and its
|
||||
* arguments. See
|
||||
* {@link java.lang.ProcessBuilder#ProcessBuilder(List)}.
|
||||
*/
|
||||
public void run(List<String> command) throws ProcessException {
|
||||
try {
|
||||
ProcessBuilder builder = new ProcessBuilder(command);
|
||||
|
||||
if (workingDirectory != null) {
|
||||
builder.directory(workingDirectory);
|
||||
}
|
||||
|
||||
if (!environmentAdditions.isEmpty()) {
|
||||
builder.environment().putAll(this.environmentAdditions);
|
||||
}
|
||||
|
||||
Process process = builder.start();
|
||||
StreamEater outputEater = new StreamEater(process.getInputStream());
|
||||
StreamEater errorEater = new StreamEater(process.getErrorStream());
|
||||
|
||||
this.returnCode = process.waitFor();
|
||||
|
||||
outputEater.join();
|
||||
this.stdOut = outputEater.getContents();
|
||||
|
||||
errorEater.join();
|
||||
this.stdErr = errorEater.getContents();
|
||||
} catch (IOException e) {
|
||||
throw new ProcessException("Exception when handling sub-process:",
|
||||
e);
|
||||
} catch (InterruptedException e) {
|
||||
throw new ProcessException("Exception when handling sub-process:",
|
||||
e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getReturnCode() {
|
||||
return returnCode;
|
||||
}
|
||||
|
||||
public String getStdErr() {
|
||||
return stdErr;
|
||||
}
|
||||
|
||||
public String getStdOut() {
|
||||
return stdOut;
|
||||
}
|
||||
|
||||
/**
|
||||
* A thread that reads an InputStream until it reaches end of file, then
|
||||
* closes the stream.
|
||||
*/
|
||||
private static class StreamEater extends Thread {
|
||||
private final InputStream stream;
|
||||
|
||||
private final StringWriter contents = new StringWriter();
|
||||
|
||||
private final byte[] buffer = new byte[4096];
|
||||
|
||||
public StreamEater(InputStream stream) {
|
||||
this.stream = stream;
|
||||
this.start();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
int howMany = 0;
|
||||
while (true) {
|
||||
howMany = stream.read(buffer);
|
||||
if (howMany > 0) {
|
||||
contents.write(new String(buffer, 0, howMany));
|
||||
} else if (howMany == 0) {
|
||||
Thread.yield();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
try {
|
||||
stream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getContents() {
|
||||
return contents.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicates a problem when dealing with a spawned sub-process.
|
||||
*/
|
||||
public static class ProcessException extends Exception {
|
||||
|
||||
public ProcessException() {
|
||||
}
|
||||
|
||||
public ProcessException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public ProcessException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public ProcessException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,253 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.revisioninfo;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.EOFException;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.revisioninfo.ProcessRunner.ProcessException;
|
||||
|
||||
/**
|
||||
* Get release and revision information to display on screen. Put this
|
||||
* information into a single line and append it to the specified file.
|
||||
*
|
||||
* Ask Git for the information. If Git is available, and if this is a working
|
||||
* directory, then we can build the info from the responses we get from
|
||||
* "git describe", "git symbolic-ref" and "git log".
|
||||
*
|
||||
* If that doesn't work, read the information from the "revisionInfo" file in
|
||||
* the product directory. Presumably, that file was created when the source was
|
||||
* exported from Git.
|
||||
*
|
||||
* If that doesn't work either, return something like this:
|
||||
* "productName ~ unknown ~ unknown"
|
||||
*/
|
||||
public class RevisionInfoBuilder {
|
||||
|
||||
/**
|
||||
* Indicates a problem with the command-line arguments.
|
||||
*/
|
||||
private static class UsageException extends Exception {
|
||||
UsageException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An object that holds the revision information and a message about how we
|
||||
* obtained it.
|
||||
*/
|
||||
private static class Results {
|
||||
final String message;
|
||||
final String infoLine;
|
||||
|
||||
Results(String message, String infoLine) {
|
||||
this.message = message;
|
||||
this.infoLine = infoLine;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return message + ": " + infoLine;
|
||||
}
|
||||
}
|
||||
|
||||
private static final String GIT_DIRECTORY_NAME = ".git";
|
||||
private static final String[] GIT_DESCRIBE_COMMAND = { "git", "describe" };
|
||||
private static final String[] GIT_SREF_COMMAND = { "git", "symbolic-ref",
|
||||
"HEAD" };
|
||||
private static final String[] GIT_LOG_COMMAND = { "git", "log",
|
||||
"--pretty=format:%h", "-1" };
|
||||
private static final String INFO_LINE_DELIMITER = " ~ ";
|
||||
private static final String REVISION_INFO_FILENAME = "revisionInfo";
|
||||
|
||||
private final String productName;
|
||||
private final File productDirectory;
|
||||
private final File resultFile;
|
||||
|
||||
private Results results;
|
||||
|
||||
public RevisionInfoBuilder(String[] args) throws UsageException {
|
||||
if (args.length != 3) {
|
||||
throw new UsageException(
|
||||
"RevisionInfoBuilder requires 3 arguments, not "
|
||||
+ args.length);
|
||||
}
|
||||
|
||||
productName = args[0];
|
||||
productDirectory = new File(args[1]);
|
||||
resultFile = new File(args[2]);
|
||||
|
||||
if (!productDirectory.isDirectory()) {
|
||||
throw new UsageException("Directory '"
|
||||
+ productDirectory.getAbsolutePath() + "' does not exist.");
|
||||
}
|
||||
|
||||
if (!resultFile.getParentFile().exists()) {
|
||||
throw new UsageException("Result file '"
|
||||
+ resultFile.getAbsolutePath()
|
||||
+ "' does not exist, and we can't create it "
|
||||
+ "because it's parent directory doesn't exist either.");
|
||||
}
|
||||
}
|
||||
|
||||
private void buildInfo() {
|
||||
results = buildInfoFromGit();
|
||||
if (results == null) {
|
||||
results = buildInfoFromFile();
|
||||
}
|
||||
if (results == null) {
|
||||
results = buildDummyInfo();
|
||||
}
|
||||
}
|
||||
|
||||
private Results buildInfoFromGit() {
|
||||
if (!isThisAGitWorkspace()) {
|
||||
System.out.println("Not a git workspace");
|
||||
return null;
|
||||
}
|
||||
|
||||
String release = assembleReleaseNameFromGit();
|
||||
if (release == null) {
|
||||
System.out.println("Couldn't get release name from Git");
|
||||
}
|
||||
|
||||
String revision = obtainCommitIdFromGit();
|
||||
if (revision == null) {
|
||||
System.out.println("Couldn't get commit ID from Git");
|
||||
}
|
||||
|
||||
if ((revision == null) && (release == null)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new Results("Info from Git", buildLine(release, revision));
|
||||
}
|
||||
|
||||
private boolean isThisAGitWorkspace() {
|
||||
File gitDirectory = new File(productDirectory, GIT_DIRECTORY_NAME);
|
||||
return gitDirectory.isDirectory();
|
||||
}
|
||||
|
||||
private String assembleReleaseNameFromGit() {
|
||||
String describeResponse = runSubProcess(GIT_DESCRIBE_COMMAND);
|
||||
String srefResponse = runSubProcess(GIT_SREF_COMMAND);
|
||||
return parseReleaseName(describeResponse, srefResponse);
|
||||
}
|
||||
|
||||
private String obtainCommitIdFromGit() {
|
||||
String logResponse = runSubProcess(GIT_LOG_COMMAND);
|
||||
return parseLogResponse(logResponse);
|
||||
}
|
||||
|
||||
private String parseReleaseName(String describeResponse, String srefResponse) {
|
||||
if (describeResponse != null) {
|
||||
return describeResponse.trim() + " tag";
|
||||
} else if (srefResponse != null) {
|
||||
return srefResponse.substring(srefResponse.lastIndexOf('/') + 1)
|
||||
.trim() + " branch";
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String parseLogResponse(String logResponse) {
|
||||
return logResponse;
|
||||
}
|
||||
|
||||
private String runSubProcess(String[] cmdArray) {
|
||||
List<String> command = Arrays.asList(cmdArray);
|
||||
try {
|
||||
ProcessRunner runner = new ProcessRunner();
|
||||
runner.setWorkingDirectory(productDirectory);
|
||||
|
||||
runner.run(command);
|
||||
|
||||
int rc = runner.getReturnCode();
|
||||
if (rc != 0) {
|
||||
throw new ProcessRunner.ProcessException("Return code from "
|
||||
+ command + " was " + rc);
|
||||
}
|
||||
|
||||
String output = runner.getStdOut();
|
||||
// System.err.println(command + " response was '" + output + "'");
|
||||
return output;
|
||||
} catch (ProcessException e) {
|
||||
// System.out.println(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Results buildInfoFromFile() {
|
||||
try {
|
||||
File revisionInfoFile = new File(productDirectory,
|
||||
REVISION_INFO_FILENAME);
|
||||
BufferedReader reader = new BufferedReader(new FileReader(
|
||||
revisionInfoFile));
|
||||
|
||||
String release = reader.readLine();
|
||||
if (release == null) {
|
||||
throw new EOFException("No release line in file.");
|
||||
}
|
||||
|
||||
String revision = reader.readLine();
|
||||
if (revision == null) {
|
||||
throw new EOFException("No revision line in file.");
|
||||
}
|
||||
|
||||
return new Results("Info from file", buildLine(release, revision));
|
||||
} catch (IOException e) {
|
||||
System.out.println("No information from file: " + e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private Results buildDummyInfo() {
|
||||
String line = buildLine(null, null);
|
||||
return new Results("Using dummy info", line);
|
||||
}
|
||||
|
||||
private String buildLine(String release, String revision) {
|
||||
if (release == null) {
|
||||
release = "unknown";
|
||||
}
|
||||
if (revision == null) {
|
||||
revision = "unknown";
|
||||
}
|
||||
return productName + INFO_LINE_DELIMITER + release.trim()
|
||||
+ INFO_LINE_DELIMITER + revision.trim();
|
||||
}
|
||||
|
||||
private void writeLine() throws IOException {
|
||||
Writer writer = null;
|
||||
writer = new FileWriter(resultFile, true);
|
||||
writer.write(results.infoLine + "\n");
|
||||
writer.close();
|
||||
|
||||
System.out.println(results);
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
RevisionInfoBuilder builder = new RevisionInfoBuilder(args);
|
||||
builder.buildInfo();
|
||||
builder.writeLine();
|
||||
} catch (UsageException e) {
|
||||
System.err.println(e);
|
||||
System.err.println("usage: RevisionInfoBuilder [product_name] "
|
||||
+ "[product_directory] [output_file]");
|
||||
System.exit(1);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,346 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testing;
|
||||
|
||||
import static edu.cornell.mannlib.vitro.utilities.testing.VitroTestRunner.ReportLevel.BRIEF;
|
||||
import static edu.cornell.mannlib.vitro.utilities.testing.VitroTestRunner.ReportLevel.FULL;
|
||||
import static edu.cornell.mannlib.vitro.utilities.testing.VitroTestRunner.ReportLevel.MORE;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runner.Result;
|
||||
import org.junit.runner.notification.Failure;
|
||||
import org.junit.runner.notification.RunListener;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.testing.VitroTestRunner.ReportLevel;
|
||||
|
||||
/**
|
||||
* Listen to events as they come from the JUnit test runner. The events from the
|
||||
* lifecycle methods are broken down into semantic chunks and executed. Three
|
||||
* levels of output are available.
|
||||
*
|
||||
* On the surface, JUnit treats "failures" (failed assertions) the same as
|
||||
* "errors" (unexpected exceptions). We're going to distinguish between them.
|
||||
*
|
||||
* @author jeb228
|
||||
*/
|
||||
public class VitroTestRunListener extends RunListener {
|
||||
private final ReportLevel reportLevel;
|
||||
|
||||
private int classCount;
|
||||
private int testsTotal;
|
||||
private int errorsTotal;
|
||||
private int failuresTotal;
|
||||
private int ignoresTotal;
|
||||
private long overallStartTime;
|
||||
|
||||
private Class<?> currentClass;
|
||||
private int testsCurrentClass;
|
||||
private int errorsCurrentClass;
|
||||
private int failuresCurrentClass;
|
||||
private int ignoresCurrentClass;
|
||||
private long classStartTime;
|
||||
|
||||
private String currentTest;
|
||||
private boolean testHadError;
|
||||
private boolean testFailed;
|
||||
private boolean testIgnored;
|
||||
private long testStartTime;
|
||||
|
||||
public VitroTestRunListener(ReportLevel reportLevel) {
|
||||
this.reportLevel = reportLevel;
|
||||
}
|
||||
|
||||
/** Did any of the tests fail or have errors? */
|
||||
public boolean didEverythingPass() {
|
||||
return (failuresTotal == 0) && (errorsTotal == 0);
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Life-cycle methods that will be called by the test runner.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public void testRunStarted(Description description) throws Exception {
|
||||
openTestRun();
|
||||
reportTestRunStart();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testStarted(Description description) throws Exception {
|
||||
if (currentClass != description.getTestClass()) {
|
||||
if (currentClass != null) {
|
||||
closeCurrentClass();
|
||||
reportCurrentClass();
|
||||
}
|
||||
|
||||
openCurrentClass(description);
|
||||
reportCurrentClassStart();
|
||||
}
|
||||
|
||||
openCurrentTest(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testAssumptionFailure(Failure failure) {
|
||||
if (isError(failure)) {
|
||||
testHadError = true;
|
||||
reportError(failure);
|
||||
} else {
|
||||
testFailed = true;
|
||||
reportFailure(failure);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFailure(Failure failure) throws Exception {
|
||||
if (isError(failure)) {
|
||||
testHadError = true;
|
||||
reportError(failure);
|
||||
} else {
|
||||
testFailed = true;
|
||||
reportFailure(failure);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testFinished(Description description) throws Exception {
|
||||
closeCurrentTest();
|
||||
reportCurrentTest();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testIgnored(Description description) throws Exception {
|
||||
testStarted(description);
|
||||
testIgnored = true;
|
||||
testFinished(description);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void testRunFinished(Result result) throws Exception {
|
||||
if (currentClass != null) {
|
||||
closeCurrentClass();
|
||||
reportCurrentClass();
|
||||
}
|
||||
closeTestRun();
|
||||
reportTestRun();
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Handling the logical events.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private void openTestRun() {
|
||||
overallStartTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private void closeTestRun() {
|
||||
// Nothing to close.
|
||||
}
|
||||
|
||||
private void reportTestRunStart() {
|
||||
if (reportLevel == FULL) {
|
||||
System.out
|
||||
.println("Starting test run at " + time(overallStartTime));
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
if (reportLevel == MORE) {
|
||||
System.out
|
||||
.println("Starting test run at " + time(overallStartTime));
|
||||
System.out.println();
|
||||
System.out.println("Tests Pass Error Fail Ignore Seconds");
|
||||
}
|
||||
}
|
||||
|
||||
private void reportTestRun() {
|
||||
int successes = testsTotal - errorsTotal - failuresTotal - ignoresTotal;
|
||||
|
||||
if (reportLevel != BRIEF) {
|
||||
System.out.println();
|
||||
}
|
||||
|
||||
System.out.format(
|
||||
"Tests Pass Error Fail Ignore Seconds TOTAL (%d classes)\n",
|
||||
classCount);
|
||||
System.out.format(" %4d %4d %4d %4d %4d %6s\n", testsTotal,
|
||||
successes, errorsTotal, failuresTotal, ignoresTotal,
|
||||
elapsed(overallStartTime));
|
||||
|
||||
if (reportLevel != BRIEF) {
|
||||
System.out.println("Ending test run at "
|
||||
+ time(System.currentTimeMillis()));
|
||||
}
|
||||
}
|
||||
|
||||
private void openCurrentClass(Description description) {
|
||||
currentClass = description.getTestClass();
|
||||
classStartTime = System.currentTimeMillis();
|
||||
testsCurrentClass = 0;
|
||||
errorsCurrentClass = 0;
|
||||
failuresCurrentClass = 0;
|
||||
ignoresCurrentClass = 0;
|
||||
}
|
||||
|
||||
private void closeCurrentClass() {
|
||||
classCount++;
|
||||
testsTotal += testsCurrentClass;
|
||||
errorsTotal += errorsCurrentClass;
|
||||
failuresTotal += failuresCurrentClass;
|
||||
ignoresTotal += ignoresCurrentClass;
|
||||
}
|
||||
|
||||
private void reportCurrentClassStart() {
|
||||
if (reportLevel == FULL) {
|
||||
System.out.format("Tests Pass Error Fail Ignore Seconds %s\n",
|
||||
currentClass.getName());
|
||||
}
|
||||
}
|
||||
|
||||
private void reportCurrentClass() {
|
||||
int successes = testsCurrentClass - errorsCurrentClass
|
||||
- failuresCurrentClass - ignoresCurrentClass;
|
||||
if (reportLevel == MORE) {
|
||||
System.out.format(" %4d %4d %4d %4d %4d %6s %s\n",
|
||||
testsCurrentClass, successes, errorsCurrentClass,
|
||||
failuresCurrentClass, ignoresCurrentClass,
|
||||
elapsed(classStartTime), currentClass.getSimpleName());
|
||||
}
|
||||
if (reportLevel == FULL) {
|
||||
System.out.println("-----------------------------------");
|
||||
System.out.format(" %4d %4d %4d %4d %4d %6s\n",
|
||||
testsCurrentClass, successes, errorsCurrentClass,
|
||||
failuresCurrentClass, ignoresCurrentClass,
|
||||
elapsed(classStartTime));
|
||||
System.out.println();
|
||||
}
|
||||
}
|
||||
|
||||
private void openCurrentTest(Description description) {
|
||||
currentTest = description.getMethodName();
|
||||
testHadError = false;
|
||||
testFailed = false;
|
||||
testIgnored = false;
|
||||
testStartTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
private void closeCurrentTest() {
|
||||
if (testHadError) {
|
||||
errorsCurrentClass++;
|
||||
}
|
||||
if (testFailed) {
|
||||
failuresCurrentClass++;
|
||||
}
|
||||
if (testIgnored) {
|
||||
ignoresCurrentClass++;
|
||||
}
|
||||
testsCurrentClass++;
|
||||
}
|
||||
|
||||
private boolean isError(Failure failure) {
|
||||
Throwable throwable = failure.getException();
|
||||
return (throwable != null) && !(throwable instanceof AssertionError);
|
||||
}
|
||||
|
||||
private void reportError(Failure error) {
|
||||
Description description = error.getDescription();
|
||||
String methodName = description.getMethodName();
|
||||
String className = description.getTestClass().getName();
|
||||
String message = error.getMessage();
|
||||
System.out.format("EXCEPTION: test %s() in %s: %s\n",
|
||||
methodName, className, message);
|
||||
System.out.println(formatStackTrace(error.getException()));
|
||||
}
|
||||
|
||||
private void reportFailure(Failure failure) {
|
||||
Description description = failure.getDescription();
|
||||
String methodName = description.getMethodName();
|
||||
String className = description.getTestClass().getName();
|
||||
String message = failure.getMessage();
|
||||
System.out.format("TEST FAILED: test %s() in %s: %s\n", methodName,
|
||||
className, message);
|
||||
}
|
||||
|
||||
private void reportCurrentTest() {
|
||||
if (reportLevel == FULL) {
|
||||
char passFlag = (testIgnored | testFailed | testHadError) ? ' '
|
||||
: '1';
|
||||
char errorFlag = testHadError ? '1' : ' ';
|
||||
char failFlag = testFailed ? '1' : ' ';
|
||||
char ignoreFlag = testIgnored ? '1' : ' ';
|
||||
System.out.format(
|
||||
" %c %c %c %c %6s %s()\n",
|
||||
passFlag, errorFlag, failFlag, ignoreFlag,
|
||||
elapsed(testStartTime), currentTest);
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Formatting methods.
|
||||
// -------------------------------------------------------------------------
|
||||
|
||||
private final SimpleDateFormat formatter = new SimpleDateFormat(
|
||||
"HH:mm:ss 'on' MMM dd, yyyy");
|
||||
|
||||
private String time(long time) {
|
||||
return formatter.format(new Date(time));
|
||||
}
|
||||
|
||||
/** Show elapsed time in 6 columns. */
|
||||
private String elapsed(long start) {
|
||||
long interval = System.currentTimeMillis() - start;
|
||||
return String.format("%6.2f", ((float) interval) / 1000.0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Trim the stack trace: don't show the line saying "23 more", and don't
|
||||
* show the lines about org.junit or java.lang.reflect or sun.reflect.
|
||||
*
|
||||
* Once we hit some "client code", we won't trim any futher lines even if
|
||||
* they belong to org.junit, or the others.
|
||||
*
|
||||
* If we have nested exceptions, the process repeats for each "Caused by"
|
||||
* section.
|
||||
*/
|
||||
private String formatStackTrace(Throwable throwable) {
|
||||
StringWriter w = new StringWriter();
|
||||
throwable.printStackTrace(new PrintWriter(w));
|
||||
String[] lineArray = w.toString().split("\\n");
|
||||
List<String> lines = new ArrayList<String>(Arrays.asList(lineArray));
|
||||
|
||||
boolean removing = true;
|
||||
for (int i = lines.size() - 1; i > 0; i--) {
|
||||
String line = lines.get(i);
|
||||
if (removing) {
|
||||
if (line.matches("\\s*[\\.\\s\\d]+more\\s*")
|
||||
|| line.contains("at "
|
||||
+ VitroTestRunner.class.getName())
|
||||
|| line.contains("at org.junit.")
|
||||
|| line.contains("at java.lang.reflect.")
|
||||
|| line.contains("at sun.reflect.")) {
|
||||
lines.remove(line);
|
||||
} else {
|
||||
removing = false;
|
||||
}
|
||||
} else {
|
||||
if (line.contains("Caused by: ")) {
|
||||
removing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (String line : lines) {
|
||||
result.append(line).append('\n');
|
||||
}
|
||||
return result.toString().trim();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,153 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testing;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
|
||||
import org.junit.runner.JUnitCore;
|
||||
|
||||
/**
|
||||
* A Java application that will run the Vitro unit tests. It searches for unit
|
||||
* tests in the supplied source directory (any file whose name is *Test.Java).
|
||||
* It runs the tests with a variety of reporting detail, depending on the level
|
||||
* selected. If the level selector is absent or unrecognized, the medium level
|
||||
* is used.
|
||||
*
|
||||
* @author jeb228
|
||||
*/
|
||||
public class VitroTestRunner {
|
||||
public enum ReportLevel {
|
||||
/** Report only the one-line summary. */
|
||||
BRIEF,
|
||||
/** Report times and statistics for each test class. */
|
||||
MORE,
|
||||
/** Report times and statistics for each test method. */
|
||||
FULL
|
||||
}
|
||||
|
||||
private final List<Class<?>> classes;
|
||||
private final VitroTestRunListener listener;
|
||||
|
||||
/**
|
||||
* Locate the test classes. Initialize the test listener.
|
||||
*/
|
||||
public VitroTestRunner(File sourceRootDir, ReportLevel reportLevel) {
|
||||
List<String> classNames = getListOfTestClassNames(sourceRootDir);
|
||||
this.classes = getTestClasses(classNames);
|
||||
this.listener = new VitroTestRunListener(reportLevel);
|
||||
}
|
||||
|
||||
/**
|
||||
* Start a recursive search through the source directory.
|
||||
*/
|
||||
private List<String> getListOfTestClassNames(File sourceRootDir) {
|
||||
SortedSet<String> names = new TreeSet<String>();
|
||||
searchForTestClasses(names, "", sourceRootDir);
|
||||
return new ArrayList<String>(names);
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively search the directory for files in the form "*Test.java".
|
||||
* Ignore any files or directories whose names start with a "."
|
||||
*/
|
||||
private void searchForTestClasses(SortedSet<String> names, String prefix,
|
||||
File directory) {
|
||||
for (File file : directory.listFiles()) {
|
||||
String filename = file.getName();
|
||||
if (filename.startsWith(".")) {
|
||||
// ignore .svn, etc.
|
||||
} else if (file.isDirectory()) {
|
||||
searchForTestClasses(names, prefix + filename + ".", file);
|
||||
} else if (filename.endsWith("Test.java")) {
|
||||
String classname = filename.substring(0, filename.length() - 5);
|
||||
names.add(prefix + classname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiate a class for each test class name.
|
||||
*/
|
||||
private List<Class<?>> getTestClasses(List<String> classNames) {
|
||||
List<Class<?>> classes = new ArrayList<Class<?>>();
|
||||
for (String classname : classNames) {
|
||||
try {
|
||||
classes.add(Class.forName(classname));
|
||||
} catch (ClassNotFoundException e) {
|
||||
throw new IllegalArgumentException("Can't load test class: "
|
||||
+ classname, e);
|
||||
}
|
||||
}
|
||||
return classes;
|
||||
}
|
||||
|
||||
/**
|
||||
* We've located all of the test clases. Now run them.
|
||||
*/
|
||||
private void run() {
|
||||
JUnitCore junitCore = new JUnitCore();
|
||||
junitCore.addListener(this.listener);
|
||||
junitCore.run(this.classes.toArray(new Class<?>[0]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Did any of the tests fail?
|
||||
*/
|
||||
private boolean didEverythingPass() {
|
||||
return this.listener.didEverythingPass();
|
||||
}
|
||||
|
||||
/**
|
||||
* <p>
|
||||
* You must provide a path to the source directory of the test classes.
|
||||
* </p>
|
||||
* <p>
|
||||
* You may also provide a reporting level of "BRIEF", "MORE", or "FULL". If
|
||||
* no level is provided, or if it is not recognized, "BRIEF" is used.
|
||||
* </p>
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
if ((args.length < 1) || (args.length > 2)) {
|
||||
usage("Wrong number of arguments: expecting 1 or 2, but found "
|
||||
+ args.length + ".");
|
||||
}
|
||||
File sourceRootDir = new File(args[0]);
|
||||
|
||||
if (!sourceRootDir.exists()) {
|
||||
usage(sourceRootDir + " does not exist.");
|
||||
}
|
||||
if (!sourceRootDir.isDirectory()) {
|
||||
usage(sourceRootDir + " is not a directory.");
|
||||
}
|
||||
|
||||
ReportLevel reportLevel = ReportLevel.MORE;
|
||||
if (args.length == 2) {
|
||||
for (ReportLevel level : ReportLevel.values()) {
|
||||
if (level.toString().equalsIgnoreCase(args[1])) {
|
||||
reportLevel = level;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
VitroTestRunner runner = new VitroTestRunner(sourceRootDir, reportLevel);
|
||||
runner.run();
|
||||
|
||||
if (!runner.didEverythingPass()) {
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell them how it should have been done.
|
||||
*/
|
||||
private static void usage(String message) {
|
||||
System.out.println(message);
|
||||
System.out.println("usage: " + VitroTestRunner.class.getSimpleName()
|
||||
+ " sourceRootDirectory [ BRIEF | MORE | FULL ]");
|
||||
System.exit(1);
|
||||
}
|
||||
}
|
300
legacy/utilities/licenser/licenser.rb
Normal file
300
legacy/utilities/licenser/licenser.rb
Normal file
|
@ -0,0 +1,300 @@
|
|||
=begin
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Scan the source directory, checking for expected "magic license tags",
|
||||
or
|
||||
Copy the source directory, inserting licensing information into the files.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
--------------------------------------------------------------------------------
|
||||
=end
|
||||
$: << File.dirname(File.expand_path(__FILE__))
|
||||
require 'date'
|
||||
require 'fileutils'
|
||||
require 'pathname'
|
||||
require 'property_file_reader'
|
||||
require 'licenser_stats'
|
||||
|
||||
class Licenser
|
||||
|
||||
MAGIC_STRING = '$This file is distributed under the terms of the license in /doc/license.txt$'
|
||||
|
||||
# ------------------------------------------------------------------------------------
|
||||
private
|
||||
# ------------------------------------------------------------------------------------
|
||||
#
|
||||
# Some paths in the properties file, if they are relative, should be relative to the
|
||||
# properties file itself.
|
||||
def relative_to_properties(properties, key)
|
||||
path = properties[key]
|
||||
base = File.dirname(properties['properties_file_path'])
|
||||
|
||||
return nil if path == nil
|
||||
return File.expand_path(path) if Pathname.new(path).absolute?
|
||||
return File.expand_path(File.join(base, path))
|
||||
end
|
||||
|
||||
# Some paths in the properties file, if they are relative, should be relative to the
|
||||
# source directory.
|
||||
def relative_to_source(properties, key)
|
||||
path = properties[key]
|
||||
base = @source_dir ? @source_dir : ''
|
||||
|
||||
return nil if path == nil
|
||||
return path if Pathname.new(path).absolute?
|
||||
return File.expand_path(File.join(base, path))
|
||||
end
|
||||
|
||||
# Confirm that the parameters are reasonable.
|
||||
#
|
||||
def sanity_checks_on_parameters()
|
||||
# Check that all necessary properties are here.
|
||||
raise("Properties file must contain a value for 'source_dir'") if @source_dir == nil
|
||||
raise("Properties file must contain a value for 'known_exceptions'") if @known_exceptions_file == nil
|
||||
raise("Properties file must contain a value for 'skip_directories'") if @skip_directories_list == nil
|
||||
raise("Properties file must contain a value for 'file_matchers'") if @file_match_list == nil
|
||||
raise("Properties file must contain a value for 'report_level'") if @report_level_string == nil
|
||||
|
||||
if !File.exist?(@source_dir)
|
||||
raise "Source directory does not exist: #{@source_dir}"
|
||||
end
|
||||
|
||||
if !File.exist?(@known_exceptions_file)
|
||||
raise "Known exceptions file does not exist: #{@known_exceptions_file}"
|
||||
end
|
||||
end
|
||||
|
||||
# The globs in the exceptions file are assumed to be
|
||||
# relative to the source directory. Make them explicitly so.
|
||||
#
|
||||
# Ignore any blank lines or lines that start with a '#'
|
||||
#
|
||||
def prepare_exception_globs(exceptions_file, source_dir)
|
||||
source_path = File.expand_path(source_dir)
|
||||
globs = []
|
||||
File.open(exceptions_file) do |file|
|
||||
file.each do |line|
|
||||
glob = line.strip
|
||||
if (glob.length > 0) && (glob[0..0] != '#')
|
||||
globs << "#{source_path}/#{glob}".gsub('//', '/')
|
||||
end
|
||||
end
|
||||
end
|
||||
return globs
|
||||
end
|
||||
|
||||
# Recursively scan this directory, and copy if we are not scan-only.
|
||||
#
|
||||
def scan_dir(source_dir, target_dir)
|
||||
@stats.enter_directory(source_dir)
|
||||
|
||||
Dir.foreach(File.join(@source_dir, source_dir)) do |filename|
|
||||
source_path_relative = File.join(source_dir, filename)
|
||||
source_path = File.join(@source_dir, source_path_relative)
|
||||
target_path_relative = File.join(target_dir, filename)
|
||||
target_path = File.join(@target_dir, target_path_relative)
|
||||
|
||||
# What kind of beast is this?
|
||||
if filename == '.' || filename == '..'
|
||||
is_skipped_directory = true
|
||||
else
|
||||
if File.directory?(source_path)
|
||||
if (path_matches_skipped?(source_path_relative))
|
||||
is_skipped_directory = true
|
||||
else
|
||||
is_directory = true
|
||||
end
|
||||
else
|
||||
if filename_matches_pattern?(filename)
|
||||
if path_matches_exception?(source_path_relative)
|
||||
is_exception = true
|
||||
else
|
||||
is_match = true
|
||||
end
|
||||
else
|
||||
is_ignored = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if is_skipped_directory
|
||||
# do nothing
|
||||
elsif is_directory
|
||||
scan_dir(source_path_relative, target_path_relative)
|
||||
elsif is_match
|
||||
@stats.record_scan_matching(filename)
|
||||
scan_file(source_path, filename)
|
||||
elsif is_exception
|
||||
@stats.record_known_exception(filename)
|
||||
else # not a match
|
||||
@stats.record_scan_non_matching(filename)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Is this directory one of the skipped?
|
||||
#
|
||||
def path_matches_skipped?(relative_path)
|
||||
@skip_directories.each do |glob|
|
||||
return true if File.fnmatch(glob, relative_path)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
# Does this file path match any of the exceptions?
|
||||
#
|
||||
def path_matches_exception?(relative_path)
|
||||
path = File.expand_path(File.join(@source_dir, relative_path))
|
||||
@known_exceptions.each do |pattern|
|
||||
return true if File.fnmatch(pattern, path)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
# Does this filename match any of the patterns?
|
||||
#
|
||||
def filename_matches_pattern?(filename)
|
||||
@file_matchers.each do |pattern|
|
||||
return true if File.fnmatch(pattern, filename)
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
# This file should contain a license tag.
|
||||
#
|
||||
def scan_file(source_path, filename)
|
||||
found = 0
|
||||
File.open(source_path) do |source_file|
|
||||
source_file.each do |line|
|
||||
if line.include?(MAGIC_STRING)
|
||||
found += 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if found == 0
|
||||
@stats.record_no_tag(filename, source_path)
|
||||
elsif found == 1
|
||||
@stats.record_tag(filename)
|
||||
else
|
||||
raise("File contains #{found} license lines: #{source_path}")
|
||||
end
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------------
|
||||
public
|
||||
# ------------------------------------------------------------------------------------
|
||||
|
||||
# Setup and get ready to process.
|
||||
#
|
||||
# * properties is a map of keys to values, probably parsed from a properties file.
|
||||
#
|
||||
def initialize(properties)
|
||||
@file_match_list = properties['file_matchers']
|
||||
@skip_directories_list = properties['skip_directories']
|
||||
@report_level_string = properties['report_level']
|
||||
|
||||
# These properties contain paths, and if they are relative paths, they
|
||||
# should be relative to the properties file itself.
|
||||
@source_dir = relative_to_properties(properties, 'source_dir')
|
||||
@target_dir = relative_to_properties(properties, 'target_dir')
|
||||
|
||||
# These properties contain paths, and if they are relative paths, they
|
||||
# should be relative to the source_directory.
|
||||
@license_file = relative_to_source(properties, 'license_file')
|
||||
@known_exceptions_file = relative_to_source(properties, 'known_exceptions')
|
||||
|
||||
sanity_checks_on_parameters()
|
||||
|
||||
@full_report = @report_level_string === 'full'
|
||||
@short_report = @report_level_string === 'short'
|
||||
@file_matchers = @file_match_list.strip.split(/,\s*/)
|
||||
@skip_directories = @skip_directories_list.strip.split(/,\s*/)
|
||||
@known_exceptions = prepare_exception_globs(@known_exceptions_file, @source_dir)
|
||||
|
||||
@stats = LicenserStats.new(@source_dir, @file_matchers, @full_report)
|
||||
end
|
||||
|
||||
# Start the recursive scanning (and copying).
|
||||
def process()
|
||||
scan_dir('.', '.')
|
||||
end
|
||||
|
||||
# Report the summary statistics
|
||||
def report(properties)
|
||||
if (@short_report)
|
||||
tags = 0
|
||||
@stats.tagged_files.each {|line| tags += line[1] }
|
||||
known = 0
|
||||
@stats.known_exceptions.each {|line| known += line[1] }
|
||||
missing = 0
|
||||
@stats.missing_tags.each {|line| missing += line[1] }
|
||||
|
||||
puts "Licenser: scanned #{@stats.file_count} files in #{@stats.dir_count} directories."
|
||||
printf(" Licensed files: %5d\n", tags)
|
||||
printf(" Known exceptions: %5d\n", known)
|
||||
printf(" Missing tags: %5d\n", missing)
|
||||
else
|
||||
puts "Licenser: run completed at #{DateTime.now.strftime("%H:%M:%S on %b %d, %Y")}"
|
||||
puts " scanned #{@stats.file_count} files in #{@stats.dir_count} directories."
|
||||
puts
|
||||
puts 'Licensed files'
|
||||
@stats.tagged_files.sort.each do |line|
|
||||
printf("%5d %s\n", line[1], line[0])
|
||||
end
|
||||
puts
|
||||
puts 'Known non-licensed files'
|
||||
@stats.known_exceptions.sort.each do |line|
|
||||
printf("%5d %s\n", line[1], line[0])
|
||||
end
|
||||
puts
|
||||
puts 'Missing tags'
|
||||
@stats.missing_tags.sort.each do |line|
|
||||
printf("%5d %s\n", line[1], line[0])
|
||||
end
|
||||
puts
|
||||
puts 'properties:'
|
||||
properties.each do |key, value|
|
||||
puts " #{key} = #{value}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Were we successful or not?
|
||||
def success?
|
||||
return @stats.missing_tags.empty?
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
#
|
||||
# ------------------------------------------------------------------------------------
|
||||
# Standalone calling.
|
||||
#
|
||||
# Do this if this program was called from the command line. That is, if the command
|
||||
# expands to the path of this file.
|
||||
# ------------------------------------------------------------------------------------
|
||||
#
|
||||
|
||||
if File.expand_path($0) == File.expand_path(__FILE__)
|
||||
if ARGV.length == 0
|
||||
raise("No arguments - usage is: ruby licenser.rb <properties_file>")
|
||||
end
|
||||
if !File.file?(ARGV[0])
|
||||
raise "File does not exist: '#{ARGV[0]}'."
|
||||
end
|
||||
|
||||
properties = PropertyFileReader.read(ARGV[0])
|
||||
|
||||
l = Licenser.new(properties)
|
||||
l.process
|
||||
l.report(properties)
|
||||
|
||||
if l.success?
|
||||
puts "Licenser was successful."
|
||||
exit 0
|
||||
else
|
||||
puts "Licenser found problems."
|
||||
exit 1
|
||||
end
|
||||
end
|
95
legacy/utilities/licenser/licenser_stats.rb
Normal file
95
legacy/utilities/licenser/licenser_stats.rb
Normal file
|
@ -0,0 +1,95 @@
|
|||
=begin
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
Collect the statistics of a licenser run.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
=end
|
||||
|
||||
class LicenserStats
|
||||
attr_reader :tagged_files
|
||||
attr_reader :missing_tags
|
||||
attr_reader :known_exceptions
|
||||
attr_reader :file_count
|
||||
attr_reader :dir_count
|
||||
|
||||
# ------------------------------------------------------------------------------------
|
||||
private
|
||||
# ------------------------------------------------------------------------------------
|
||||
#
|
||||
def which_match(filename)
|
||||
@file_matchers.each do |matcher|
|
||||
return matcher if File.fnmatch(matcher, filename)
|
||||
end
|
||||
raise("filename matches no matchers!: #{filename}")
|
||||
end
|
||||
|
||||
# ------------------------------------------------------------------------------------
|
||||
public
|
||||
# ------------------------------------------------------------------------------------
|
||||
|
||||
def initialize(root_dir, file_matchers, full)
|
||||
@root_dir = "#{root_dir}/".gsub('//', '/')
|
||||
@file_matchers = file_matchers
|
||||
@full = full
|
||||
|
||||
# keep track of how many tags are found in all file types
|
||||
@tagged_files = Hash.new()
|
||||
file_matchers.each do |matcher|
|
||||
@tagged_files[matcher] = 0
|
||||
end
|
||||
|
||||
# keep track of missing tags, only in file types that have missing tags
|
||||
@missing_tags = Hash.new(0)
|
||||
|
||||
# keep track of how many known non-licensed files we encounter, and what types.
|
||||
@known_exceptions = Hash.new(0)
|
||||
|
||||
# keep track of how many files are copied
|
||||
@file_count = 0
|
||||
|
||||
#keep track of how many directories are copied
|
||||
@dir_count = 0
|
||||
end
|
||||
|
||||
def enter_directory(path)
|
||||
@dir_count += 1
|
||||
puts "Entering directory: #{path}" if @full
|
||||
end
|
||||
|
||||
def record_scan_non_matching(filename)
|
||||
@file_count += 1
|
||||
puts " Scan without mods: #{filename}" if @full
|
||||
end
|
||||
|
||||
def record_copy_non_matching(filename)
|
||||
@file_count += 1
|
||||
puts " Copy without mods: #{filename}" if @full
|
||||
end
|
||||
|
||||
def record_scan_matching(filename)
|
||||
@file_count += 1
|
||||
puts " Scan with mods: #{filename}" if @full
|
||||
end
|
||||
|
||||
def record_copy_matching(filename)
|
||||
@file_count += 1
|
||||
puts " Copy with mods: #{filename}" if @full
|
||||
end
|
||||
|
||||
def record_known_exception(filename)
|
||||
@file_count += 1
|
||||
puts " Known exception: #{filename}" if @full
|
||||
@known_exceptions[which_match(filename)] += 1
|
||||
end
|
||||
|
||||
def record_tag(filename)
|
||||
puts " Found license tag into #{filename}" if @full
|
||||
@tagged_files[which_match(filename)] += 1
|
||||
end
|
||||
|
||||
def record_no_tag(filename, source_path)
|
||||
puts "ERROR: Found no license tag in #{source_path.sub(@root_dir, '')}"
|
||||
@missing_tags[which_match(filename)] += 1
|
||||
end
|
||||
end
|
39
legacy/utilities/licenser/property_file_reader.rb
Normal file
39
legacy/utilities/licenser/property_file_reader.rb
Normal file
|
@ -0,0 +1,39 @@
|
|||
=begin
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
A utility class that reads a properties file and returns a hash containing the
|
||||
properties.
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
=end
|
||||
|
||||
class PropertyFileReader
|
||||
# Read a properties file and return a hash.
|
||||
#
|
||||
# Parameters: the path to the properties file
|
||||
#
|
||||
# The hash includes the special property "properties_file_path", which holds
|
||||
# the path to the properties file.
|
||||
#
|
||||
def self.read(file_path)
|
||||
properties = {}
|
||||
properties["properties_file_path"] = File.expand_path(file_path)
|
||||
|
||||
File.open(file_path) do |file|
|
||||
file.each_line do |line|
|
||||
line.strip!
|
||||
if line.length == 0 || line[0] == ?# || line[0] == ?!
|
||||
# ignore blank lines, and lines starting with '#' or '!'.
|
||||
elsif line =~ /(.*?)\s*[=:]\s*(.*)/
|
||||
# key and value are separated by '=' or ':' and optional whitespace.
|
||||
properties[$1.strip] = $2
|
||||
else
|
||||
# No '=' or ':' means that the value is empty.
|
||||
properties[line] = ''
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return properties
|
||||
end
|
||||
end
|
134
legacy/utilities/rdbmigration/build.xml
Normal file
134
legacy/utilities/rdbmigration/build.xml
Normal file
|
@ -0,0 +1,134 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||
|
||||
<!-- ======================================================================
|
||||
A tool to migrate RDB data into TDB.
|
||||
====================================================================== -->
|
||||
|
||||
<project name="RDB-migration" default="describe">
|
||||
|
||||
<property name="working.dir" location=".work" />
|
||||
<property name="src.dir" location="src" />
|
||||
<property name="lib.dir" location="lib" />
|
||||
|
||||
<!-- =================================
|
||||
target: describe
|
||||
================================= -->
|
||||
<target name="describe"
|
||||
description="--> Describe the targets (this is the default).">
|
||||
<echo>
|
||||
all - Compiles and runs the RDB migration tool.
|
||||
</echo>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: all
|
||||
================================= -->
|
||||
<target name="all"
|
||||
depends="clean, run"
|
||||
description="Build from scratch and run the migration.">
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: clean
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="clean">
|
||||
<delete dir="${working.dir}" />
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: setup
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="setup" depends="getBuildProperties, getRuntimeProperties">
|
||||
<mkdir dir="${working.dir}" />
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: getBuildProperties
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="getBuildProperties">
|
||||
<property name="vivo.distribution.dir" location="../.." />
|
||||
<property name="build.properties.file"
|
||||
location="${vivo.distribution.dir}/build.properties" />
|
||||
<fail message="You must create a "${build.properties.file}" file.">
|
||||
<condition>
|
||||
<not>
|
||||
<available file="${build.properties.file}" />
|
||||
</not>
|
||||
</condition>
|
||||
</fail>
|
||||
|
||||
<property file="${build.properties.file}" />
|
||||
<fail unless="vitro.core.dir"
|
||||
message="${build.properties.file} must contain a value for vitro.core.dir" />
|
||||
<fail unless="vitro.home"
|
||||
message="${build.properties.file} must contain a value for vitro.home" />
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: getRuntimeProperties
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="getRuntimeProperties">
|
||||
<property name="runtime.properties.file"
|
||||
location="${vitro.home}/runtime.properties" />
|
||||
<fail message="You must create a "${runtime.properties.file}" file.">
|
||||
<condition>
|
||||
<not>
|
||||
<available file="${runtime.properties.file}" />
|
||||
</not>
|
||||
</condition>
|
||||
</fail>
|
||||
|
||||
<property file="${runtime.properties.file}" />
|
||||
<fail unless="VitroConnection.DataSource.url"
|
||||
message="${runtime.properties.file} must contain a value for VitroConnection.DataSource.url" />
|
||||
<fail unless="VitroConnection.DataSource.username"
|
||||
message="${runtime.properties.file} must contain a value for VitroConnection.DataSource.username" />
|
||||
<fail unless="VitroConnection.DataSource.password"
|
||||
message="${runtime.properties.file} must contain a value for VitroConnection.DataSource.password" />
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: compile
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="compile" depends="setup">
|
||||
<path id="main.compile.classpath">
|
||||
<fileset dir="${lib.dir}" includes="*.jar" />
|
||||
</path>
|
||||
|
||||
<javac srcdir="${src.dir}"
|
||||
destdir="${working.dir}"
|
||||
debug="true"
|
||||
deprecation="true"
|
||||
encoding="UTF8"
|
||||
includeantruntime="false"
|
||||
optimize="true"
|
||||
source="1.7">
|
||||
<classpath refid="main.compile.classpath" />
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: run
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="run" depends="compile">
|
||||
<path id="migrate.run.classpath">
|
||||
<pathelement location="${working.dir}" />
|
||||
<path refid="main.compile.classpath" />
|
||||
</path>
|
||||
|
||||
<java classname="edu.cornell.mannlib.vivo.utilities.rdbmigration.RdbMigrator"
|
||||
fork="no"
|
||||
failonerror="true">
|
||||
<arg value="${vitro.home}" />
|
||||
<arg value="${VitroConnection.DataSource.url}" />
|
||||
<arg value="${VitroConnection.DataSource.username}" />
|
||||
<arg value="${VitroConnection.DataSource.password}" />
|
||||
<jvmarg value="-Xms512m"/>
|
||||
<jvmarg value="-Xmx2048m"/>
|
||||
<classpath refid="migrate.run.classpath" />
|
||||
</java>
|
||||
</target>
|
||||
|
||||
</project>
|
BIN
legacy/utilities/rdbmigration/lib/arq-2.8.5.jar
Normal file
BIN
legacy/utilities/rdbmigration/lib/arq-2.8.5.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/rdbmigration/lib/commons-lang-2.6.jar
Normal file
BIN
legacy/utilities/rdbmigration/lib/commons-lang-2.6.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/rdbmigration/lib/icu4j-3.4.4.jar
Normal file
BIN
legacy/utilities/rdbmigration/lib/icu4j-3.4.4.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/rdbmigration/lib/iri-0.8.jar
Normal file
BIN
legacy/utilities/rdbmigration/lib/iri-0.8.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/rdbmigration/lib/jena-2.6.4.jar
Normal file
BIN
legacy/utilities/rdbmigration/lib/jena-2.6.4.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/rdbmigration/lib/log4j-1.2.14.jar
Normal file
BIN
legacy/utilities/rdbmigration/lib/log4j-1.2.14.jar
Normal file
Binary file not shown.
Binary file not shown.
BIN
legacy/utilities/rdbmigration/lib/slf4j-api-1.5.6.jar
Normal file
BIN
legacy/utilities/rdbmigration/lib/slf4j-api-1.5.6.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/rdbmigration/lib/slf4j-log4j12-1.5.6.jar
Normal file
BIN
legacy/utilities/rdbmigration/lib/slf4j-log4j12-1.5.6.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/rdbmigration/lib/tdb-0.8.7.jar
Normal file
BIN
legacy/utilities/rdbmigration/lib/tdb-0.8.7.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/rdbmigration/lib/xercesImpl.jar
Normal file
BIN
legacy/utilities/rdbmigration/lib/xercesImpl.jar
Normal file
Binary file not shown.
|
@ -0,0 +1,362 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vivo.utilities.rdbmigration;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DatabaseMetaData;
|
||||
import java.sql.Date;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.PreparedStatement;
|
||||
import java.sql.ResultSet;
|
||||
import java.sql.SQLException;
|
||||
import java.sql.Statement;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
import com.hp.hpl.jena.db.DBConnection;
|
||||
import com.hp.hpl.jena.db.GraphRDB;
|
||||
import com.hp.hpl.jena.graph.Node;
|
||||
import com.hp.hpl.jena.query.Dataset;
|
||||
import com.hp.hpl.jena.sparql.core.DatasetGraph;
|
||||
import com.hp.hpl.jena.tdb.TDBFactory;
|
||||
|
||||
/**
|
||||
* A free-standing application that walks the user through the process of
|
||||
* copying VIVO configuration data from RDB to TDB.
|
||||
*/
|
||||
public class RdbMigrator {
|
||||
private static final String DIRECTORY_TDB = "tdbModels";
|
||||
private static final String TABLE_RDB = "jena_graph";
|
||||
private static final String TABLE_MIGRATED = "vivo_rdb_migrated";
|
||||
private static final List<String> EXPECTED_MODELS = Arrays
|
||||
.asList(new String[] {
|
||||
"http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadata",
|
||||
"http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadata-displayModel",
|
||||
"http://vitro.mannlib.cornell.edu/default/vitro-kb-displayMetadataTBOX",
|
||||
"http://vitro.mannlib.cornell.edu/default/vitro-kb-userAccounts" });
|
||||
|
||||
private final String vivoHomeDir;
|
||||
private final String jdbcUrl;
|
||||
private final String username;
|
||||
private final String password;
|
||||
|
||||
private File targetDir;
|
||||
private boolean alreadyMigrated;
|
||||
private List<String> modelsToCopy;
|
||||
|
||||
/**
|
||||
* Confirm all of the parameters. Ask the user for approval. Do the
|
||||
* migration.
|
||||
*
|
||||
* @throws UserDeclinedException
|
||||
* If the user decides not to continue.
|
||||
*/
|
||||
public RdbMigrator(String vivoHomeDir, String jdbcUrl, String username,
|
||||
String password) throws UserDeclinedException, IOException,
|
||||
SQLException {
|
||||
this.vivoHomeDir = vivoHomeDir;
|
||||
this.jdbcUrl = jdbcUrl;
|
||||
this.username = username;
|
||||
this.password = password;
|
||||
|
||||
testDbConnection();
|
||||
|
||||
checkThatRdbExists();
|
||||
|
||||
confirmTargetDirectory();
|
||||
|
||||
if (doesTdbExist()) {
|
||||
askContinueOverTdb();
|
||||
}
|
||||
|
||||
if (isAlreadyMigrated()) {
|
||||
askMigrateAgain();
|
||||
}
|
||||
|
||||
getListOfRdbModels();
|
||||
|
||||
if (isUnexpectedModels()) {
|
||||
askMigrateAllModels();
|
||||
}
|
||||
|
||||
askApprovalForMigrationPlan();
|
||||
|
||||
migrate();
|
||||
}
|
||||
|
||||
private void testDbConnection() {
|
||||
try (Connection conn = getSqlConnection()) {
|
||||
// Just open and close it.
|
||||
} catch (SQLException e) {
|
||||
quit("Can't log in to database: '" + jdbcUrl + "', '" + username
|
||||
+ "', '" + password + "'\n" + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
private void checkThatRdbExists() throws SQLException {
|
||||
try (Connection conn = getSqlConnection()) {
|
||||
DatabaseMetaData md = conn.getMetaData();
|
||||
try (ResultSet rs = md.getTables(null, null, TABLE_RDB, null);) {
|
||||
if (!rs.next()) {
|
||||
quit("The database at '" + jdbcUrl
|
||||
+ "' contains no RDB tables.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void confirmTargetDirectory() {
|
||||
File vivoHome = new File(vivoHomeDir);
|
||||
if (!vivoHome.isDirectory()) {
|
||||
quit("'" + vivoHome + "' is not a directory.");
|
||||
}
|
||||
if (!vivoHome.canWrite()) {
|
||||
quit("Can't write to '" + vivoHome + "'.");
|
||||
}
|
||||
targetDir = new File(vivoHome, DIRECTORY_TDB);
|
||||
}
|
||||
|
||||
private boolean doesTdbExist() {
|
||||
if (!targetDir.exists()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!targetDir.isDirectory()) {
|
||||
quit("'" + targetDir + "' is not a directory.");
|
||||
}
|
||||
if (!targetDir.canWrite()) {
|
||||
quit("Can't write to '" + targetDir + "'.");
|
||||
}
|
||||
|
||||
String[] filenames = targetDir.list();
|
||||
if (filenames == null || filenames.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void askContinueOverTdb() throws UserDeclinedException, IOException {
|
||||
ask("A directory of TDB files exists at '" + targetDir + "'.\n"
|
||||
+ " Migration will replace the existing triples.\n"
|
||||
+ "Continue? (y/n)");
|
||||
}
|
||||
|
||||
private boolean isAlreadyMigrated() throws SQLException {
|
||||
try (Connection conn = getSqlConnection()) {
|
||||
DatabaseMetaData md = conn.getMetaData();
|
||||
try (ResultSet rs = md.getTables(null, null, TABLE_MIGRATED, null);) {
|
||||
if (rs.next()) {
|
||||
alreadyMigrated = true;
|
||||
announceMigrationDate(conn);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void announceMigrationDate(Connection conn) {
|
||||
String migrationDate = "UNKNOWN DATE";
|
||||
String query = String.format("SELECT date FROM %s LIMIT 1",
|
||||
TABLE_MIGRATED);
|
||||
|
||||
try (Statement stmt = conn.createStatement();
|
||||
java.sql.ResultSet rs = stmt.executeQuery(query)) {
|
||||
if (rs.next()) {
|
||||
migrationDate = rs.getString("DATE");
|
||||
}
|
||||
} catch (SQLException e) {
|
||||
// go with default answer.
|
||||
}
|
||||
|
||||
System.out.println("It looks like this RDB data has already been "
|
||||
+ "migrated to TDB, on " + migrationDate
|
||||
+ "\n (found a table named '" + TABLE_MIGRATED + "')");
|
||||
}
|
||||
|
||||
private void askMigrateAgain() throws UserDeclinedException, IOException {
|
||||
ask("Migrate again? (y/n)");
|
||||
}
|
||||
|
||||
private void getListOfRdbModels() throws SQLException {
|
||||
try (Connection conn = getSqlConnection();
|
||||
ClosingDBConnection rdb = new ClosingDBConnection(conn)) {
|
||||
modelsToCopy = rdb.getAllModelNames().toList();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUnexpectedModels() {
|
||||
List<String> unexpectedModels = new ArrayList<>(modelsToCopy);
|
||||
unexpectedModels.removeAll(EXPECTED_MODELS);
|
||||
if (unexpectedModels.isEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
System.out
|
||||
.println("VIVO requires only these models from RDB:\n "
|
||||
+ StringUtils.join(EXPECTED_MODELS, "\n ")
|
||||
+ "\nYour RDB triple-store contains these additional models:\n "
|
||||
+ StringUtils.join(unexpectedModels, "\n "));
|
||||
return true;
|
||||
}
|
||||
|
||||
private void askMigrateAllModels() throws IOException, UserDeclinedException {
|
||||
System.out.println("Migrate all models, or only the required models?\n"
|
||||
+ " Enter 'all' or 'only', or anything else to quit.");
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
|
||||
String s = br.readLine();
|
||||
if (s != null) {
|
||||
if (s.trim().toLowerCase().equals("all")) {
|
||||
return;
|
||||
} else if (s.trim().toLowerCase().equals("only")) {
|
||||
modelsToCopy = EXPECTED_MODELS;
|
||||
return;
|
||||
}
|
||||
}
|
||||
throw new UserDeclinedException("Enter 'all' or 'only'.");
|
||||
}
|
||||
|
||||
private void askApprovalForMigrationPlan() throws SQLException,
|
||||
UserDeclinedException, IOException {
|
||||
int modelCount = 0;
|
||||
int tripleCount = 0;
|
||||
try (Connection conn = getSqlConnection();
|
||||
ClosingDBConnection rdb = new ClosingDBConnection(conn)) {
|
||||
for (String modelName : modelsToCopy) {
|
||||
modelCount++;
|
||||
try (ClosingGraphRDB graph = new ClosingGraphRDB(rdb, modelName)) {
|
||||
tripleCount += graph.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
String warning = alreadyMigrated ? " Existing triples will be over-written.\n"
|
||||
: "";
|
||||
String question = String.format("Migrating %d triples in %d models "
|
||||
+ "to TDB files in '%s'\n%sContinue? (y/n)", tripleCount,
|
||||
modelCount, targetDir, warning);
|
||||
ask(question);
|
||||
}
|
||||
|
||||
private void quit(String message) {
|
||||
throw new IllegalArgumentException(
|
||||
"--------------------------------------------------------------\n"
|
||||
+ message
|
||||
+ "\n--------------------------------------------------------------");
|
||||
}
|
||||
|
||||
private void ask(String string) throws UserDeclinedException, IOException {
|
||||
System.out.println(string);
|
||||
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
|
||||
String s = br.readLine();
|
||||
if ((s == null) || (!s.trim().toLowerCase().equals("y"))) {
|
||||
throw new UserDeclinedException("OK.");
|
||||
}
|
||||
}
|
||||
|
||||
private static class UserDeclinedException extends Exception {
|
||||
public UserDeclinedException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public void migrate() throws SQLException {
|
||||
copyData();
|
||||
writeMigrationRecord();
|
||||
}
|
||||
|
||||
private void copyData() throws SQLException {
|
||||
try (Connection conn = getSqlConnection();
|
||||
ClosingDBConnection rdb = new ClosingDBConnection(conn)) {
|
||||
Dataset tdbDataset = TDBFactory.createDataset(targetDir
|
||||
.getAbsolutePath());
|
||||
copyGraphs(rdb, tdbDataset);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void copyGraphs(ClosingDBConnection rdb, Dataset tdbDataset) {
|
||||
DatasetGraph tdbDsGraph = tdbDataset.asDatasetGraph();
|
||||
for (String modelName : modelsToCopy) {
|
||||
try (ClosingGraphRDB graph = new ClosingGraphRDB(rdb, modelName)) {
|
||||
tdbDsGraph.addGraph(Node.createURI(modelName), graph);
|
||||
System.out
|
||||
.println(String.format(" copied %4d triples from %s",
|
||||
graph.size(), modelName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeMigrationRecord() throws SQLException {
|
||||
String createTable = String.format("CREATE TABLE %s (date DATE)",
|
||||
TABLE_MIGRATED);
|
||||
String deleteOldDates = String.format("DELETE FROM %s", TABLE_MIGRATED);
|
||||
String insertDate = String.format("INSERT INTO %s (date) VALUES (?)",
|
||||
TABLE_MIGRATED);
|
||||
try (Connection conn = getSqlConnection();
|
||||
Statement stmt = conn.createStatement();
|
||||
PreparedStatement pstmt = conn.prepareStatement(insertDate)) {
|
||||
if (alreadyMigrated) {
|
||||
stmt.executeUpdate(deleteOldDates);
|
||||
} else {
|
||||
stmt.executeUpdate(createTable);
|
||||
}
|
||||
pstmt.setDate(1, new Date(System.currentTimeMillis()));
|
||||
pstmt.executeUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
private Connection getSqlConnection() throws SQLException {
|
||||
Properties connectionProps;
|
||||
connectionProps = new Properties();
|
||||
connectionProps.put("user", username);
|
||||
connectionProps.put("password", password);
|
||||
return DriverManager.getConnection(jdbcUrl, connectionProps);
|
||||
}
|
||||
|
||||
private static class ClosingDBConnection extends DBConnection implements
|
||||
AutoCloseable {
|
||||
ClosingDBConnection(Connection sqlConnection) {
|
||||
super(sqlConnection, "MySQL");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private static class ClosingGraphRDB extends GraphRDB implements
|
||||
AutoCloseable {
|
||||
ClosingGraphRDB(DBConnection rdb, String modelName) {
|
||||
super(rdb, modelName, null,
|
||||
GraphRDB.OPTIMIZE_ALL_REIFICATIONS_AND_HIDE_NOTHING, false);
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Main routine
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public static void main(String[] args) {
|
||||
if (args.length != 4) {
|
||||
System.out.println("Usage: RdbMigrator vivoHomeDir, jdbcUrl, "
|
||||
+ "username, password");
|
||||
}
|
||||
|
||||
try {
|
||||
new RdbMigrator(args[0], args[1], args[2], args[3]);
|
||||
} catch (IllegalArgumentException | UserDeclinedException e) {
|
||||
System.out.println(e.getMessage());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
19
legacy/utilities/sdb_to_tdb/README.txt
Normal file
19
legacy/utilities/sdb_to_tdb/README.txt
Normal file
|
@ -0,0 +1,19 @@
|
|||
A tool to convert triples from SDB to TDB.
|
||||
|
||||
Unlike the RDF-migration tool, this tool is not tied to the VIVO
|
||||
properties files. Instead, you must provide:
|
||||
* The URL for the SDB, including username and password parameters
|
||||
* The directory path for the TDB.
|
||||
The directory must exist.
|
||||
The directory must be empty, unless the "force" optiopn is used.
|
||||
|
||||
Examples:
|
||||
|
||||
java -jar sdb2tdb.jar \
|
||||
jdbc:mysql://localhost/vitrodb?user=vivoUser&password=vivoPass \
|
||||
/usr/local/my/tdb
|
||||
|
||||
java -Xms512m -Xmx4096m -jar .work/sdb2tdb.jar \
|
||||
'jdbc:mysql://localhost/weill17?user=vivoUser&password=vivoPass' \
|
||||
/Users/jeb228/Testing/instances/weill-develop/vivo_home/contentTdb \
|
||||
force
|
89
legacy/utilities/sdb_to_tdb/build.xml
Normal file
89
legacy/utilities/sdb_to_tdb/build.xml
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||
|
||||
<!-- ======================================================================
|
||||
A tool to convert SDB data into TDB.
|
||||
====================================================================== -->
|
||||
|
||||
<project name="SDB-to-TDB" default="describe">
|
||||
|
||||
<property name="working.dir" location=".work" />
|
||||
<property name="src.dir" location="src" />
|
||||
<property name="lib.dir" location="lib" />
|
||||
<property name="classes.dir" location="${working.dir}/classes" />
|
||||
<property name="jar.file" location="${working.dir}/sdb2tdb.jar" />
|
||||
|
||||
<!-- =================================
|
||||
target: describe
|
||||
================================= -->
|
||||
<target name="describe"
|
||||
description="--> Describe the targets (this is the default).">
|
||||
<echo>
|
||||
all - Compiles the tool ad packs it into a JAR
|
||||
</echo>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: all
|
||||
================================= -->
|
||||
<target name="all"
|
||||
depends="clean, jar"
|
||||
description="Build from scratch and run the migration.">
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: clean
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="clean">
|
||||
<delete dir="${working.dir}" />
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: prepare
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="prepare">
|
||||
<mkdir dir="${working.dir}" />
|
||||
<mkdir dir="${classes.dir}" />
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: compile
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="compile" depends="prepare">
|
||||
<path id="main.compile.classpath">
|
||||
<fileset dir="${lib.dir}" includes="*.jar" />
|
||||
</path>
|
||||
|
||||
<javac srcdir="${src.dir}"
|
||||
destdir="${classes.dir}"
|
||||
debug="true"
|
||||
deprecation="true"
|
||||
encoding="UTF8"
|
||||
includeantruntime="false"
|
||||
optimize="true"
|
||||
source="1.7">
|
||||
<classpath refid="main.compile.classpath" />
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: jar
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="jar" depends="compile">
|
||||
<jar destfile="${jar.file}">
|
||||
<manifest>
|
||||
<attribute name="Main-Class"
|
||||
value="edu.cornell.mannlib.vitro.utilities.sdb2tdb.Sdb2Tdb" />
|
||||
</manifest>
|
||||
<fileset dir="${classes.dir}" />
|
||||
<archives>
|
||||
<zips>
|
||||
<fileset dir="${lib.dir}" includes="**/*.jar" />
|
||||
</zips>
|
||||
</archives>
|
||||
</jar>
|
||||
</target>
|
||||
|
||||
</project>
|
||||
|
BIN
legacy/utilities/sdb_to_tdb/lib/jena-arq-2.10.1.jar
Normal file
BIN
legacy/utilities/sdb_to_tdb/lib/jena-arq-2.10.1.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/sdb_to_tdb/lib/jena-core-2.10.1.jar
Normal file
BIN
legacy/utilities/sdb_to_tdb/lib/jena-core-2.10.1.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/sdb_to_tdb/lib/jena-iri-0.9.6.jar
Normal file
BIN
legacy/utilities/sdb_to_tdb/lib/jena-iri-0.9.6.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/sdb_to_tdb/lib/jena-sdb-1.3.6.jar
Normal file
BIN
legacy/utilities/sdb_to_tdb/lib/jena-sdb-1.3.6.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/sdb_to_tdb/lib/jena-tdb-0.10.0.jar
Normal file
BIN
legacy/utilities/sdb_to_tdb/lib/jena-tdb-0.10.0.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/sdb_to_tdb/lib/log4j-1.2.16.jar
Normal file
BIN
legacy/utilities/sdb_to_tdb/lib/log4j-1.2.16.jar
Normal file
Binary file not shown.
Binary file not shown.
BIN
legacy/utilities/sdb_to_tdb/lib/slf4j-api-1.6.6.jar
Normal file
BIN
legacy/utilities/sdb_to_tdb/lib/slf4j-api-1.6.6.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/sdb_to_tdb/lib/slf4j-log4j12-1.6.6.jar
Normal file
BIN
legacy/utilities/sdb_to_tdb/lib/slf4j-log4j12-1.6.6.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/sdb_to_tdb/lib/xercesImpl-2.11.0.jar
Normal file
BIN
legacy/utilities/sdb_to_tdb/lib/xercesImpl-2.11.0.jar
Normal file
Binary file not shown.
BIN
legacy/utilities/sdb_to_tdb/lib/xml-apis-1.4.01.jar
Normal file
BIN
legacy/utilities/sdb_to_tdb/lib/xml-apis-1.4.01.jar
Normal file
Binary file not shown.
|
@ -0,0 +1,291 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.sdb2tdb;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.FilenameFilter;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.sql.Connection;
|
||||
import java.sql.DriverManager;
|
||||
import java.sql.SQLException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.jena.riot.RDFDataMgr;
|
||||
|
||||
import com.hp.hpl.jena.graph.Node;
|
||||
import com.hp.hpl.jena.graph.Triple;
|
||||
import com.hp.hpl.jena.query.Dataset;
|
||||
import com.hp.hpl.jena.query.Query;
|
||||
import com.hp.hpl.jena.query.QueryExecution;
|
||||
import com.hp.hpl.jena.query.QueryExecutionFactory;
|
||||
import com.hp.hpl.jena.query.QueryFactory;
|
||||
import com.hp.hpl.jena.query.QuerySolution;
|
||||
import com.hp.hpl.jena.query.ResultSet;
|
||||
import com.hp.hpl.jena.rdf.model.Model;
|
||||
import com.hp.hpl.jena.sdb.SDB;
|
||||
import com.hp.hpl.jena.sdb.SDBFactory;
|
||||
import com.hp.hpl.jena.sdb.Store;
|
||||
import com.hp.hpl.jena.sdb.StoreDesc;
|
||||
import com.hp.hpl.jena.sdb.store.DatabaseType;
|
||||
import com.hp.hpl.jena.sdb.store.LayoutType;
|
||||
import com.hp.hpl.jena.tdb.TDBFactory;
|
||||
|
||||
/**
|
||||
* Copy all of the data from an SDB triple-store to a TDB triple-store. See
|
||||
* README.txt for more details.
|
||||
*
|
||||
* Examples of invoking it:
|
||||
*
|
||||
* <pre>
|
||||
* java -jar sdb2tdb.jar \
|
||||
* 'jdbc:mysql://localhost/vitrodb?user=vivoUser&password=vivoPass'\
|
||||
* /usr/local/my/tdb
|
||||
*
|
||||
* java -Xms2048m -Xmx2048m -jar .work/sdb2tdb.jar \
|
||||
* 'jdbc:mysql://localhost/weill17?user=vivoUser&password=vivoPass' \
|
||||
* /Users/jeb228/Testing/instances/weill-develop/vivo_home/contentTdb \
|
||||
* force
|
||||
* </pre>
|
||||
*
|
||||
* Each graph is copied separately. Small graphs are simply loaded into memory
|
||||
* and transferred. Large graphs are read to produce a streaming result set
|
||||
* which is written to a temporary file. That file is then read into a TDB
|
||||
* model.
|
||||
*
|
||||
* This has been tested with graphs up to 6 million triples without crashing.
|
||||
*/
|
||||
public class Sdb2Tdb {
|
||||
private static final int LARGE_MODEL_THRESHOLD = 500_000;
|
||||
private final String driverClassName;
|
||||
private final String jdbcUrl;
|
||||
private final String destination;
|
||||
private final boolean force;
|
||||
|
||||
private Dataset sdbDataset;
|
||||
private Dataset tdbDataset;
|
||||
|
||||
public Sdb2Tdb(List<String> hardArgs) throws UsageException {
|
||||
List<String> args = new ArrayList<>(hardArgs);
|
||||
|
||||
if (!args.isEmpty() && args.indexOf("force") == (args.size() - 1)) {
|
||||
this.force = true;
|
||||
args.remove(args.size() - 1);
|
||||
} else {
|
||||
this.force = false;
|
||||
}
|
||||
|
||||
if (args.size() == 3) {
|
||||
this.driverClassName = args.remove(0);
|
||||
} else if (args.size() == 2) {
|
||||
this.driverClassName = "com.mysql.jdbc.Driver";
|
||||
} else {
|
||||
throw new UsageException("Wrong number of arguments: "
|
||||
+ hardArgs.size());
|
||||
}
|
||||
|
||||
this.jdbcUrl = args.get(0);
|
||||
this.destination = args.get(1);
|
||||
|
||||
checkDriverClass();
|
||||
checkJdbcUrl();
|
||||
checkDestination();
|
||||
}
|
||||
|
||||
private void checkDriverClass() throws UsageException {
|
||||
try {
|
||||
Class.forName(this.driverClassName).newInstance();
|
||||
} catch (Exception e) {
|
||||
throw new UsageException("Can't instantiate JDBC driver: "
|
||||
+ this.driverClassName);
|
||||
}
|
||||
}
|
||||
|
||||
private void checkJdbcUrl() {
|
||||
if ((!this.jdbcUrl.matches("\\buser\\b"))
|
||||
|| (!this.jdbcUrl.matches("\\bpassword\\b"))) {
|
||||
System.out.println("\nWARNING: The JDBC url probably should "
|
||||
+ "contain values for user and password.\n");
|
||||
}
|
||||
}
|
||||
|
||||
private void checkDestination() throws UsageException {
|
||||
File destDir = new File(this.destination);
|
||||
|
||||
if (!destDir.isDirectory()) {
|
||||
throw new UsageException(
|
||||
"The destination directory does not exist: '"
|
||||
+ this.destination + "'");
|
||||
}
|
||||
|
||||
if (!destDir.canWrite()) {
|
||||
throw new UsageException("Cannot write to '" + this.destination
|
||||
+ "'");
|
||||
}
|
||||
|
||||
if (!(this.force || getDestinationFilenames().isEmpty())) {
|
||||
throw new UsageException("The destination directory is not empty. "
|
||||
+ "Choose another destination, or use the 'force' option");
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> getDestinationFilenames() {
|
||||
File destDir = new File(this.destination);
|
||||
String[] filenames = destDir.list(new FilenameFilter() {
|
||||
@Override
|
||||
public boolean accept(File dir, String name) {
|
||||
return !(name.equals(".") || name.equals(".."));
|
||||
}
|
||||
});
|
||||
return Arrays.asList(filenames);
|
||||
}
|
||||
|
||||
private void translate() throws SQLException, IOException {
|
||||
try {
|
||||
sdbDataset = openSdbDataset();
|
||||
tdbDataset = openTdbDataset();
|
||||
copyGraphs();
|
||||
} finally {
|
||||
if (tdbDataset != null) {
|
||||
tdbDataset.close();
|
||||
}
|
||||
if (sdbDataset != null) {
|
||||
sdbDataset.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Dataset openSdbDataset() throws SQLException {
|
||||
Connection conn = DriverManager.getConnection(this.jdbcUrl);
|
||||
Store store = SDBFactory.connectStore(conn, makeSdbStoreDesc());
|
||||
|
||||
SDB.getContext().set(SDB.jdbcStream, Boolean.TRUE);
|
||||
SDB.getContext().set(SDB.jdbcFetchSize, Integer.MIN_VALUE);
|
||||
|
||||
return SDBFactory.connectDataset(store);
|
||||
}
|
||||
|
||||
private StoreDesc makeSdbStoreDesc() {
|
||||
return new StoreDesc(LayoutType.LayoutTripleNodesHash,
|
||||
DatabaseType.MySQL);
|
||||
}
|
||||
|
||||
private Dataset openTdbDataset() {
|
||||
return TDBFactory.createDataset(new File(this.destination)
|
||||
.getAbsolutePath());
|
||||
}
|
||||
|
||||
private void copyGraphs() throws IOException {
|
||||
for (Iterator<String> modelNames = sdbDataset.listNames(); modelNames
|
||||
.hasNext();) {
|
||||
String modelName = modelNames.next();
|
||||
Model model = sdbDataset.getNamedModel(modelName);
|
||||
if (model.size() < LARGE_MODEL_THRESHOLD) {
|
||||
copySmallModel(modelName, model);
|
||||
} else {
|
||||
copyLargeModel(modelName, model);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void copySmallModel(String modelName, Model model) {
|
||||
System.out.println(String.format("Copying %6d triples: %s",
|
||||
model.size(), modelName));
|
||||
tdbDataset.addNamedModel(modelName, model);
|
||||
}
|
||||
|
||||
private void copyLargeModel(String modelName, Model model)
|
||||
throws IOException {
|
||||
File tempFile = File.createTempFile("sdb-", ".n3");
|
||||
System.out.println(String.format("Copying %6d triples: %s %s",
|
||||
model.size(), modelName, tempFile.getAbsolutePath()));
|
||||
model.close();
|
||||
|
||||
try (OutputStream os = new FileOutputStream(tempFile);
|
||||
GraphToTriples trips = new GraphToTriples(this, modelName)) {
|
||||
RDFDataMgr.writeTriples(os, trips);
|
||||
}
|
||||
System.out.println("Wrote it.");
|
||||
|
||||
try (InputStream is = new FileInputStream(tempFile)) {
|
||||
tdbDataset.getNamedModel(modelName).read(is, null, "N-TRIPLE");
|
||||
}
|
||||
System.out.println("Read it.");
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
try {
|
||||
Sdb2Tdb sdb2tdb = new Sdb2Tdb(Arrays.asList(args));
|
||||
sdb2tdb.translate();
|
||||
|
||||
} catch (UsageException e) {
|
||||
System.out.println();
|
||||
System.out.println(e.getMessage());
|
||||
System.out.println(e.getProperUsage());
|
||||
System.out.println();
|
||||
} catch (SQLException | IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
private static class UsageException extends Exception {
|
||||
public UsageException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public String getProperUsage() {
|
||||
return "Usage is: java -jar sdb2tdb [driver_class] <jdbcUrl> <destination_directory> [force]";
|
||||
}
|
||||
}
|
||||
|
||||
private static class GraphToTriples implements Iterator<Triple>,
|
||||
AutoCloseable {
|
||||
private static final String QUERY_TEMPLATE = "" //
|
||||
+ "SELECT ?s ?p ?o \n" //
|
||||
+ "WHERE { \n" //
|
||||
+ " GRAPH <%s> { \n" //
|
||||
+ " ?s ?p ?o . \n" //
|
||||
+ " } \n" //
|
||||
+ "}";
|
||||
|
||||
private final QueryExecution qe;
|
||||
private final ResultSet results;
|
||||
|
||||
GraphToTriples(Sdb2Tdb parent, String graphUri) {
|
||||
String qStr = String.format(QUERY_TEMPLATE, graphUri);
|
||||
Query q = QueryFactory.create(qStr);
|
||||
qe = QueryExecutionFactory.create(q, parent.sdbDataset);
|
||||
results = qe.execSelect();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasNext() {
|
||||
return results.hasNext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Triple next() {
|
||||
QuerySolution solution = results.nextSolution();
|
||||
Node s = solution.get("s").asNode();
|
||||
Node p = solution.get("p").asNode();
|
||||
Node o = solution.get("o").asNode();
|
||||
return new Triple(s, p, o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
qe.close();
|
||||
}
|
||||
}
|
||||
}
|
9
legacy/utilities/solrtester/log4j.properties
Normal file
9
legacy/utilities/solrtester/log4j.properties
Normal file
|
@ -0,0 +1,9 @@
|
|||
log4j.appender.AllAppender=org.apache.log4j.ConsoleAppender
|
||||
log4j.appender.AllAppender.layout=org.apache.log4j.PatternLayout
|
||||
log4j.appender.AllAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c] %m%n
|
||||
# log4j.appender.AllAppender.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss,SSS} %-5p [%c{1}] %m%n
|
||||
|
||||
log4j.rootLogger=INFO, AllAppender
|
||||
|
||||
# Make all of the Solr classes quieter...
|
||||
log4j.logger.org.apache.solr=WARN
|
110
legacy/utilities/solrtester/solrtester-build.xml
Normal file
110
legacy/utilities/solrtester/solrtester-build.xml
Normal file
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- $This file is distributed under the terms of the license in /doc/license.txt$ -->
|
||||
|
||||
<!-- ======================================================================
|
||||
Integration tests to be sure that the Solr configuration does what we
|
||||
want.
|
||||
====================================================================== -->
|
||||
|
||||
<project name="solr-tester" default="describe">
|
||||
<property name="src.dir" location="src" />
|
||||
<property name="vitro.libs.dir" location="../../webapp/lib" />
|
||||
<property name="solr.home.template"
|
||||
location="../../solr/homeDirectoryTemplate" />
|
||||
<property name="solr.war.file" location="../../solr/solr-4.7.2.war" />
|
||||
|
||||
<property name="working.dir" location=".work" />
|
||||
<property name="classes.dir" location="${working.dir}/classes" />
|
||||
<property name="solr.working.dir" location="${working.dir}/solrHome" />
|
||||
<property name="solr.libs.dir" location="${working.dir}/solrLibs" />
|
||||
|
||||
<path id="main.compile.classpath">
|
||||
<fileset dir="${vitro.libs.dir}" includes="*.jar" />
|
||||
<fileset dir="${solr.libs.dir}" includes="*.jar" />
|
||||
</path>
|
||||
|
||||
<path id="test.run.classpath">
|
||||
<pathelement location="${classes.dir}" />
|
||||
<path refid="main.compile.classpath" />
|
||||
</path>
|
||||
|
||||
<!-- =================================
|
||||
target: describe
|
||||
================================= -->
|
||||
<target name="describe"
|
||||
description="--> Describe the targets (this is the default).">
|
||||
<echo>
|
||||
all - Runs "clean", then "test".
|
||||
clean - Delete all artifacts so the next build will be from scratch.
|
||||
test - Set up the Solr configuration, compile, and run the JUnit tests.
|
||||
</echo>
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: all
|
||||
================================= -->
|
||||
<target name="all"
|
||||
depends="clean,test"
|
||||
description="Build from scratch and run the tests.">
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: clean
|
||||
================================= -->
|
||||
<target name="clean"
|
||||
description="Delete the Solr configuration and the compiled clases.">
|
||||
<delete dir="${working.dir}" />
|
||||
</target>
|
||||
|
||||
<!-- =================================
|
||||
target: test
|
||||
================================= -->
|
||||
<target name="test" depends="compile" description="Run the tests.">
|
||||
<java classname="org.junit.runner.JUnitCore"
|
||||
fork="yes"
|
||||
failonerror="true">
|
||||
<arg value="edu.cornell.mannlib.vitro.utilities.solrtest.SolrConfigTester" />
|
||||
<classpath refid="test.run.classpath" />
|
||||
<sysproperty key="test.solr.home" value="${solr.working.dir}" />
|
||||
</java>
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: setup
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="setup">
|
||||
<mkdir dir="${working.dir}" />
|
||||
|
||||
<mkdir dir="${solr.working.dir}" />
|
||||
<sync todir="${solr.working.dir}" includeemptydirs="true">
|
||||
<fileset dir="${solr.home.template}" />
|
||||
</sync>
|
||||
|
||||
<mkdir dir="${solr.libs.dir}" />
|
||||
<unwar src="${solr.war.file}" dest="${solr.libs.dir}">
|
||||
<patternset includes="WEB-INF/lib/*" />
|
||||
<mapper type="flatten" />
|
||||
</unwar>
|
||||
|
||||
<mkdir dir="${classes.dir}" />
|
||||
<copy file="log4j.properties" todir="${classes.dir}" />
|
||||
</target>
|
||||
|
||||
<!-- - - - - - - - - - - - - - - - - -
|
||||
target: compile
|
||||
- - - - - - - - - - - - - - - - - -->
|
||||
<target name="compile" depends="setup">
|
||||
<javac srcdir="${src.dir}"
|
||||
destdir="${classes.dir}"
|
||||
debug="true"
|
||||
deprecation="true"
|
||||
encoding="UTF8"
|
||||
includeantruntime="false"
|
||||
optimize="true"
|
||||
source="1.7">
|
||||
<classpath refid="main.compile.classpath" />
|
||||
</javac>
|
||||
</target>
|
||||
|
||||
</project>
|
|
@ -0,0 +1,250 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.solrtest;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.log4j.Level;
|
||||
import org.apache.log4j.Logger;
|
||||
import org.apache.solr.client.solrj.SolrServerException;
|
||||
import org.apache.solr.client.solrj.embedded.EmbeddedSolrServer;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.common.SolrDocumentList;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.SolrInputDocument;
|
||||
import org.apache.solr.common.params.ModifiableSolrParams;
|
||||
import org.apache.solr.core.CoreContainer;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.JUnit4;
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*/
|
||||
@RunWith(JUnit4.class)
|
||||
public class SolrConfigTester {
|
||||
private static CoreContainer container;
|
||||
private static EmbeddedSolrServer server;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
String solrHomeString = System.getProperty("test.solr.home");
|
||||
container = new CoreContainer(solrHomeString);
|
||||
|
||||
try (LogLeveler l = new LogLeveler(SolrCore.class, Level.ERROR)) {
|
||||
container.load();
|
||||
}
|
||||
|
||||
server = new EmbeddedSolrServer(container, "collection1");
|
||||
server.deleteByQuery("*:*");
|
||||
server.commit();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
server.shutdown();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// The tests
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
@Test
|
||||
public void serverResponds() throws Exception {
|
||||
server.ping();
|
||||
}
|
||||
|
||||
@Test(expected = SolrException.class)
|
||||
public void docIdIsRequred() throws Exception {
|
||||
try (LogLeveler l = new LogLeveler(SolrCore.class, Level.OFF)) {
|
||||
addDocuments(inputDoc(null, field("nameRaw", "No ID")));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simpleName() throws Exception {
|
||||
addDocuments(inputDoc("idSimple", field("nameRaw", "SimpleName")));
|
||||
assertQueryResults("simple name", "SimpleName", "idSimple");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void upperLowerName() throws Exception {
|
||||
addDocuments(inputDoc("idUpperLower", field("nameRaw", "Lower, Upper")));
|
||||
assertQueryResults("upper lower name", "Lower", "idUpperLower");
|
||||
assertQueryResults("upper lower name", "lower", "idUpperLower");
|
||||
assertQueryResults("upper lower name", "UPPER", "idUpperLower");
|
||||
}
|
||||
|
||||
/**
|
||||
* Why does it behave as if name is stemmed? Is that the behavior that we
|
||||
* want?
|
||||
*/
|
||||
@Ignore
|
||||
@Test
|
||||
public void nameIsNotStemmed() throws Exception {
|
||||
addDocuments(inputDoc("nameStemming",
|
||||
field("nameRaw", "Swimming, Bills"),
|
||||
field("nameLowercaseSingleValued", "Lower, Upper")));
|
||||
assertQueryResults("name not stemmed", "Swimming", "nameStemming");
|
||||
assertQueryResults("name not stemmed", "Bills", "nameStemming");
|
||||
assertQueryResults("name not stemmed", "Swim");
|
||||
assertQueryResults("name not stemmed", "Bill");
|
||||
}
|
||||
|
||||
/**
|
||||
* A name with an umlaut over an o is found exactly, or with the umlaut
|
||||
* missing, upper case or lower case.
|
||||
*/
|
||||
@Test
|
||||
public void nameWithUmlaut() throws Exception {
|
||||
addDocuments(inputDoc("idUmlaut",
|
||||
field("nameRaw", "P\u00F6ysen B\u00F6ysen")));
|
||||
assertQueryResults("name with umlaut", "Boysen", "idUmlaut");
|
||||
assertQueryResults("name with umlaut", "B\u00F6ysen", "idUmlaut");
|
||||
assertQueryResults("name with umlaut", "BOYSEN", "idUmlaut");
|
||||
assertQueryResults("name with umlaut", "B\u00D6YSEN", "idUmlaut");
|
||||
}
|
||||
|
||||
/**
|
||||
* ALLTEXT is searched, but to make the 3rd case work, we need
|
||||
* ALLTEXTUNSTEMMED also. Why is that?
|
||||
*/
|
||||
@Test
|
||||
public void allTextIsSearched() throws Exception {
|
||||
addDocuments(inputDoc("allTextSearch", field("ALLTEXT", "Wonderful"),
|
||||
field("ALLTEXTUNSTEMMED", "Wonderful")));
|
||||
assertQueryResults("all text", "Wonderful", "allTextSearch");
|
||||
assertQueryResults("all text", "wonderful", "allTextSearch");
|
||||
assertQueryResults("all text", "WoNdErFuL", "allTextSearch");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allTextIsStemmed() throws Exception {
|
||||
addDocuments(inputDoc("allTextStem", field("ALLTEXT", "Swimming"),
|
||||
field("ALLTEXTUNSTEMMED", "Swimming")));
|
||||
assertQueryResults("all text stem", "Swim", "allTextStem");
|
||||
assertQueryResults("all text stem", "swim", "allTextStem");
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public InputField field(String name, String... values) {
|
||||
return new InputField(name, null, values);
|
||||
}
|
||||
|
||||
public InputField field(String name, Float boost, String... values) {
|
||||
return new InputField(name, boost, values);
|
||||
}
|
||||
|
||||
public SolrInputDocument inputDoc(String docId, InputField... fields) {
|
||||
SolrInputDocument doc = new SolrInputDocument();
|
||||
if (docId != null) {
|
||||
doc.addField("DocId", docId);
|
||||
}
|
||||
for (InputField f : fields) {
|
||||
for (String value : f.values) {
|
||||
if (f.boost == null) {
|
||||
doc.addField(f.name, value);
|
||||
} else {
|
||||
doc.addField(f.name, value, f.boost);
|
||||
}
|
||||
}
|
||||
}
|
||||
return doc;
|
||||
}
|
||||
|
||||
public void addDocuments(SolrInputDocument... documents)
|
||||
throws SolrServerException, IOException {
|
||||
for (SolrInputDocument doc : documents) {
|
||||
server.add(doc);
|
||||
}
|
||||
server.commit();
|
||||
}
|
||||
|
||||
private void assertQueryResults(String message, String query,
|
||||
String... expectedDocIds) throws SolrServerException {
|
||||
ModifiableSolrParams params = new ModifiableSolrParams();
|
||||
params.set("q", query);
|
||||
|
||||
QueryResponse qResp = server.query(params);
|
||||
|
||||
SolrDocumentList docList = qResp.getResults();
|
||||
List<String> expected = Arrays.asList(expectedDocIds);
|
||||
List<String> actual = new ArrayList<>();
|
||||
for (int i = 0; i < docList.getNumFound(); i++) {
|
||||
actual.add(String.valueOf(docList.get(i).getFirstValue("DocId")));
|
||||
}
|
||||
assertEquals(message + " : '" + query + "'", expected, actual);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper classes
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public static class InputField {
|
||||
final String name;
|
||||
final Float boost;
|
||||
final String[] values;
|
||||
|
||||
public InputField(String name, Float boost, String[] values) {
|
||||
this.name = name;
|
||||
this.boost = boost;
|
||||
this.values = values;
|
||||
}
|
||||
}
|
||||
|
||||
public static class LogLeveler implements AutoCloseable {
|
||||
private final Logger logger;
|
||||
private final Level initialLevel;
|
||||
|
||||
public LogLeveler(Class<?> clazz, Level desiredLevel) {
|
||||
this.logger = Logger.getLogger(clazz);
|
||||
this.initialLevel = this.logger.getLevel();
|
||||
this.logger.setLevel(desiredLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
this.logger.setLevel(this.initialLevel);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO
|
||||
*
|
||||
* <pre>
|
||||
* // ** Let's index a document into our embedded server
|
||||
*
|
||||
* SolrInputDocument newDoc = new SolrInputDocument();
|
||||
* newDoc.addField("title", "Test Document 1");
|
||||
* newDoc.addField("id", "doc-1");
|
||||
* newDoc.addField("text", "Hello world!");
|
||||
* server.add(newDoc);
|
||||
* server.commit();
|
||||
*
|
||||
* // ** And now let's query for it
|
||||
*
|
||||
* params.set("q", "title:test");
|
||||
* QueryResponse qResp = server.query(params);
|
||||
*
|
||||
* SolrDocumentList docList = qResp.getResults();
|
||||
* System.out.println("Num docs: " + docList.getNumFound());
|
||||
* SolrDocument doc = docList.get(0);
|
||||
* System.out.println("Title: " + doc.getFirstValue("title").toString());
|
||||
* </pre>
|
||||
*/
|
|
@ -0,0 +1,26 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||
|
||||
/**
|
||||
* Indicates a problem with the attempt to run a command in a sub-process.
|
||||
*/
|
||||
public class CommandRunnerException extends Exception {
|
||||
|
||||
public CommandRunnerException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public CommandRunnerException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public CommandRunnerException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public CommandRunnerException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||
|
||||
/**
|
||||
* Indicates a problem so severe that we might as well stop now.
|
||||
*/
|
||||
public class FatalException extends RuntimeException {
|
||||
|
||||
public FatalException() {
|
||||
super();
|
||||
}
|
||||
|
||||
public FatalException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public FatalException(Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public FatalException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,434 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileFilter;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.Listener;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.LoggingListener;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.listener.MulticastListener;
|
||||
|
||||
/**
|
||||
* Holds the runtime parameters that are read from the properties file, perhaps
|
||||
* with modifications from the GUI if we are running interactively.
|
||||
*/
|
||||
public class SeleniumRunnerParameters {
|
||||
public static final String PROP_OUTPUT_DIRECTORY = "output_directory";
|
||||
public static final String PROP_UPLOAD_DIRECTORY = "upload_directory";
|
||||
public static final String PROP_SUITE_DIRECTORIES = "suite_parent_directories";
|
||||
public static final String PROP_WEBSITE_URL = "website_url";
|
||||
public static final String PROP_USER_EXTENSIONS_PATH = "user_extensions_path";
|
||||
public static final String PROP_FIREFOX_PROFILE_PATH = "firefox_profile_template_path";
|
||||
public static final String PROP_SUITE_TIMEOUT_LIMIT = "suite_timeout_limit";
|
||||
public static final String PROP_SELENIUM_JAR_PATH = "selenium_jar_path";
|
||||
public static final String PROP_IGNORED_TESTS = "ignored_tests_file";
|
||||
public static final String PROP_SUMMARY_CSS = "summary_css_file";
|
||||
|
||||
public static final String LOGFILE_NAME = "log_file.txt";
|
||||
|
||||
private final String websiteUrl;
|
||||
private final File userExtensionsFile;
|
||||
private final File firefoxProfileDir;
|
||||
private final int suiteTimeoutLimit;
|
||||
private final File seleniumJarPath;
|
||||
private final File uploadDirectory;
|
||||
private final File outputDirectory;
|
||||
private final File logFile;
|
||||
private final Collection<File> suiteParentDirectories;
|
||||
private final ModelCleanerProperties modelCleanerProperties;
|
||||
private final IgnoredTests ignoredTests;
|
||||
|
||||
private boolean cleanModel = true;
|
||||
private boolean cleanUploads = true;
|
||||
|
||||
// If we fail during the parameter parsing, we'll still write the log
|
||||
// somewhere.
|
||||
private Listener listener = new LoggingListener(System.out);
|
||||
|
||||
/**
|
||||
* Read the required properties from the property file, and do some checks
|
||||
* on them.
|
||||
*/
|
||||
public SeleniumRunnerParameters(String propertiesFilepath)
|
||||
throws IOException {
|
||||
Properties props = loadPropertiesFile(propertiesFilepath);
|
||||
|
||||
this.websiteUrl = getRequiredProperty(props, PROP_WEBSITE_URL);
|
||||
this.userExtensionsFile = checkReadableFile(props,
|
||||
PROP_USER_EXTENSIONS_PATH);
|
||||
this.firefoxProfileDir = checkOptionalReadableDirectory(props,
|
||||
PROP_FIREFOX_PROFILE_PATH);
|
||||
this.suiteTimeoutLimit = getRequiredIntegerProperty(props,
|
||||
PROP_SUITE_TIMEOUT_LIMIT);
|
||||
this.seleniumJarPath = checkReadableFile(props, PROP_SELENIUM_JAR_PATH);
|
||||
this.uploadDirectory = checkReadWriteDirectory(props,
|
||||
PROP_UPLOAD_DIRECTORY);
|
||||
|
||||
this.outputDirectory = checkOutputDirectory(props);
|
||||
this.logFile = new File(this.outputDirectory, LOGFILE_NAME);
|
||||
this.listener = new MulticastListener();
|
||||
addListener(new LoggingListener(this.logFile));
|
||||
|
||||
this.suiteParentDirectories = checkSuiteParentDirectories(props);
|
||||
|
||||
this.modelCleanerProperties = new ModelCleanerProperties(props);
|
||||
|
||||
// Get the list of ignored tests.
|
||||
String ignoredFilesPath = getRequiredProperty(props, PROP_IGNORED_TESTS);
|
||||
File ignoredFilesFile = new File(ignoredFilesPath);
|
||||
FileHelper.checkReadableFile(ignoredFilesFile, "File '"
|
||||
+ ignoredFilesPath + "'");
|
||||
this.ignoredTests = new IgnoredTests(ignoredFilesFile);
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the properties from the properties file.
|
||||
*/
|
||||
private Properties loadPropertiesFile(String propertiesFilepath)
|
||||
throws FileNotFoundException, IOException {
|
||||
File propsFile = new File(propertiesFilepath);
|
||||
if (!propsFile.exists()) {
|
||||
throw new FileNotFoundException("Property file does not exist: '"
|
||||
+ propsFile + "'");
|
||||
}
|
||||
|
||||
Reader propsReader = null;
|
||||
try {
|
||||
propsReader = new FileReader(propsFile);
|
||||
Properties props = new Properties();
|
||||
props.load(propsReader);
|
||||
return props;
|
||||
} finally {
|
||||
if (propsReader != null) {
|
||||
try {
|
||||
propsReader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* If there is a parameter for this key, it should point to a readable
|
||||
* directory.
|
||||
*/
|
||||
private File checkOptionalReadableDirectory(Properties props, String key) {
|
||||
String value = props.getProperty(key);
|
||||
if (value == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
value = value.trim();
|
||||
if (value.trim().length() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
File dir = new File(value);
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' does not exist.");
|
||||
}
|
||||
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not a directory.");
|
||||
}
|
||||
|
||||
if (!dir.canRead()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not readable.");
|
||||
}
|
||||
|
||||
return dir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that there is a property for the required directory path, and that
|
||||
* it points to a valid directory.
|
||||
*/
|
||||
private File checkReadWriteDirectory(Properties props, String key) {
|
||||
String value = getRequiredProperty(props, key);
|
||||
|
||||
File dir = new File(value);
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' does not exist. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not a directory. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
if (!dir.canRead()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not readable. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
|
||||
if (!dir.canWrite()) {
|
||||
throw new IllegalArgumentException("Directory " + key + " '"
|
||||
+ value + "' is not writeable. (" + dir.getAbsolutePath()
|
||||
+ ")");
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
private File checkReadableFile(Properties props, String key) {
|
||||
String value = getRequiredProperty(props, key);
|
||||
|
||||
File file = new File(value);
|
||||
|
||||
if (!file.exists()) {
|
||||
throw new IllegalArgumentException("File " + key + ": '" + value
|
||||
+ "' does not exist. (" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!file.isFile()) {
|
||||
throw new IllegalArgumentException("File " + key + ": '" + value
|
||||
+ "' is not a file. (" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!file.canRead()) {
|
||||
throw new IllegalArgumentException("File " + key + ": '" + value
|
||||
+ "' is not readable. (" + file.getAbsolutePath() + ")");
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property for the output directory. If it does not exist, create
|
||||
* it (the parent must exist). Ensure that it is writeable.
|
||||
*/
|
||||
private File checkOutputDirectory(Properties props) throws IOException {
|
||||
String value = getRequiredProperty(props, PROP_OUTPUT_DIRECTORY);
|
||||
File outputDirectory = new File(value);
|
||||
File outputParent = outputDirectory.getParentFile();
|
||||
|
||||
if (!outputDirectory.exists()) {
|
||||
if (!outputParent.exists()) {
|
||||
throw new IllegalArgumentException(
|
||||
"Output directory does not exist, nor does its parent. '"
|
||||
+ outputDirectory + "' ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
outputDirectory.mkdir();
|
||||
if (!outputDirectory.exists()) {
|
||||
throw new IOException("Failed to create output directory: '"
|
||||
+ outputDirectory + "' ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
if (!outputDirectory.isDirectory()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ outputDirectory.getPath() + "' is not a directory. ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!outputDirectory.canRead()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ outputDirectory.getPath() + "' is not readable. ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!outputDirectory.canWrite()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ outputDirectory.getPath() + "' is not writeable. ("
|
||||
+ outputDirectory.getAbsolutePath() + ")");
|
||||
}
|
||||
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the property for the suite directories and ensure that each one is
|
||||
* indeed a readable directory.
|
||||
*/
|
||||
private Collection<File> checkSuiteParentDirectories(Properties props) {
|
||||
String value = getRequiredProperty(props, PROP_SUITE_DIRECTORIES);
|
||||
|
||||
List<File> dirs = new ArrayList<File>();
|
||||
String[] paths = value.split("[:;]");
|
||||
for (String path : paths) {
|
||||
File dir = new File(path.trim());
|
||||
|
||||
if (!dir.exists()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ dir.getPath() + "' does not exist. ("
|
||||
+ dir.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!dir.isDirectory()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ dir.getPath() + "' is not a directory. ("
|
||||
+ dir.getAbsolutePath() + ")");
|
||||
}
|
||||
if (!dir.canRead()) {
|
||||
throw new IllegalArgumentException("Suite directory '"
|
||||
+ dir.getPath() + "' is not readable. ("
|
||||
+ dir.getAbsolutePath() + ")");
|
||||
}
|
||||
dirs.add(dir);
|
||||
}
|
||||
return dirs;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the value for this property. If there isn't one, or if it's empty,
|
||||
* complain.
|
||||
*/
|
||||
private String getRequiredProperty(Properties props, String key) {
|
||||
String value = props.getProperty(key);
|
||||
if ((value == null) || (value.trim().length() == 0)) {
|
||||
throw new IllegalArgumentException(
|
||||
"Property file must provide a value for '" + key + "'");
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* This required property must be a valid integer.
|
||||
*/
|
||||
private int getRequiredIntegerProperty(Properties props, String key) {
|
||||
String value = getRequiredProperty(props, key);
|
||||
try {
|
||||
return Integer.parseInt(value.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
throw new IllegalArgumentException("Property value for '" + key
|
||||
+ "' is not a valid integer: " + value);
|
||||
}
|
||||
}
|
||||
|
||||
public String getWebsiteUrl() {
|
||||
return websiteUrl;
|
||||
}
|
||||
|
||||
public File getUserExtensionsFile() {
|
||||
return userExtensionsFile;
|
||||
}
|
||||
|
||||
public boolean hasFirefoxProfileDir() {
|
||||
return firefoxProfileDir != null;
|
||||
}
|
||||
|
||||
public File getFirefoxProfileDir() {
|
||||
return firefoxProfileDir;
|
||||
}
|
||||
|
||||
public int getSuiteTimeoutLimit() {
|
||||
return suiteTimeoutLimit;
|
||||
}
|
||||
|
||||
public File getSeleniumJarPath() {
|
||||
return seleniumJarPath;
|
||||
}
|
||||
|
||||
public void addListener(Listener l) {
|
||||
if (listener instanceof MulticastListener) {
|
||||
((MulticastListener) listener).addListener(l);
|
||||
} else {
|
||||
throw new IllegalStateException("Listener is not a multi-cast -- "
|
||||
+ "can't add new listeners.");
|
||||
}
|
||||
}
|
||||
|
||||
public Listener getListener() {
|
||||
return listener;
|
||||
}
|
||||
|
||||
public void setListener(Listener logger) {
|
||||
this.listener = logger;
|
||||
}
|
||||
|
||||
public File getUploadDirectory() {
|
||||
return uploadDirectory;
|
||||
}
|
||||
|
||||
public File getOutputDirectory() {
|
||||
return outputDirectory;
|
||||
}
|
||||
|
||||
public File getLogFile() {
|
||||
return logFile;
|
||||
}
|
||||
|
||||
public Collection<File> getSuiteParentDirectories() {
|
||||
return suiteParentDirectories;
|
||||
}
|
||||
|
||||
public ModelCleanerProperties getModelCleanerProperties() {
|
||||
return modelCleanerProperties;
|
||||
}
|
||||
|
||||
public IgnoredTests getIgnoredTests() {
|
||||
return ignoredTests;
|
||||
}
|
||||
|
||||
public boolean isCleanModel() {
|
||||
return cleanModel;
|
||||
}
|
||||
|
||||
public void setCleanModel(boolean cleanModel) {
|
||||
this.cleanModel = cleanModel;
|
||||
}
|
||||
|
||||
public boolean isCleanUploads() {
|
||||
return cleanUploads;
|
||||
}
|
||||
|
||||
public void setCleanUploads(boolean cleanUploads) {
|
||||
this.cleanUploads = cleanUploads;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "Parameters:" + "\n websiteUrl: " + websiteUrl
|
||||
+ "\n userExtensionsFile: " + userExtensionsFile
|
||||
+ "\n firefoxProfileDir: " + firefoxProfileDir
|
||||
+ "\n suiteTimeoutLimit: " + suiteTimeoutLimit
|
||||
+ "\n seleniumJarPath: " + seleniumJarPath
|
||||
+ "\n uploadDirectory: " + uploadDirectory
|
||||
+ "\n outputDirectory: " + outputDirectory
|
||||
+ "\n suiteParentDirectories: " + suiteParentDirectories
|
||||
+ "\n modelCleanerProperties: " + modelCleanerProperties
|
||||
+ "\n" + ignoredTests + "\n cleanModel: " + cleanModel
|
||||
+ "\n cleanUploads: " + cleanUploads;
|
||||
}
|
||||
|
||||
/**
|
||||
* Look inside this parent directory and find any suite directories. You can
|
||||
* recognize a suite directory because it contains a file named Suite.html.
|
||||
*/
|
||||
public Collection<File> findSuiteDirs(File parentDir) {
|
||||
return Arrays.asList(parentDir.listFiles(new FileFilter() {
|
||||
public boolean accept(File pathname) {
|
||||
if (!pathname.isDirectory()) {
|
||||
return false;
|
||||
}
|
||||
if (pathname.getName().charAt(0) == '.') {
|
||||
return false;
|
||||
}
|
||||
|
||||
File suiteFile = new File(pathname, "Suite.html");
|
||||
if (suiteFile.exists()) {
|
||||
return true;
|
||||
} else {
|
||||
listener.subProcessErrout("Warning: suite file '"
|
||||
+ suiteFile.getPath() + "' does not exist.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,294 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner.datamodel;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.SortedMap;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.Status;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData.TestData;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.output.SuiteResults;
|
||||
|
||||
/**
|
||||
* Collect all that we know about suites, tests, and their current status.
|
||||
*/
|
||||
public class DataModel {
|
||||
|
||||
/* base data */
|
||||
private Collection<SuiteContents> suiteContents = Collections.emptyList();
|
||||
private Collection<File> selectedSuites = Collections.emptyList();
|
||||
private Collection<SuiteResults> suiteResults = Collections.emptyList();
|
||||
private OutputDataListener.Info dataListenerInfo = OutputDataListener.Info.EMPTY_INFO;
|
||||
private IgnoredTests ignoredTestList = IgnoredTests.EMPTY_LIST;
|
||||
private LogStats logStats = LogStats.EMPTY_LOG_STATS; // TODO
|
||||
|
||||
/* derived data */
|
||||
private Status runStatus = Status.PENDING;
|
||||
|
||||
private final SortedMap<String, SuiteData> suiteDataMap = new TreeMap<String, SuiteData>();
|
||||
private final EnumMap<Status, List<SuiteData>> suiteMapByStatus = new EnumMap<Status, List<SuiteData>>(
|
||||
Status.class);
|
||||
|
||||
private final List<TestData> allTests = new ArrayList<TestData>();
|
||||
private final EnumMap<Status, List<TestData>> testMapByStatus = new EnumMap<Status, List<TestData>>(
|
||||
Status.class);
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Constructor
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public DataModel() {
|
||||
calculate();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Update the base data.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public void setSuiteContents(Collection<SuiteContents> suiteContents) {
|
||||
this.suiteContents = new ArrayList<SuiteContents>(suiteContents);
|
||||
calculate();
|
||||
}
|
||||
|
||||
public void setSelectedSuites(Collection<File> selectedSuites) {
|
||||
this.selectedSuites = new ArrayList<File>(selectedSuites);
|
||||
calculate();
|
||||
}
|
||||
|
||||
public Collection<File> getSelectedSuites() {
|
||||
return new ArrayList<File>(selectedSuites);
|
||||
}
|
||||
|
||||
public void setSuiteResults(Collection<SuiteResults> suiteResults) {
|
||||
this.suiteResults = new ArrayList<SuiteResults>(suiteResults);
|
||||
calculate();
|
||||
}
|
||||
|
||||
public void captureDataListener(OutputDataListener dataListener) {
|
||||
this.dataListenerInfo = dataListener.getInfo();
|
||||
calculate();
|
||||
}
|
||||
|
||||
public void setIgnoredTestList(IgnoredTests ignoredTestList) {
|
||||
this.ignoredTestList = ignoredTestList;
|
||||
calculate();
|
||||
}
|
||||
|
||||
public void setLogStats(LogStats logStats) { // TODO
|
||||
this.logStats = logStats;
|
||||
calculate();
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Keep the derived data current.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Data in the model has been updated. Refresh all derived data.
|
||||
*/
|
||||
private void calculate() {
|
||||
// Clear all derived data.
|
||||
runStatus = Status.OK;
|
||||
|
||||
suiteDataMap.clear();
|
||||
suiteMapByStatus.clear();
|
||||
for (Status s : Status.values()) {
|
||||
suiteMapByStatus.put(s, new ArrayList<SuiteData>());
|
||||
}
|
||||
|
||||
allTests.clear();
|
||||
testMapByStatus.clear();
|
||||
for (Status s : Status.values()) {
|
||||
testMapByStatus.put(s, new ArrayList<TestData>());
|
||||
}
|
||||
|
||||
/*
|
||||
* Populate the Suite map with all Suites.
|
||||
*/
|
||||
Map<String, SuiteResults> resultsMap = new HashMap<String, SuiteResults>();
|
||||
for (SuiteResults result : suiteResults) {
|
||||
resultsMap.put(result.getName(), result);
|
||||
}
|
||||
Map<String, SuiteContents> contentsMap = new HashMap<String, SuiteContents>();
|
||||
for (SuiteContents contents : suiteContents) {
|
||||
contentsMap.put(contents.getName(), contents);
|
||||
}
|
||||
|
||||
for (SuiteContents contents : suiteContents) {
|
||||
String name = contents.getName();
|
||||
SuiteResults result = resultsMap.get(name);
|
||||
boolean ignored = ignoredTestList.isIgnored(name);
|
||||
ProcessOutput failureMessages = dataListenerInfo
|
||||
.getFailureMessages().get(name);
|
||||
suiteDataMap.put(name, new SuiteData(name, ignored, contents,
|
||||
result, failureMessages));
|
||||
}
|
||||
|
||||
/*
|
||||
* Map the Suites by status.
|
||||
*/
|
||||
for (SuiteData s : suiteDataMap.values()) {
|
||||
getSuites(s.getStatus()).add(s);
|
||||
}
|
||||
|
||||
/**
|
||||
* Populate the Test map with all Tests, and map by status.
|
||||
*/
|
||||
for (SuiteData s : suiteDataMap.values()) {
|
||||
for (TestData t : s.getTestMap().values()) {
|
||||
allTests.add(t);
|
||||
getTests(t.getStatus()).add(t);
|
||||
}
|
||||
}
|
||||
|
||||
if (logStats.hasErrors() || !getSuites(Status.ERROR).isEmpty()) {
|
||||
runStatus = Status.ERROR;
|
||||
} else if (!getSuites(Status.PENDING).isEmpty()) {
|
||||
runStatus = Status.PENDING;
|
||||
} else {
|
||||
runStatus = Status.OK;
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Access the derived data.
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
public Status getRunStatus() {
|
||||
return runStatus;
|
||||
}
|
||||
|
||||
public long getStartTime() {
|
||||
return dataListenerInfo.getStartTime();
|
||||
}
|
||||
|
||||
public long getEndTime() {
|
||||
return dataListenerInfo.getEndTime();
|
||||
}
|
||||
|
||||
public long getElapsedTime() {
|
||||
return dataListenerInfo.getElapsedTime();
|
||||
}
|
||||
|
||||
public boolean isAnyPasses() {
|
||||
return !getTests(Status.OK).isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAnyFailures() {
|
||||
return !getTests(Status.ERROR).isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAnyIgnores() {
|
||||
return !getTests(Status.IGNORED).isEmpty();
|
||||
}
|
||||
|
||||
public boolean isAnyPending() {
|
||||
return !getTests(Status.PENDING).isEmpty();
|
||||
}
|
||||
|
||||
public int getTotalSuiteCount() {
|
||||
return suiteDataMap.size();
|
||||
}
|
||||
|
||||
public int getPassingSuiteCount() {
|
||||
return getSuites(Status.OK).size();
|
||||
}
|
||||
|
||||
public int getFailingSuiteCount() {
|
||||
return getSuites(Status.ERROR).size();
|
||||
}
|
||||
|
||||
public int getIgnoredSuiteCount() {
|
||||
return getSuites(Status.IGNORED).size();
|
||||
}
|
||||
|
||||
public int getPendingSuiteCount() {
|
||||
return getSuites(Status.PENDING).size();
|
||||
}
|
||||
|
||||
public Collection<SuiteData> getAllSuites() {
|
||||
return suiteDataMap.values();
|
||||
}
|
||||
|
||||
public Map<String, SuiteData> getSuitesWithFailureMessages() {
|
||||
Map<String, SuiteData> map = new TreeMap<String, SuiteData>();
|
||||
for (SuiteData s : suiteDataMap.values()) {
|
||||
if (s.getFailureMessages() != null) {
|
||||
map.put(s.getName(), s);
|
||||
}
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
public int getTotalTestCount() {
|
||||
return allTests.size();
|
||||
}
|
||||
|
||||
public int getPassingTestCount() {
|
||||
return getTests(Status.OK).size();
|
||||
}
|
||||
|
||||
public int getFailingTestCount() {
|
||||
return getTests(Status.ERROR).size();
|
||||
}
|
||||
|
||||
public int getIgnoredTestCount() {
|
||||
return getTests(Status.IGNORED).size();
|
||||
}
|
||||
|
||||
public int getPendingTestCount() {
|
||||
return getTests(Status.PENDING).size();
|
||||
}
|
||||
|
||||
public Collection<TestData> getAllTests() {
|
||||
return Collections.unmodifiableCollection(allTests);
|
||||
}
|
||||
|
||||
public Collection<TestData> getFailingTests() {
|
||||
return Collections.unmodifiableCollection(getTests(Status.ERROR));
|
||||
}
|
||||
|
||||
public Collection<TestData> getIgnoredTests() {
|
||||
return Collections.unmodifiableCollection(getTests(Status.IGNORED));
|
||||
}
|
||||
|
||||
public Collection<IgnoredTestInfo> getIgnoredTestInfo() {
|
||||
return ignoredTestList.getList();
|
||||
}
|
||||
|
||||
public String getReasonForIgnoring(String suiteName, String testName) {
|
||||
return ignoredTestList.getReasonForIgnoring(suiteName, testName);
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------
|
||||
// Helper methods
|
||||
// ----------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Get the list of suites that have this status.
|
||||
*/
|
||||
private List<SuiteData> getSuites(Status st) {
|
||||
return suiteMapByStatus.get(st);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the list of tests that have this status.
|
||||
*/
|
||||
private List<TestData> getTests(Status st) {
|
||||
return testMapByStatus.get(st);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,375 @@
|
|||
/* $This file is distributed under the terms of the license in /doc/license.txt$ */
|
||||
|
||||
package edu.cornell.mannlib.vitro.utilities.testrunner.output;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.FileHelper;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.IgnoredTests.IgnoredTestInfo;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.LogStats;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.SeleniumRunnerParameters;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.Status;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.DataModel;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.datamodel.SuiteData.TestData;
|
||||
import edu.cornell.mannlib.vitro.utilities.testrunner.output.OutputDataListener.ProcessOutput;
|
||||
|
||||
/**
|
||||
* Creates the summary HTML file.
|
||||
*/
|
||||
public class OutputSummaryFormatter {
|
||||
public static final String SUMMARY_HTML_FILENAME = "summary.html";
|
||||
public static final String SUMMARY_CSS_FILENAME = "summary.css";
|
||||
private final SimpleDateFormat dateFormat = new SimpleDateFormat(
|
||||
"yyyy-MM-dd HH:mm:ss");
|
||||
private final SeleniumRunnerParameters parms;
|
||||
|
||||
private LogStats logStats;
|
||||
private DataModel dataModel;
|
||||
|
||||
public OutputSummaryFormatter(SeleniumRunnerParameters parms) {
|
||||
this.parms = parms;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a summary HTML file from the info contained in this log file and
|
||||
* these suite outputs.
|
||||
*/
|
||||
public void format(LogStats logStats, DataModel dataModel) {
|
||||
this.logStats = logStats;
|
||||
this.dataModel = dataModel;
|
||||
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
copyCssFile();
|
||||
|
||||
File outputFile = new File(parms.getOutputDirectory(),
|
||||
SUMMARY_HTML_FILENAME);
|
||||
writer = new PrintWriter(outputFile);
|
||||
|
||||
writeHeader(writer);
|
||||
writeStatsSection(writer);
|
||||
writeErrorMessagesSection(writer);
|
||||
writeCondensedTable(writer);
|
||||
writeIgnoreSection(writer);
|
||||
writeSuiteErrorMessagesSection(writer);
|
||||
writeFooter(writer);
|
||||
} catch (IOException e) {
|
||||
// There is no appeal for any problems here. Just report them.
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy the CSS file into the output directory.
|
||||
*/
|
||||
private void copyCssFile() {
|
||||
InputStream cssStream = this.getClass().getResourceAsStream(
|
||||
SUMMARY_CSS_FILENAME);
|
||||
if (cssStream == null) {
|
||||
System.out.println("Couldn't find the CSS file: '"
|
||||
+ SUMMARY_CSS_FILENAME + "'");
|
||||
} else {
|
||||
File cssTarget = new File(parms.getOutputDirectory(),
|
||||
SUMMARY_CSS_FILENAME);
|
||||
try {
|
||||
FileHelper.copy(cssStream, cssTarget);
|
||||
cssStream.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void writeHeader(PrintWriter w) {
|
||||
Status runStatus = dataModel.getRunStatus();
|
||||
String statusString = (runStatus == Status.PENDING) ? "IN PROGRESS"
|
||||
: runStatus.toString();
|
||||
String startString = formatDateTime(dataModel.getStartTime());
|
||||
|
||||
w.println("<html>");
|
||||
w.println("<head>");
|
||||
w.println(" <title>Summary of Acceptance Tests " + startString
|
||||
+ "</title>");
|
||||
w.println(" <link rel=\"stylesheet\" type=\"text/css\" "
|
||||
+ "href=\"summary.css\">");
|
||||
w.println("</head>");
|
||||
w.println("<body>");
|
||||
w.println();
|
||||
w.println(" <div class=\"heading\">");
|
||||
w.println(" Acceptance test results: " + startString);
|
||||
w.println(" <div class=\"" + runStatus.getHtmlClass()
|
||||
+ " one-word\">" + statusString + "</div>");
|
||||
w.println(" </div>");
|
||||
}
|
||||
|
||||
private void writeStatsSection(PrintWriter w) {
|
||||
String passClass = dataModel.isAnyPasses() ? Status.OK.getHtmlClass()
|
||||
: "";
|
||||
String failClass = dataModel.isAnyFailures() ? Status.ERROR
|
||||
.getHtmlClass() : "";
|
||||
String ignoreClass = dataModel.isAnyIgnores() ? Status.IGNORED
|
||||
.getHtmlClass() : "";
|
||||
|
||||
String start = formatDateTime(dataModel.getStartTime());
|
||||
String end = formatDateTime(dataModel.getEndTime());
|
||||
String elapsed = formatElapsedTime(dataModel.getElapsedTime());
|
||||
|
||||
w.println(" <div class=\"section\">Summary</div>");
|
||||
w.println();
|
||||
w.println(" <table class=\"summary\" cellspacing=\"0\">");
|
||||
w.println(" <tr>");
|
||||
w.println(" <td>");
|
||||
w.println(" <table cellspacing=\"0\">");
|
||||
w.println(" <tr><td>Start time:</td><td>" + start
|
||||
+ "</td></tr>");
|
||||
w.println(" <tr><td>End time:</td><td>" + end + "</td></tr>");
|
||||
w.println(" <tr><td>Elapsed time</td><td>" + elapsed
|
||||
+ "</td></tr>");
|
||||
w.println(" </table>");
|
||||
w.println(" </td>");
|
||||
w.println(" <td>");
|
||||
w.println(" <table class=\"tallys\" cellspacing=\"0\">");
|
||||
w.println(" <tr><th> </th><th>Suites</th><th>Tests</th>");
|
||||
w.println(" <tr class=\"" + passClass
|
||||
+ "\"><td>Passed</td><td>" + dataModel.getPassingSuiteCount()
|
||||
+ "</td><td>" + dataModel.getPassingTestCount() + "</td>");
|
||||
w.println(" <tr class=\"" + failClass
|
||||
+ "\"><td>Failed</td><td>" + dataModel.getFailingSuiteCount()
|
||||
+ "</td><td>" + dataModel.getFailingTestCount() + "</td>");
|
||||
w.println(" <tr class=\"" + ignoreClass
|
||||
+ "\"><td>Ignored</td><td>" + dataModel.getIgnoredSuiteCount()
|
||||
+ "</td><td>" + dataModel.getIgnoredTestCount() + "</td>");
|
||||
if (dataModel.isAnyPending()) {
|
||||
w.println(" <tr class=\"" + Status.PENDING.getHtmlClass()
|
||||
+ "\"><td>Pending</td><td>"
|
||||
+ dataModel.getPendingSuiteCount() + "</td><td>"
|
||||
+ dataModel.getPendingTestCount() + "</td>");
|
||||
}
|
||||
w.println(" <tr><td class=\"total\">Total</td><td>"
|
||||
+ dataModel.getTotalSuiteCount() + "</td><td>"
|
||||
+ dataModel.getTotalTestCount() + "</td>");
|
||||
w.println(" </table>");
|
||||
w.println(" </td>");
|
||||
w.println(" </tr>");
|
||||
w.println(" </table>");
|
||||
w.println();
|
||||
}
|
||||
|
||||
private void writeErrorMessagesSection(PrintWriter w) {
|
||||
String errorClass = Status.ERROR.getHtmlClass();
|
||||
|
||||
w.println(" <div class=section>Errors and warnings</div>");
|
||||
w.println();
|
||||
w.println(" <table cellspacing=\"0\">");
|
||||
|
||||
if ((!logStats.hasErrors()) && (!logStats.hasWarnings())) {
|
||||
w.println(" <tr><td colspan=\"2\">No errors or warnings</td></tr>");
|
||||
} else {
|
||||
for (String e : logStats.getErrors()) {
|
||||
w.println(" <tr class=\"" + errorClass
|
||||
+ "\"><td>ERROR</td><td>" + e + "</td></tr>");
|
||||
}
|
||||
}
|
||||
w.println(" </table>");
|
||||
w.println();
|
||||
}
|
||||
|
||||
private void writeCondensedTable(PrintWriter w) {
|
||||
w.println(" <div class=section>Condensed List</div>");
|
||||
w.println();
|
||||
w.println(" <table class=\"condensed\" cellspacing=\"0\">");
|
||||
for (SuiteData s : dataModel.getAllSuites()) {
|
||||
String sReason = "";
|
||||
if (s.getStatus() == Status.IGNORED) {
|
||||
sReason = dataModel.getReasonForIgnoring(s.getName(), "*");
|
||||
} else if (s.getFailureMessages() != null) {
|
||||
sReason = s.getFailureMessages().getErrout();
|
||||
} else if (s.getStatus() == Status.PENDING) {
|
||||
sReason = Status.PENDING.toString();
|
||||
}
|
||||
|
||||
w.println(" <tr>");
|
||||
w.println(" <td class=\"" + s.getStatus().getHtmlClass()
|
||||
+ "\">");
|
||||
w.println(" <div class=\"suite\">" + outputLink(s)
|
||||
+ "</div>");
|
||||
if (!sReason.isEmpty()) {
|
||||
// The entire class is either failed or pending or ignored.
|
||||
w.println(" <div class=\"reason\">" + sReason + "</div>");
|
||||
} else {
|
||||
// Show the individual tests.
|
||||
for (TestData t : s.getTestMap().values()) {
|
||||
String tClass = t.getStatus().getHtmlClass();
|
||||
String tReason = dataModel.getReasonForIgnoring(
|
||||
s.getName(), t.getTestName());
|
||||
|
||||
w.println(" <div class=\"test " + tClass + "\">");
|
||||
w.println(" " + outputLink(t));
|
||||
if (!tReason.isEmpty()) {
|
||||
w.println(" <div class=\"tReason\">" + tReason
|
||||
+ "</div>");
|
||||
}
|
||||
w.println(" </div>");
|
||||
}
|
||||
}
|
||||
w.println(" </td>");
|
||||
w.println(" </tr>");
|
||||
}
|
||||
w.println(" </table>");
|
||||
w.println();
|
||||
}
|
||||
|
||||
private void writeSuiteErrorMessagesSection(PrintWriter w) {
|
||||
Map<String, SuiteData> failedSuiteMap = dataModel
|
||||
.getSuitesWithFailureMessages();
|
||||
if (failedSuiteMap.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
w.println(" <div class=section>All tests</div>");
|
||||
w.println();
|
||||
for (SuiteData s : failedSuiteMap.values()) {
|
||||
ProcessOutput output = s.getFailureMessages();
|
||||
|
||||
w.println(" <a name=\"" + SuiteData.failureMessageAnchor(s)
|
||||
+ "\">");
|
||||
w.println(" <table cellspacing=\"0\">");
|
||||
w.println(" <tr><th>Standard Output</th></tr>\n");
|
||||
w.println(" <tr><td><pre>" + output.getStdout()
|
||||
+ "</pre></td></tr>\n");
|
||||
w.println(" </table>");
|
||||
w.println("<br/> <br/>");
|
||||
|
||||
w.println(" <table cellspacing=\"0\">");
|
||||
w.println(" <tr><th>Error Output</th></tr>\n");
|
||||
w.println(" <tr><td><pre>" + output.getErrout()
|
||||
+ "</pre></td></tr>\n");
|
||||
w.println(" </table>");
|
||||
w.println("<br/> <br/>");
|
||||
w.println();
|
||||
}
|
||||
}
|
||||
|
||||
private void writeIgnoreSection(PrintWriter w) {
|
||||
String warnClass = Status.IGNORED.getHtmlClass();
|
||||
Collection<IgnoredTestInfo> ignoredTests = dataModel
|
||||
.getIgnoredTestInfo();
|
||||
|
||||
w.println(" <div class=section>Ignored</div>");
|
||||
w.println();
|
||||
w.println(" <table class=\"ignored\" cellspacing=\"0\">");
|
||||
w.println(" <tr><th>Suite name</th><th>Test name</th>"
|
||||
+ "<th>Reason for ignoring</th></tr>\n");
|
||||
if (ignoredTests.isEmpty()) {
|
||||
w.println(" <tr><td colspan=\"3\">No tests ignored.</td>"
|
||||
+ "</tr>");
|
||||
} else {
|
||||
for (IgnoredTestInfo info : ignoredTests) {
|
||||
String suiteName = info.suiteName;
|
||||
String testName = info.testName;
|
||||
String reason = dataModel.getReasonForIgnoring(suiteName,
|
||||
testName);
|
||||
|
||||
w.println(" <tr class=\"" + warnClass + "\">");
|
||||
w.println(" <td>" + suiteName + "</td>");
|
||||
w.println(" <td>" + testName + "</td>");
|
||||
w.println(" <td>" + reason + "</td>");
|
||||
w.println(" </tr>");
|
||||
}
|
||||
}
|
||||
w.println(" </table>");
|
||||
w.println();
|
||||
}
|
||||
|
||||
private void writeFooter(PrintWriter w) {
|
||||
w.println(" <div class=section>Log</div>");
|
||||
w.println(" <pre class=\"log\">");
|
||||
|
||||
Reader reader = null;
|
||||
try {
|
||||
reader = new FileReader(parms.getLogFile());
|
||||
char[] buffer = new char[4096];
|
||||
int howMany;
|
||||
while (-1 != (howMany = reader.read(buffer))) {
|
||||
w.write(buffer, 0, howMany);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
if (reader != null) {
|
||||
try {
|
||||
reader.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
w.println(" </pre>");
|
||||
w.println("</body>");
|
||||
w.println("</html>");
|
||||
}
|
||||
|
||||
private String formatElapsedTime(long elapsed) {
|
||||
if (elapsed == 0) {
|
||||
return "---";
|
||||
}
|
||||
|
||||
long elapsedSeconds = elapsed / 1000L;
|
||||
long seconds = elapsedSeconds % 60L;
|
||||
long elapsedMinutes = elapsedSeconds / 60L;
|
||||
long minutes = elapsedMinutes % 60L;
|
||||
long hours = elapsedMinutes / 60L;
|
||||
|
||||
String elapsedTime = "";
|
||||
if (hours > 0) {
|
||||
elapsedTime += hours + "h ";
|
||||
}
|
||||
if (minutes > 0 || hours > 0) {
|
||||
elapsedTime += minutes + "m ";
|
||||
}
|
||||
elapsedTime += seconds + "s";
|
||||
|
||||
return elapsedTime;
|
||||
}
|
||||
|
||||
private String formatDateTime(long dateTime) {
|
||||
if (dateTime == 0) {
|
||||
return "---";
|
||||
}
|
||||
|
||||
return dateFormat.format(new Date(dateTime));
|
||||
}
|
||||
|
||||
private String outputLink(SuiteData s) {
|
||||
if (s.getOutputLink() == null) {
|
||||
return s.getName();
|
||||
} else {
|
||||
return "<a href=\"" + s.getOutputLink() + "\">" + s.getName()
|
||||
+ "</a>";
|
||||
}
|
||||
}
|
||||
|
||||
private String outputLink(TestData t) {
|
||||
if (t.getOutputLink() == null) {
|
||||
return t.getTestName();
|
||||
} else {
|
||||
return "<a href=\"" + t.getOutputLink() + "\">" + t.getTestName()
|
||||
+ "</a>";
|
||||
}
|
||||
}
|
||||
}
|
58
legacy/utilities/testrunner/test-user-model.owl
Normal file
58
legacy/utilities/testrunner/test-user-model.owl
Normal file
|
@ -0,0 +1,58 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#"
|
||||
xmlns:owl="http://www.w3.org/2002/07/owl#"
|
||||
xmlns:vitro="http://vitro.mannlib.cornell.edu/ns/vitro/0.7#"
|
||||
xmlns:auth="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#"
|
||||
xmlns="http://vitro.mannlib.cornell.edu/ns/vitro/default#"
|
||||
xml:base="http://vitro.mannlib.cornell.edu/ns/vitro/default">
|
||||
|
||||
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/TestAdmin">
|
||||
<auth:emailAddress rdf:datatype="http://www.w3.org/2001/XMLSchema#string">testAdmin@cornell.edu</auth:emailAddress>
|
||||
<auth:externalAuthId rdf:datatype="http://www.w3.org/2001/XMLSchema#string">testAdmin</auth:externalAuthId>
|
||||
<auth:firstName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Test</auth:firstName>
|
||||
<auth:lastName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Admin</auth:lastName>
|
||||
<auth:md5password rdf:datatype="http://www.w3.org/2001/XMLSchema#string">DC647EB65E6711E155375218212B3964</auth:md5password>
|
||||
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
|
||||
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
|
||||
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
|
||||
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#ADMIN"/>
|
||||
</auth:UserAccount>
|
||||
|
||||
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/JohnCurator">
|
||||
<auth:emailAddress rdf:datatype="http://www.w3.org/2001/XMLSchema#string">johnCurator@cornell.edu</auth:emailAddress>
|
||||
<auth:externalAuthId rdf:datatype="http://www.w3.org/2001/XMLSchema#string">johnCurator</auth:externalAuthId>
|
||||
<auth:firstName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">John</auth:firstName>
|
||||
<auth:lastName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Curator</auth:lastName>
|
||||
<auth:md5password rdf:datatype="http://www.w3.org/2001/XMLSchema#string">DC647EB65E6711E155375218212B3964</auth:md5password>
|
||||
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
|
||||
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
|
||||
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
|
||||
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#CURATOR"/>
|
||||
</auth:UserAccount>
|
||||
|
||||
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/SallyEditor">
|
||||
<auth:emailAddress rdf:datatype="http://www.w3.org/2001/XMLSchema#string">sallyEditor@cornell.edu</auth:emailAddress>
|
||||
<auth:externalAuthId rdf:datatype="http://www.w3.org/2001/XMLSchema#string">sallyEditor</auth:externalAuthId>
|
||||
<auth:firstName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Sally</auth:firstName>
|
||||
<auth:lastName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Editor</auth:lastName>
|
||||
<auth:md5password rdf:datatype="http://www.w3.org/2001/XMLSchema#string">DC647EB65E6711E155375218212B3964</auth:md5password>
|
||||
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
|
||||
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
|
||||
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
|
||||
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#EDITOR"/>
|
||||
</auth:UserAccount>
|
||||
|
||||
<auth:UserAccount rdf:about="http://vivo.mydomain.edu/individual/JoeUser">
|
||||
<auth:emailAddress rdf:datatype="http://www.w3.org/2001/XMLSchema#string">joeUser@cornell.edu</auth:emailAddress>
|
||||
<auth:externalAuthId rdf:datatype="http://www.w3.org/2001/XMLSchema#string">joeUser</auth:externalAuthId>
|
||||
<auth:firstName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">Joe</auth:firstName>
|
||||
<auth:lastName rdf:datatype="http://www.w3.org/2001/XMLSchema#string">User</auth:lastName>
|
||||
<auth:md5password rdf:datatype="http://www.w3.org/2001/XMLSchema#string">DC647EB65E6711E155375218212B3964</auth:md5password>
|
||||
<auth:status rdf:datatype="http://www.w3.org/2001/XMLSchema#string">ACTIVE</auth:status>
|
||||
<auth:loginCount rdf:datatype="http://www.w3.org/2001/XMLSchema#int">1</auth:loginCount>
|
||||
<auth:passwordLinkExpires rdf:datatype="http://www.w3.org/2001/XMLSchema#long">0</auth:passwordLinkExpires>
|
||||
<auth:hasPermissionSet rdf:resource="http://vitro.mannlib.cornell.edu/ns/vitro/authorization#SELF_EDITOR"/>
|
||||
</auth:UserAccount>
|
||||
|
||||
</rdf:RDF>
|
Loading…
Add table
Add a link
Reference in a new issue