You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@openaz.apache.org by pd...@apache.org on 2015/04/13 17:38:11 UTC

[11/51] [partial] incubator-openaz git commit: Initial seed of merged of AT&T and JP Morgan code

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/TestBase.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/TestBase.java b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/TestBase.java
new file mode 100755
index 0000000..4a105c2
--- /dev/null
+++ b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/TestBase.java
@@ -0,0 +1,1074 @@
+/*
+ *                        AT&T - PROPRIETARY
+ *          THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+ *        AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+ *             ACCORDANCE WITH APPLICABLE AGREEMENTS.
+ *
+ *          Copyright (c) 2014 AT&T Knowledge Ventures
+ *              Unpublished and Not for Publication
+ *                     All Rights Reserved
+ */
+package com.att.research.xacmlatt.pdp.test;
+
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.GnuParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.http.entity.ContentType;
+
+import com.att.research.xacml.api.AttributeValue;
+import com.att.research.xacml.api.DataType;
+import com.att.research.xacml.api.DataTypeException;
+import com.att.research.xacml.api.DataTypeFactory;
+import com.att.research.xacml.api.Decision;
+import com.att.research.xacml.api.Identifier;
+import com.att.research.xacml.api.Request;
+import com.att.research.xacml.api.RequestAttributes;
+import com.att.research.xacml.api.Response;
+import com.att.research.xacml.api.Result;
+import com.att.research.xacml.api.pdp.PDPEngine;
+import com.att.research.xacml.api.pdp.PDPEngineFactory;
+import com.att.research.xacml.api.pdp.PDPException;
+import com.att.research.xacml.api.pep.PEPException;
+import com.att.research.xacml.std.IdentifierImpl;
+import com.att.research.xacml.std.StdAttributeValue;
+import com.att.research.xacml.std.StdMutableAttribute;
+import com.att.research.xacml.std.StdMutableRequest;
+import com.att.research.xacml.std.StdMutableRequestAttributes;
+import com.att.research.xacml.std.dom.DOMRequest;
+import com.att.research.xacml.std.dom.DOMResponse;
+import com.att.research.xacml.std.dom.DOMStructureException;
+import com.att.research.xacml.std.json.JSONRequest;
+import com.att.research.xacml.std.json.JSONResponse;
+import com.att.research.xacml.std.json.JSONStructureException;
+import com.att.research.xacml.util.FactoryException;
+import com.att.research.xacml.util.XACMLProperties;
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+
+/**
+ * This is a base class for setting up a test environment. Using properties files, it contains the
+ * necessary information for 
+ * 1. defining and providing attributes
+ * 2. defining and instantiating the PDP engine
+ * 3. creating PEP requests and calling the PDP engine
+ * 
+ * @author pameladragosh
+ *
+ */
+public class TestBase extends SimpleFileVisitor<Path> {
+	private static final Log logger	= LogFactory.getLog(TestBase.class);
+	
+	public class HelpException extends Exception {
+		private static final long serialVersionUID = 1L;
+		
+	}
+	
+	/**
+	 * This private class holds information for properties defined for attribute
+	 * generation. The user can configure the properties file such that attributes
+	 * can be automatically generated and added into each request.
+	 * 
+	 * @author pameladragosh
+	 *
+	 */
+	class Generator {
+		Path file;
+		InputStream is;
+		BufferedReader reader;
+		List<StdMutableAttribute> attributes = new ArrayList<StdMutableAttribute>();
+		
+		public Generator(Path path) {
+			this.file = path;
+		}
+
+		/**
+		 * read - reads in the next line of data
+		 * 
+		 * @return	String - a line from the csv containing attribute data
+		 */
+		public String	read() {
+			String str = null;
+			if (is == null) {
+				try {
+					is = Files.newInputStream(file);
+				} catch (IOException e) {
+					logger.error(e);
+					return null;
+				}
+			}
+			if (reader == null) {
+				reader = new BufferedReader(new InputStreamReader(this.is));
+			}
+			try {
+				str = reader.readLine();
+				if (str == null) {
+					//
+					// No more strings, close up
+					//
+					this.close();
+				}
+				if (logger.isDebugEnabled()) {
+					logger.debug(str);
+				}
+			} catch (IOException e) {
+				logger.error(e);
+			}
+			return str;
+		}
+		
+		public void 	close() {
+			if (this.reader != null) {
+				try {
+					this.reader.close();
+				} catch (IOException idontcare) {
+				} finally {
+					this.reader = null;
+					this.is = null;
+				}
+			}
+		}
+		
+	}
+	
+	public static final String PROP_GENERATOR = "xacml.attribute.generator";
+	
+	public static final String OPTION_HELP = "help";
+	public static final String OPTION_TESTDIR = "dir";
+	public static final String OPTION_TESTREST = "rest";
+	public static final String OPTION_TESTURL = "url";
+	public static final String OPTION_TESTOUTPUT = "output";
+	public static final String OPTION_LOOP = "loop";
+	public static final String OPTION_TESTNUMBERS = "testNumbers";
+
+	public static final String DEFAULT_RESTURL = "https://localhost:8443/pdp/";
+	
+	public static Options options = new Options();
+	static {
+		options.addOption(new Option(OPTION_HELP, false, "Prints help."));
+		options.addOption(new Option(OPTION_TESTDIR, true, "Directory path where all the test properties and data are located."));
+		options.addOption(new Option(OPTION_TESTREST, false, "Test against RESTful PDP."));
+		options.addOption(new Option(OPTION_TESTURL, true, "URL to the RESTful PDP. Default is " + DEFAULT_RESTURL));
+		options.addOption(new Option(OPTION_TESTOUTPUT, true, "Specify a different location for dumping responses."));
+		options.addOption(new Option(OPTION_LOOP, true, "Number of times to loop through the tests. Default is 1. A value of -1 runs indefinitely."));
+		options.addOption(new Option(OPTION_TESTNUMBERS, true, "Comma-separated list of numbers found in the names of the test files to be run.  Numbers must exactly match the file name, e.g. '02'.  Used to limit testing to specific set of tests."));
+	}
+	
+	protected String directory = null;
+	protected Path output = null;
+	protected boolean isREST;
+	protected URL restURL = null;
+	protected int loop = 1;
+	protected PDPEngine engine = null;
+	protected List<Generator> generators = new ArrayList<Generator>();
+	protected static DataTypeFactory dataTypeFactory		= null;
+	
+	private long	permits = 0;
+	private long	denies = 0;
+	private long	notapplicables = 0;
+	private long	indeterminates = 0;
+	
+	private long	expectedPermits = 0;
+	private long	expectedDenies = 0;
+	private long	expectedNotApplicables = 0;
+	private long	expectedIndeterminates = 0;
+	
+	private long	generatedpermits = 0;
+	private long	generateddenies = 0;
+	private long	generatednotapplicables = 0;
+	private long	generatedindeterminates = 0;
+	
+	private long	responseMatches = 0;
+	private long	responseNotMatches = 0;
+	
+	private String[]	testNumbersArray = null;
+	
+	protected final Pattern pattern = Pattern.compile("Request[.]\\d+[.](Permit|Deny|NA|Indeterminate|Generate|Unknown)\\.(json|xml)");
+	
+	public static boolean isJSON(Path file) {
+		return file.toString().endsWith(".json");
+	}
+	
+	public static boolean isXML(Path file) {
+		return file.toString().endsWith(".xml");
+	}
+	
+	public TestBase(String[] args) throws ParseException, MalformedURLException, HelpException {
+		//
+		// Finish Initialization
+		//
+		this.restURL = new URL(DEFAULT_RESTURL);
+		//
+		// Parse arguments
+		//
+		this.parseCommands(args);
+	}
+	
+	/**
+	 * Parse in the command line arguments that the following parameters:
+	 * 
+	 * @param args - command line arguments
+	 * @throws org.apache.commons.cli.ParseException
+	 * @throws java.net.MalformedURLException
+	 * @throws com.att.research.xacmlatt.pdp.test.TestBase.HelpException
+	 */
+	protected void parseCommands(String[] args) throws ParseException, MalformedURLException, HelpException {
+		//
+		// Parse the command line options
+		//
+		CommandLine cl;
+		cl = new GnuParser().parse(options, args);
+		//
+		// Check for what we have
+		//
+		if (cl.hasOption(OPTION_HELP)) {
+    		new HelpFormatter().printHelp("Usage: -dir testdirectory OPTIONS",
+    				options);
+    		throw new HelpException();
+		}
+		if (cl.hasOption(OPTION_TESTDIR)) {
+			this.directory = cl.getOptionValue(OPTION_TESTDIR);
+		} else {
+			throw new IllegalArgumentException("You must specify a test directory. -dir path/to/some/where");
+		}
+		if (cl.hasOption(OPTION_TESTREST)) {
+			this.isREST = true;
+		} else {
+			this.isREST = false;
+		}
+		if (cl.hasOption(OPTION_TESTURL)) {
+			this.restURL = new URL(cl.getOptionValue(OPTION_TESTURL));
+		}
+		if (cl.hasOption(OPTION_TESTOUTPUT)) {
+			this.output = Paths.get(cl.getOptionValue(OPTION_TESTOUTPUT));
+		} else {
+			this.output = Paths.get(this.directory, "results");
+		}
+		if (cl.hasOption(OPTION_LOOP)) {
+			this.loop = Integer.parseInt(cl.getOptionValue(OPTION_LOOP));
+		}
+		if (cl.hasOption(OPTION_TESTNUMBERS)) {
+			String testNumberString = cl.getOptionValue(OPTION_TESTNUMBERS);
+			testNumbersArray = testNumberString.split(",");
+			//
+			// reset strings to include dots so they exactly match pattern in file name
+			//
+			for (int i = 0; i < testNumbersArray.length; i++) {
+				testNumbersArray[i] = "." + testNumbersArray[i] + ".";
+			}
+		}
+	}
+	
+	/**
+	 * Using the command line options that were parsed, configures our test instance.
+	 * 
+	 * @throws com.att.research.xacml.util.FactoryException
+	 */
+	protected void configure() throws FactoryException {
+		//
+		// Setup the xacml.properties file
+		//
+		if (this.directory == null) {
+			throw new IllegalArgumentException("Must supply a path to a test directory.");
+		}
+		Path pathDir = Paths.get(this.directory, "xacml.properties");
+		if (Files.notExists(pathDir)) {
+			throw new IllegalArgumentException(pathDir.toString() + " does not exist.");
+		}
+		//
+		// Set it as the System variable so the XACML factories know where the properties are
+		// loaded from.
+		//
+		System.setProperty(XACMLProperties.XACML_PROPERTIES_NAME, pathDir.toString());
+		//
+		// Now we can create the data type factory
+		//
+		dataTypeFactory	= DataTypeFactory.newInstance();
+		//
+		// Load in what generators we are to create
+		//
+		String generators = XACMLProperties.getProperty(PROP_GENERATOR);
+		if (generators != null) {
+			//
+			// Parse the generators
+			//
+			for (String generator : Splitter.on(',').trimResults().omitEmptyStrings().split(generators)) {
+				this.configureGenerator(generator);
+			}
+		}
+		//
+		// If we are embedded, create our engine
+		//
+		if (this.isREST == false) {
+			PDPEngineFactory factory = PDPEngineFactory.newInstance();
+			this.engine = factory.newEngine();
+		}
+		//
+		// Remove all the responses from the results directory
+		//
+		this.removeResults();
+	}
+	
+	/**
+	 * Removes all the Response* files from the results directory.
+	 * 
+	 */
+	public void	removeResults() {
+		try {
+			//
+			// Determine where the results are supposed to be written to
+			//
+			Path resultsPath;
+			if (this.output != null) {
+				resultsPath = this.output;
+			} else {
+				resultsPath = Paths.get(this.directory.toString(), "results");
+			}
+			//
+			// Walk the files
+			//
+			Files.walkFileTree(resultsPath, new SimpleFileVisitor<Path>() {
+
+				@Override
+				public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+					if (file.getFileName().toString().startsWith("Response")) {
+						Files.delete(file);
+					}
+					return super.visitFile(file, attrs);
+				}				
+			});
+		} catch (IOException e) {
+			logger.error("Failed to removeRequests from " + this.directory + " " + e);
+		}
+	}
+	
+	/**
+	 * Configure's a specific generator instance from the properties file.
+	 * 
+	 * @param generator
+	 */
+	protected void configureGenerator(String generator) {
+		String prefix = PROP_GENERATOR + "." + generator;
+		String file = XACMLProperties.getProperty(prefix + ".file");
+		//
+		// Create a generator object
+		//
+		Generator gen = new Generator(Paths.get(this.directory, file));
+		this.generators.add(gen);
+		//
+		// Grab attributes
+		//
+		String attributes = XACMLProperties.getProperty(prefix + ".attributes");
+		for (String attribute : Splitter.on(',').trimResults().omitEmptyStrings().split(attributes)) {
+			String attributePrefix = prefix + ".attributes." + attribute;
+			//
+			// Create an attribute value. It is simply a placeholder for the field within
+			// the CSV that contains the actual attribute value. It mainly holds the data type
+			//
+			Identifier datatype = new IdentifierImpl(XACMLProperties.getProperty(attributePrefix + ".datatype"));
+			Integer field = Integer.parseInt(XACMLProperties.getProperty(attributePrefix + ".field"));
+			StdAttributeValue<?> value = new StdAttributeValue<>(datatype, field);
+			//
+			// Get the rest of the attribute properties
+			//
+			Identifier category = new IdentifierImpl(XACMLProperties.getProperty(attributePrefix + ".category"));
+			Identifier id = new IdentifierImpl(XACMLProperties.getProperty(attributePrefix + ".id"));
+			String issuer = XACMLProperties.getProperty(attributePrefix + ".issuer");
+			boolean include = Boolean.parseBoolean(XACMLProperties.getProperty(attributePrefix + ".include", "false"));
+			//
+			// Now we have a skeleton attribute
+			//
+			gen.attributes.add(new StdMutableAttribute(category, id, value, issuer, include));
+		}
+	}
+	
+	/**
+	 * This runs() the test instance. It first configure's itself and then walks the
+	 * requests directory issue each request to the PDP engine.
+	 * 
+	 * @throws java.io.IOException
+	 * @throws com.att.research.xacml.util.FactoryException
+	 * 
+	 */
+	public void run() throws IOException, FactoryException {
+		//
+		// Configure ourselves
+		//
+		this.configure();
+		//
+		// Loop and run
+		//
+		int runs = 1;
+		do {
+			long lTimeStart = System.currentTimeMillis();
+			logger.info("Run number: " + runs);
+			//
+			// Walk the request directory
+			//
+			Files.walkFileTree(Paths.get(this.directory.toString(), "requests"), this);
+			long lTimeEnd = System.currentTimeMillis();
+			logger.info("Run elapsed time: " + (lTimeEnd - lTimeStart) + "ms");
+			//
+			// Dump the stats
+			//
+			this.dumpStats();
+			this.resetStats();
+			//
+			// Increment
+			//
+			runs++;
+		} while ((this.loop == -1 ? true : runs <= this.loop));
+	}
+	
+	@Override
+	public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+		//
+		// Sanity check the file name
+		//
+		Matcher matcher = this.pattern.matcher(file.getFileName().toString());
+		if (matcher.matches()) {
+			//
+			// if user has limited which files to use, check that here
+			//
+			if (testNumbersArray != null) {
+				String fileNameString = file.getFileName().toString();
+				boolean found = false;
+				for (String numberString : testNumbersArray) {
+					if (fileNameString.contains(numberString)) {
+						found = true;
+						break;
+					}
+				}
+				if (found == false) {
+					//
+					// this test is not in the list to be run, so skip it
+					//
+					return super.visitFile(file, attrs);
+				}
+			}
+			try {
+				//
+				// Pull what this request is supposed to be
+				//
+				String group = null;
+				int count = matcher.groupCount();
+				if (count >= 1) {
+					group = matcher.group(count-1);
+				}
+				//
+				// Send it
+				//
+				this.sendRequest(file, group);
+			} catch (Exception e) {
+				logger.error(e);
+				e.printStackTrace();
+			}
+		}
+		return super.visitFile(file, attrs);
+	}
+	
+	/**
+	 * When a request file is encountered, this method is called send the request to the PDP engine. It will also dump
+	 * the response object. If the group equals "Generate", then it will loop and send the request with generated attributes
+	 * until that list is empty.
+	 * 
+	 * @param file - Request file. Eg. Request-01-Permit.json
+	 * @param group - This is the parsed out string of the request file that defines if it is a Permit/Deny/Generate etc.
+	 * @throws Exception
+	 */
+	protected void sendRequest(Path file, String group) throws Exception {
+		logger.info(file.toString());
+		int requestCount = 0;
+		do {
+			//
+			// Generate the request
+			//
+			Request request = this.generateRequest(file, group);
+			//
+			// Was something generated?
+			//
+			if (request == null) {
+				//
+				// Get out of the loop
+				//
+				logger.info("NULL request generated.");
+				break;
+			}
+			logger.info(request);
+			//
+			// Call the PDP
+			//
+			Response response = this.callPDP(request);
+			//
+			// Process the response
+			//
+			this.processResponse(file, request, response, group, requestCount);
+			//
+			// Is this a generated request?
+			//
+			if (group.equals("Generate")) {
+				//
+				// Yes, increment counter and move
+				// on to the next generated request.
+				//
+				requestCount++;
+			} else {
+				//
+				// Nope, exit the loop
+				//
+				break;
+			}
+		} while (group.equals("Generate"));
+	}
+	
+	/**
+	 * Sends the request object to the PDP engine. Either the embedded engine or the RESTful engine.
+	 * 
+	 * @param request - XACML request object
+	 * @return Response - returns the XACML response object
+	 */
+	protected Response callPDP(Request request) {
+		//
+		// Send it to the PDP
+		//
+		Response response = null;
+		if (this.isREST) {
+			try {
+				String jsonString = JSONRequest.toString(request, false);
+				//
+				// Call RESTful PDP
+				//
+				response = this.callRESTfulPDP(new ByteArrayInputStream(jsonString.getBytes()));
+			} catch (Exception e) {
+				logger.error("Error in sending RESTful request: " + e, e);
+			}
+		} else {
+			//
+			// Embedded call to PDP
+			//
+			long lTimeStart = System.currentTimeMillis();
+			try {
+				response = this.engine.decide(request);
+			} catch (PDPException e) {
+				logger.error(e);
+			}
+			long lTimeEnd = System.currentTimeMillis();
+			logger.info("Elapsed Time: " + (lTimeEnd - lTimeStart) + "ms");
+		}
+		return response;
+	}
+	
+	/**
+	 * Reads the request file into a Request object based on its type.
+	 * 
+	 * If the request has "Generate" in its filename, then this function will add
+	 * generated attributes into the request.
+	 * 
+	 * @param file - Request file. Eg. Request-01-Permit.json
+	 * @param group - This is the parsed out string of the request file that defines if it is a Permit/Deny/Generate etc.
+	 * @return
+	 * @throws com.att.research.xacml.std.json.JSONStructureException
+	 * @throws com.att.research.xacml.std.dom.DOMStructureException
+	 * @throws com.att.research.xacml.api.pep.PEPException
+	 */
+	protected Request generateRequest(Path file, String group) throws JSONStructureException, DOMStructureException, PEPException {
+		//
+		// Convert to a XACML Request Object
+		//
+		Request request = null;
+		if (TestBase.isJSON(file)) {
+			request = JSONRequest.load(file.toFile());
+		} else if (TestBase.isXML(file)) {
+			request = DOMRequest.load(file.toFile());
+		}
+		if (request == null) {
+			throw new PEPException("Invalid Request File: " + file.toString());
+		}
+		//
+		// Only if this request has "Generate"
+		// Request.XX.Generate.[json|xml]
+		//
+		if (group.equals("Generate")) {
+			//
+			// Add attributes to it
+			//
+			request = this.onNextRequest(request);
+		}
+		//
+		// Done
+		//
+		return request;
+	}
+
+	/**
+	 * Called to add in generated attributes into the request.
+	 * 
+	 * @param request
+	 * @return
+	 */
+	protected Request onNextRequest(Request request) {
+		//
+		// If we have no generators, just return
+		//
+		if (this.generators.isEmpty()) {
+			return request;
+		}
+		//
+		// Copy the request attributes
+		//
+		List<StdMutableRequestAttributes> attributes = new ArrayList<StdMutableRequestAttributes>();
+		for (RequestAttributes a : request.getRequestAttributes()) {
+			attributes.add(new StdMutableRequestAttributes(a));
+		}
+		//
+		// Iterate the generators
+		//
+		for (Generator generator : this.generators) {
+			//
+			// Read a row in
+			//
+			String line = generator.read();
+			//
+			// Was something read?
+			//
+			if (line == null) {
+				//
+				// No more rows to read, return null
+				//
+				return null;
+			}
+			//
+			// Split the line
+			//
+			List<String> fields = Lists.newArrayList(Splitter.on(',').trimResults().split(line));
+			//
+			// Now work on the attributes
+			//
+			for (StdMutableAttribute attribute : generator.attributes) {
+				//
+				// Grab the attribute holder, which holds the datatype and field. There should
+				// be only ONE object in the collection.
+				//
+				AttributeValue<?> value = attribute.getValues().iterator().next();
+				Integer field = (Integer) value.getValue();
+				//
+				// Is the field number valid?
+				//
+				if (field >= fields.size()) {
+					logger.error("Not enough fields: " + field + "(" + fields.size() + ")");
+					return null;
+				}
+				//
+				// Determine what datatype it is
+				//
+				DataType<?> dataTypeExtended	= dataTypeFactory.getDataType(value.getDataTypeId());
+				if (dataTypeExtended == null) {
+					logger.error("Failed to determine datatype");
+					return null;
+				}
+				//
+				// Create the attribute value
+				//
+				try {
+					AttributeValue<?> attributeValue = dataTypeExtended.createAttributeValue(fields.get(field));					
+					//
+					// Create the attribute
+					//
+					StdMutableAttribute newAttribute = new StdMutableAttribute(attribute.getCategory(),
+																				attribute.getAttributeId(),
+																				attributeValue,
+																				attribute.getIssuer(),
+																				attribute.getIncludeInResults());
+					boolean added = false;
+					for (StdMutableRequestAttributes a : attributes) {
+						//
+						// Does the category exist?
+						//
+						if (a.getCategory().equals(attribute.getCategory())) {
+							//
+							// Yes - add in the new attribute value
+							//
+							a.add(newAttribute);
+							added = true;
+							break;
+						}
+					}
+					if (added == false) {
+						//
+						// New category - create it and add it in
+						//
+						StdMutableRequestAttributes a = new StdMutableRequestAttributes(); 
+						a.setCategory(newAttribute.getCategory());
+						a.add(newAttribute);
+						attributes.add(a);
+					}
+				} catch (DataTypeException e) {
+					logger.error(e);
+					return null;
+				}
+			}
+		}
+		//
+		// Now form our final request
+		//
+		StdMutableRequest newRequest = new StdMutableRequest();
+		newRequest.setCombinedDecision(request.getCombinedDecision());
+		newRequest.setRequestDefaults(request.getRequestDefaults());
+		newRequest.setReturnPolicyIdList(request.getReturnPolicyIdList());
+		newRequest.setStatus(request.getStatus());
+		for (StdMutableRequestAttributes a : attributes) {
+			newRequest.add(a);
+		}
+		return newRequest;
+	}
+
+	/**
+	 * This makes an HTTP POST call to a running PDP RESTful servlet to get a decision.
+	 * 
+	 * @param file
+	 * @return
+	 */
+	protected Response callRESTfulPDP(InputStream is) {
+		Response response = null;
+		HttpURLConnection connection = null;
+		try {
+
+			//
+			// Open up the connection
+			//
+			connection = (HttpURLConnection) this.restURL.openConnection();
+			connection.setRequestProperty("Content-Type", "application/json");
+			//
+			// Setup our method and headers
+			//
+            connection.setRequestMethod("POST");
+            connection.setUseCaches(false);
+            //
+            // Adding this in. It seems the HttpUrlConnection class does NOT
+            // properly forward our headers for POST re-direction. It does so
+            // for a GET re-direction.
+            //
+            // So we need to handle this ourselves.
+            //
+            connection.setInstanceFollowRedirects(false);
+			connection.setDoOutput(true);
+			connection.setDoInput(true);
+			//
+			// Send the request
+			//
+			try (OutputStream os = connection.getOutputStream()) {
+				IOUtils.copy(is, os);
+			}
+            //
+            // Do the connect
+            //
+            connection.connect();
+            if (connection.getResponseCode() == 200) {
+            	//
+            	// Read the response
+            	//
+        		ContentType contentType = null;
+        		try {
+        			contentType = ContentType.parse(connection.getContentType());
+        			
+        			if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_JSON.getMimeType())) {
+                		response = JSONResponse.load(connection.getInputStream());
+        			} else if (contentType.getMimeType().equalsIgnoreCase(ContentType.APPLICATION_XML.getMimeType()) ||
+        					contentType.getMimeType().equalsIgnoreCase("application/xacml+xml") ) {
+                		response = DOMResponse.load(connection.getInputStream());
+        			} else {
+                		logger.error("unknown content-type: " + contentType);
+                	}
+
+                } catch (Exception e) {
+        			String message = "Parsing Content-Type: " + connection.getContentType() + ", error=" + e.getMessage();
+        			logger.error(message, e);
+        		}
+
+            } else {
+            	logger.error(connection.getResponseCode() + " " + connection.getResponseMessage());
+            }
+		} catch (Exception e) {
+			logger.error(e);
+		}
+		
+		return response;
+	}
+	
+	/**
+	 * This processes a response. Saves the response out to disk. If there is a corresponding response file for the request located
+	 * in the "responses" sub-directory, then this method will compare that response file with what the engine returned to see if it
+	 * matched.
+	 * 
+	 * @param requestFile
+	 * @param request
+	 * @param response
+	 * @param group
+	 * @param count
+	 * @throws Exception
+	 */
+	protected void processResponse(Path requestFile, Request request, Response response, String group, int count) throws Exception {
+		//
+		// Construct the output filename
+		//
+		Path responseFile = null;
+		Path resultFile = null;
+		int num = requestFile.getNameCount();
+		if (num < 2) {
+			logger.error("Too few dir's in request filename.");
+			throw new Exception("Too few dir's in request filename. Format should be Request.[0-9]+.{Permit|Deny|NA|Indeterminate}.{json|xml}");
+		}
+		String filename = requestFile.getFileName().toString();
+		if (group.equals("Generate")) {
+			//
+			// Using count variable, construct a filename
+			//
+			//		i.e. Response.03.Generate.{count}.json
+			//
+			filename = "Response" + filename.substring(filename.indexOf('.'), filename.lastIndexOf('.')) + String.format("%03d", count) + filename.substring(filename.lastIndexOf('.'));
+		} else {
+			//
+			// Construct filename
+			//
+			filename = "Response" + filename.substring(filename.indexOf('.'));
+		}
+		//
+		// Determine equivalent response file path
+		//
+		responseFile = Paths.get(requestFile.subpath(0, num - 2).toString(), "responses");
+		if (Files.notExists(responseFile)) {
+			//
+			// Create it
+			//
+			logger.warn(responseFile.toString() + " does NOT exist, creating...");
+			try {
+				Files.createDirectories(responseFile);
+			} catch (IOException e) {
+				logger.error(e);
+				throw new Exception("Cannot proceed without an output directory.");
+			}
+		}
+		responseFile = Paths.get(responseFile.toString(), filename);
+		//
+		// Determine path to write result file
+		//
+		if (this.output != null) {
+			//
+			// User specified an output path
+			//
+			resultFile = this.output;
+		} else {
+			//
+			// Default path
+			//
+			resultFile = Paths.get(requestFile.subpath(0, num - 2).toString(), "results");
+		}
+		//
+		// Check if the path exists
+		//
+		if (Files.notExists(resultFile)) {
+			//
+			// Create it
+			//
+			logger.warn(resultFile.toString() + " does NOT exist, creating...");
+			try {
+				Files.createDirectories(resultFile);
+			} catch (IOException e) {
+				logger.error(e);
+				throw new Exception("Cannot proceed without an output directory.");
+			}
+		}
+		//
+		// Add the filename to the path
+		//
+		resultFile = Paths.get(resultFile.toString(), filename);
+		//
+		// Check if there is an equivalent response in the response
+		// directory. If so, compare our response result with that one.
+		//
+		boolean succeeded = true;
+		if (responseFile != null && Files.exists(responseFile)) {
+			//
+			// Do comparison
+			//
+			Response expectedResponse = null;
+			if (TestBase.isJSON(responseFile)) {
+				expectedResponse = JSONResponse.load(responseFile);
+			} else if (TestBase.isXML(responseFile)) {
+				expectedResponse = DOMResponse.load(responseFile);
+			}
+			if (expectedResponse != null) {
+				//
+				// Do the compare
+				//
+				if (response == null) {
+					logger.error("NULL response returned.");
+					this.responseNotMatches++;
+					succeeded = false;
+				} else {
+					if (response.equals(expectedResponse)) {
+						logger.info("Response matches expected response.");
+						this.responseMatches++;
+					} else {
+						logger.error("Response does not match expected response.");
+						logger.error("Expected: ");
+						logger.error(expectedResponse.toString());
+						this.responseNotMatches++;
+						succeeded = false;
+					}
+				}
+			}
+		}
+		//
+		// Write the response to the result file
+		//
+		logger.info("Request: " + requestFile.getFileName() + " response is: " + (response == null ? "null" : response.toString()));
+		if (resultFile != null && response != null) {
+			if (TestBase.isJSON(resultFile)) {
+				Files.write(resultFile, JSONResponse.toString(response, true).getBytes());
+			} else if (TestBase.isXML(resultFile)) {
+				Files.write(resultFile, DOMResponse.toString(response, true).getBytes());
+			}
+		}
+		//
+		// Stats
+		//		
+		if (group.equals("Permit")) {
+			this.expectedPermits++;
+		} else if (group.equals("Deny")) {
+			this.expectedDenies++;
+		} else if (group.equals("NA")) {
+			this.expectedNotApplicables++;
+		} else if (group.equals("Indeterminate")) {
+			this.expectedIndeterminates++;
+		}
+		if (response != null) {
+			for (Result result : response.getResults()) {
+				Decision decision = result.getDecision();
+				if (group.equals("Generate")) {
+					if (decision.equals(Decision.PERMIT)) {
+						this.generatedpermits++;
+					} else if (decision.equals(Decision.DENY)) {
+						this.generateddenies++;
+					} else if (decision.equals(Decision.NOTAPPLICABLE)) {
+						this.generatednotapplicables++;
+					} else if (decision.equals(Decision.INDETERMINATE)) {
+						this.generatedindeterminates++;
+					}
+					continue;
+				}
+				if (decision.equals(Decision.PERMIT)) {
+					this.permits++;
+					if (group.equals("Permit") == false) {
+						succeeded = false;
+						logger.error("Expected " + group + " got " + decision);
+					}
+				} else if (decision.equals(Decision.DENY)) {
+					this.denies++;
+					if (group.equals("Deny") == false) {
+						succeeded = false;
+						logger.error("Expected " + group + " got " + decision);
+					}
+				} else if (decision.equals(Decision.NOTAPPLICABLE)) {
+					this.notapplicables++;
+					if (group.equals("NA") == false) {
+						succeeded = false;
+						logger.error("Expected " + group + " got " + decision);
+					}
+				} else if (decision.equals(Decision.INDETERMINATE)) {
+					this.indeterminates++;
+					if (group.equals("Indeterminate") == false) {
+						succeeded = false;
+						logger.error("Expected " + group + " got " + decision);
+					}
+				}
+			}
+		}
+		if (succeeded) {
+			logger.info("REQUEST SUCCEEDED");
+		} else {
+			logger.info("REQUEST FAILED");
+		}
+	}
+
+	protected void	dumpStats() {
+		StringBuilder dump = new StringBuilder();
+		dump.append(System.lineSeparator());
+		dump.append("Permits: " + this.permits + " Expected: " + this.expectedPermits);
+		dump.append(System.lineSeparator());
+		dump.append("Denies: " + this.denies + " Expected: " + this.expectedDenies);
+		dump.append(System.lineSeparator());
+		dump.append("NA: " + this.notapplicables + " Expected: " + this.expectedNotApplicables);
+		dump.append(System.lineSeparator());
+		dump.append("Indeterminates: " + this.indeterminates + " Expected: " + this.expectedIndeterminates);
+		dump.append(System.lineSeparator());
+		dump.append("Generated Permits: " + this.generatedpermits);
+		dump.append(System.lineSeparator());
+		dump.append("Generated Denies: " + this.generateddenies);
+		dump.append(System.lineSeparator());
+		dump.append("Generated NA: " + this.generatednotapplicables);
+		dump.append(System.lineSeparator());
+		dump.append("Generated Indeterminates: " + this.generatedindeterminates);
+		dump.append(System.lineSeparator());
+		dump.append("Responses Matched: " + this.responseMatches);
+		dump.append(System.lineSeparator());
+		dump.append("Responses NOT Matched: " + this.responseNotMatches);
+		
+		if (this.permits != this.expectedPermits ||
+			this.denies != this.expectedDenies ||
+			this.notapplicables != this.expectedNotApplicables ||
+			this.indeterminates != this.expectedIndeterminates ||
+			this.responseNotMatches > 0) {
+			logger.fatal(dump.toString());
+		} else {
+			logger.info(dump.toString());
+		}
+	}
+	
+	protected void	resetStats() {
+		this.permits = 0;
+		this.denies = 0;
+		this.notapplicables = 0;
+		this.indeterminates = 0;
+		this.generatedpermits = 0;
+		this.generateddenies = 0;
+		this.generatednotapplicables = 0;
+		this.generatedindeterminates = 0;
+		this.responseMatches = 0;
+		this.responseNotMatches = 0;
+	}
+
+	public static void main(String[] args) {
+		try {
+			new TestBase(args).run();
+		} catch (ParseException | IOException | FactoryException e) {
+			logger.error(e);
+		} catch (HelpException e) {
+		}		
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/annotations/TestAnnotation.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/annotations/TestAnnotation.java b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/annotations/TestAnnotation.java
new file mode 100755
index 0000000..a1d679b
--- /dev/null
+++ b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/annotations/TestAnnotation.java
@@ -0,0 +1,232 @@
+/*
+ *                        AT&T - PROPRIETARY
+ *          THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+ *        AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+ *             ACCORDANCE WITH APPLICABLE AGREEMENTS.
+ *
+ *          Copyright (c) 2014 AT&T Knowledge Ventures
+ *              Unpublished and Not for Publication
+ *                     All Rights Reserved
+ */
+package com.att.research.xacmlatt.pdp.test.annotations;
+
+import java.io.IOException;
+import java.net.MalformedURLException;
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.Collection;
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.apache.commons.cli.ParseException;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.att.research.xacml.api.DataTypeException;
+import com.att.research.xacml.api.Response;
+import com.att.research.xacml.std.annotations.RequestParser;
+import com.att.research.xacml.std.annotations.XACMLAction;
+import com.att.research.xacml.std.annotations.XACMLAttribute;
+import com.att.research.xacml.std.annotations.XACMLEnvironment;
+import com.att.research.xacml.std.annotations.XACMLMultiRequest;
+import com.att.research.xacml.std.annotations.XACMLRequest;
+import com.att.research.xacml.std.annotations.XACMLRequestReference;
+import com.att.research.xacml.std.annotations.XACMLResource;
+import com.att.research.xacml.std.annotations.XACMLSubject;
+import com.att.research.xacml.std.datatypes.HexBinary;
+import com.att.research.xacml.std.datatypes.IPAddress;
+import com.att.research.xacml.std.datatypes.IPv4Address;
+import com.att.research.xacml.std.datatypes.ISO8601DateTime;
+import com.att.research.xacml.std.datatypes.ISO8601Time;
+import com.att.research.xacml.util.FactoryException;
+import com.att.research.xacmlatt.pdp.test.TestBase;
+
+/**
+ * This example application shows how to use annotations for Java classes to create requests to send to the
+ * engine.
+ * 
+ * @author pameladragosh
+ *
+ */
+public class TestAnnotation extends TestBase {
+	private static final Log logger	= LogFactory.getLog(TestAnnotation.class);
+	
+	private int	num;
+	
+	/**
+	 * This is a sample class that uses annotations. In addition to demonstrating how to use XACML annotations,
+	 * it also demonstrates the various Java objects that can be used and how the request parser will
+	 * resolve each object's datatype.
+	 * 
+	 * @author pameladragosh
+	 *
+	 */
+	@XACMLRequest(ReturnPolicyIdList=true)
+	public class MyRequestAttributes {
+		
+		public MyRequestAttributes(String user, String action, String resource) {
+			this.userID = user;
+			this.action = action;
+			this.resource = resource;
+			this.today = new Date();
+			this.yesterday = Calendar.getInstance();
+			this.yesterday.add(Calendar.DAY_OF_MONTH, -1);
+		}
+
+		@XACMLSubject(includeInResults=true)
+		String	userID;
+		
+		@XACMLSubject(attributeId="urn:oasis:names:tc:xacml:1.0:subject:subject-id-qualifier")
+		boolean admin = false;
+		
+		@XACMLSubject(attributeId="urn:oasis:names:tc:xacml:1.0:subject:key-info", issuer="com:foo:security")
+		HexBinary publicKey = new HexBinary(new byte[] {'1', '0'});
+		
+		@XACMLSubject(attributeId="urn:oasis:names:tc:xacml:1.0:subject:authentication-time")
+		ISO8601Time	authenticationTime = new ISO8601Time(8, 0, 0, 0);
+		
+		/**
+		 * Here our base object is "Object", but it is reflected as a Java "String". The parser
+		 * will then use the XACML http://www.w3.org/2001/XMLSchema#string as the datatype.
+		 */
+		@XACMLSubject(attributeId="urn:oasis:names:tc:xacml:1.0:subject:authentication-method")
+		Object authenticationMethod = new String("RSA Public Key");
+		
+		/**
+		 * Here our base object is "String", but we use the annotation for datatype to clarify
+		 * that the real XACML data type is http://www.w3.org/2001/XMLSchema#time. The parser will
+		 * use the data type factory to convert the "String" to a "ISO8601Time" Java object.
+		 */
+		@XACMLSubject(attributeId="urn:oasis:names:tc:xacml:1.0:subject:request-time", datatype="http://www.w3.org/2001/XMLSchema#time")
+		String requestTime = new String("13:20:00-05:00");
+		
+		@XACMLSubject(attributeId="urn:oasis:names:tc:xacml:1.0:subject:session-start-time")
+		ISO8601DateTime sessionStart = new ISO8601DateTime(TimeZone.getDefault().getID(), 2014, 1, 1, 10, 0, 0, 0);
+		
+		@XACMLSubject(attributeId="urn:oasis:names:tc:xacml:3.0:subject:authn-locality:ip-address")
+		IPAddress ip = new IPv4Address(new short[] {123, 134, 156, 255 }, null, null);
+		
+		@XACMLSubject(attributeId="urn:oasis:names:tc:xacml:3.0:subject:authn-locality:dns-name")
+		String dnsName = "localhost";
+		
+		@XACMLAction()
+		String	action;
+		
+		@XACMLAction(attributeId="urn:oasis:names:tc:xacml:1.0:action:implied-action")
+		long	impliedAction;
+		
+		@XACMLResource()
+		String	resource;
+		
+		@XACMLEnvironment()
+		Date		today;
+		
+		@XACMLEnvironment()
+		Calendar	yesterday;
+		
+		/**
+		 * This field demonstrates how the parser can detect collections and build a bag of values.
+		 */
+		@XACMLAttribute(attributeId="foo:bar:attribute")
+		Collection<Double>		fooBar = Arrays.asList(2.5, 3.5);
+		
+		/**
+		 * The XACMLAttribute annotation allows one to specify all the 
+		 */
+		@XACMLAttribute(category="foo:bar:category", attributeId="foo:bar:attribute2")
+		double		fooBar2 = 3.999;
+		
+		/**
+		 * This field demonstrates how the parser can detect arrays and build a bag of values.
+		 */
+		@XACMLAttribute(category="foo:bar:category", attributeId="foo:bar:attribute:many")
+		URI[]		fooBarMany = new URI[] {URI.create("file://opt/app/test"), URI.create("https://localhost:8443/")};
+		
+	};
+
+	@XACMLRequest(
+		Defaults="http://www.w3.org/TR/1999/Rec-xpath-19991116",
+		multiRequest=@XACMLMultiRequest(values={
+			@XACMLRequestReference(values={"subject1", "action", "resource"}),
+			@XACMLRequestReference(values={"subject2", "action", "resource"})})
+	)
+	public class MyMultiRequestAttributes {
+		
+		@XACMLSubject(id="subject1")
+		String	userID1 = "John";
+		
+		@XACMLSubject(id="subject2")
+		String	userID2 = "Ringo";
+
+		@XACMLAction(id="action")
+		String	action = "access";
+
+		@XACMLResource(id="resource")
+		String	resource = "www.mywebsite.com";
+	}
+
+	public TestAnnotation(String[] args) throws MalformedURLException, ParseException, HelpException {
+		super(args);
+	}
+
+	@Override
+	public void run() throws IOException, FactoryException {
+		//
+		// We are not going to iterate any existing request files. So we will override
+		// any TestBase code that assumes there are request files present.
+		//
+		//
+		// Configure ourselves
+		//
+		this.configure();
+		//
+		// Cycle through creating a few objects
+		//
+		this.num = 0;
+		this.doRequest(new MyRequestAttributes("John", "access", "www.mywebsite.com"));
+		this.num++;
+		this.doRequest(new MyRequestAttributes("Ringo", "access", "www.mywebsite.com"));
+		this.num++;
+		this.doRequest(new MyMultiRequestAttributes());
+		this.num++;
+	}
+
+	private void doRequest(Object info) {
+		try {
+			Response response = this.callPDP(RequestParser.parseRequest(info));
+			Path resultFile;
+			if (this.output != null) {
+				resultFile = Paths.get(this.output.toString(), "Response." + String.format("%03d", this.num) + ".json");
+			} else {
+				resultFile = Paths.get(this.directory, "results", "Response." + String.format("%03d", this.num) + ".json");
+			}
+			//
+			// Write the response to the result file
+			//
+			logger.info("Response is: " + response.toString());
+			if (resultFile != null) {
+				Files.write(resultFile, response.toString().getBytes());
+			}
+		} catch (IllegalArgumentException | IllegalAccessException | DataTypeException | IOException e) {
+			logger.error(e);
+			e.printStackTrace();
+		}
+	}
+	
+	public static void main(String[] args) {
+		try {
+			new TestAnnotation(args).run();
+		} catch (ParseException | IOException | FactoryException e) {
+			logger.error(e);
+		} catch (HelpException e) {
+			//
+			// ignore this, its thrown just to exit the application
+			// after dumping help to stdout.
+			//
+		}		
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/Conformance.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/Conformance.java b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/Conformance.java
new file mode 100755
index 0000000..06c17bd
--- /dev/null
+++ b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/Conformance.java
@@ -0,0 +1,625 @@
+/*
+ *                        AT&T - PROPRIETARY
+ *          THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+ *        AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+ *             ACCORDANCE WITH APPLICABLE AGREEMENTS.
+ *
+ *          Copyright (c) 2013 AT&T Knowledge Ventures
+ *              Unpublished and Not for Publication
+ *                     All Rights Reserved
+ */
+package com.att.research.xacmlatt.pdp.test.conformance;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.List;
+
+import com.att.research.xacml.api.Advice;
+import com.att.research.xacml.api.Attribute;
+import com.att.research.xacml.api.AttributeAssignment;
+import com.att.research.xacml.api.AttributeCategory;
+import com.att.research.xacml.api.AttributeValue;
+import com.att.research.xacml.api.IdReference;
+import com.att.research.xacml.api.Obligation;
+import com.att.research.xacml.api.Response;
+import com.att.research.xacml.api.Result;
+
+/**
+ * Conformance is an application that runs a <code>ConformanceTestSet</code> and dumps results comparing the actual and
+ * expected results.
+ * 
+ * TO RUN in Eclipse:
+ * This is run as a Java Application.
+ * You must first create a Run/Debug Configuration: 
+ * 		Under the Argument tab, in Program Arguments you must set the -i or --input command line argument.
+ *	 	You should also direct the output to a file using -o or --output. (default is Console)
+ *		See the init() method in this file for other useful arguments.
+ * 		Example for a Windows machine:
+ * 			-i testsets/conformance/xacml3.0-ct-v.0.4
+ * 			-o \Users\yourLogin\Downloads\conformance.txt
+ * 		You must also set the VM arguments:
+ * 			-Dxacml.properties=testsets/conformance/xacml.properties .
+ * 			-Dlog4j.configuration=.\logging.properties
+ * 
+ * @author car
+ * @version $Revision: 1.2 $
+ */
+public class Conformance {
+	private ConformanceScopeResolver scopeResolver;
+	private ConformanceTestEngine testEngine;
+	private ConformanceTestSet testSet			= new ConformanceTestSet();
+	private File outputFile;
+	private PrintWriter outputFileWriter;
+	
+	private List<String> testNamesToRun = new ArrayList<String>();
+	
+	private boolean verbose;
+	private boolean failuresOnly;
+	private boolean strict;
+	private boolean stopOnFirstError;
+	
+	private int testsRun;
+	private int decisionsMatch;
+	private int statusCodesMatch;
+	private int attributesMatch;
+	private int policyIdsMatch;
+	private int policySetIdsMatch;
+	private int associatedAdviceMatch;
+	private int obligationsMatch;
+	private int unknownFunctions;
+	
+	
+
+	
+	protected synchronized ConformanceScopeResolver getScopeResolver() {
+		if (this.scopeResolver == null) {
+			this.scopeResolver	= new ConformanceScopeResolver();
+			
+			/*
+			 * TODO:
+			 * Add the known scopes for the 2.0 conformance test.  This could be made more general by allowing loading
+			 * from a properties file eventually.
+			 */
+			try {
+				URI ID_SCOPE_ROOT	= new URI("urn:root");
+				URI ID_SCOPE_CHILD1	= new URI("urn:root:child1");
+				URI ID_SCOPE_CHILD2	= new URI("urn:root:child2");
+				URI ID_SCOPE_C1D1	= new URI("urn:root:child1:descendant1");
+				URI ID_SCOPE_C1D2	= new URI("urn:root:child1:descendant2");
+				URI ID_SCOPE_C2D1	= new URI("urn:root:child2:descendant1");
+				URI ID_SCOPE_C2D2	= new URI("urn:root:child2:descendant2");
+				
+				this.scopeResolver.add(ID_SCOPE_ROOT, ID_SCOPE_CHILD1);
+				this.scopeResolver.add(ID_SCOPE_CHILD1, ID_SCOPE_C1D1);
+				this.scopeResolver.add(ID_SCOPE_CHILD1, ID_SCOPE_C1D2);
+				this.scopeResolver.add(ID_SCOPE_ROOT, ID_SCOPE_CHILD2);
+				this.scopeResolver.add(ID_SCOPE_CHILD2, ID_SCOPE_C2D1);
+				this.scopeResolver.add(ID_SCOPE_CHILD2, ID_SCOPE_C2D2);
+			} catch (Exception ex) {
+				ex.printStackTrace(System.err);
+			}
+			
+		}
+		return this.scopeResolver;
+	}
+	
+	private void close() throws IOException {
+		if (this.outputFileWriter != null) {
+			this.outputFileWriter.close();
+		}
+	}
+	
+	private boolean init(String[] args) {
+		boolean lenientRequests	= true;
+		boolean lenientPolicies	= false;
+		// default is to not run any non-first-time iterations
+		int iterations			= -1;
+		String testSetDirectoryNames = "";
+		for (int i = 0 ; i < args.length ; ) {
+			
+			if (args[i].equals("-h") || args[i].equals("--help") || args[i].equals("-help")) {
+				printHelp();
+				return false;
+			}
+			
+			
+			// where the XML Request/Response files are located
+			if (args[i].equals("-i") || args[i].equals("--input")) {
+				i++;
+				while (i < args.length && !args[i].startsWith("-")) {
+					testSetDirectoryNames += " " + args[i];
+					try {
+						testSet.addConformanceTestSet(ConformanceTestSet.loadDirectory(new File(args[i])));
+					} catch (Exception ex) {
+						ex.printStackTrace(System.err);
+						return false;
+					}
+					i++;
+				}
+
+			// File path name where output will be put - default is stdout == Console
+			} else if (args[i].equals("-o") || args[i].equals("--output")) {
+				if (i+1 < args.length) {
+					this.outputFile	= new File(args[i+1]);
+					i	+= 2;
+				} else {
+					System.err.println("Missing argument to " + args[i] + " command line option");
+					return false;
+				}
+			// A list of specific test names (e.g.: -t IIA001 IIA007 IIIE301) - default is to run all tests
+			} else if (args[i].equals("-t") || args[i].equals("--tests")) {
+				i++;
+				while (i < args.length && !args[i].startsWith("-")) {
+					testNamesToRun.add(args[i]);
+					i++;
+				}
+				if (testNamesToRun.size() == 0) {
+					System.err.println("Missing test names after -t or --tests argument");
+					return false;
+				}
+			// Include full details in the response, both the expected reqsponse (from file) and the actual response
+			} else if (args[i].equals("-v") || args[i].equals("--verbose")) {
+				this.verbose	= true;
+				i++;
+			// Report only failures (success is silent)
+			} else if (args[i].equals("-f") || args[i].equals("--failures")) {
+				this.failuresOnly	= true;
+				i++;
+			// When set, the XML must not contain extra attibutes/elements.  Default is "lenient" where unexpected entries are ignored
+			} else if (args[i].equals("-s") || args[i].equals("--strict")) {
+				this.strict	= true;
+				i++;
+			// (self explanatory)
+			} else if (args[i].equals("--stop-on-error")) {
+				this.stopOnFirstError	= true;
+				i++;
+			} else if (args[i].equals("--lenient")) {
+				lenientPolicies	= true;
+				lenientRequests	= true;
+				i++;
+			} else if (args[i].equals("--lenient-policies")) {
+				lenientPolicies	= true;
+				i++;
+			} else if (args[i].equals("--lenient-requests")) {
+				lenientRequests	= true;
+				i++;
+			} else if (args[i].equals("--strict-policies")) {
+				lenientPolicies	= false;
+				i++;
+			} else if (args[i].equals("--strict-requests")) {
+				lenientRequests	= false;
+				i++;
+			} else if (args[i].equals("--iterations")) {
+				// this is a count of how many ADDITIONAL times the decide() should be called.
+				// The first time decide() is called it takes a long time to set up,
+				// so to get an accurate number for how fast a single Request is handled we need to ignore the time for the first run
+				// and timings for 1 or more non-first-time calls to decide().
+				if (i+1 < args.length) {
+					try {
+						iterations	= Integer.parseInt(args[i+1]);
+						i	+= 2;
+					} catch (NumberFormatException ex) {
+						System.err.println("Invalid iteration count '" + args[i+1] + "'");
+						return false;
+					}
+				} else {
+					System.err.println("Missing argument to " + args[i] + " command line option");
+					return false;					
+				}
+				if (iterations < 1) {
+					System.err.println("Cannot use --iterations " + iterations + ".  Must use an integer greater than 0");
+					return false;
+				}
+			} else {
+				System.err.println("Unknown command line option " + args[i]);
+				return false;
+			}
+		}
+	
+		this.testEngine	= new ConformanceTestEngine(this.getScopeResolver(), lenientRequests, lenientPolicies, iterations);
+
+		if (testSetDirectoryNames.length() == 0) {
+			System.err.println("No test set directory given (need -i or --iniput command line option)");
+			return false;
+		}
+		if (testSet.getListConformanceTests().size() == 0) {
+			System.err.println("No tests in given directories: " + testSetDirectoryNames);
+		}
+		
+		if (testNamesToRun.size() > 0) {
+			String s = "";
+			for (String name : testNamesToRun) {
+				s += ", " + name;
+			}
+			System.out.println("Tests limited to: " + s.substring(1));
+		}
+		
+		if (this.outputFile == null) {
+			this.outputFileWriter	= new PrintWriter(System.out);
+		} else {
+			try {
+				this.outputFileWriter	= new PrintWriter(new FileOutputStream(this.outputFile));
+			} catch (IOException ex) {
+				System.err.println("Cannot open " + this.outputFile.getAbsolutePath() + " for writing.");
+				return false;
+			}
+		}
+		
+		return true;
+	}
+	
+	private void printHelp() {
+		System.out.println("usage: Conformance --input <tests_directory> OPTIONS");
+		System.out.println("");
+		System.out.println(" -f, --failures		Only include failed tests in the output.  \n"+
+							"			Default is to include all test's results in the output file.");
+		System.out.println("");
+		System.out.println(" -h, --help		Prints help.");
+			
+		System.out.println("");
+		System.out.println(" -i, --input <dir>	Directory containing the XML Request/Response files.  \n"+
+							"			This may be multiple space-separated directory paths.  REQUIRED");
+		
+		System.out.println("");
+		System.out.println(" --iterations		The number of times to run through the set of tests in the input directory.");
+		
+		System.out.println("");
+		System.out.println(" --lenient		Allow both Requests and Policies to have unexpected elements, no data in <Content>, etc. \n"+
+							"			Default is to not allow anything that is not explicitly listed in the XACML spec.");
+		
+		System.out.println("");
+		System.out.println(" --lenient-policies	Allow Policies to have unexpected elements, no data in <Content>, etc.  \n" +
+							"			Default is to not allow anything that is not explicitly listed in the XACML spec.");
+		
+		System.out.println("");
+		System.out.println(" --lenient-requests	Allow Requests to have unexpected elements, no data in <Content>, etc.  \n" +
+							"			Default is to not allow anything that is not explicitly listed in the XACML spec.");
+		
+		System.out.println("");
+		System.out.println(" -o, --output <dir>	Directory where the output results file will be put.");
+		
+		System.out.println("");
+		System.out.println(" -s, --strict		Check both the Decision and all other parts of the Response (Attributes, Obligations and Advice). \n "+
+							"			Default is to check just the Decision.");
+		
+		System.out.println("");
+		System.out.println(" --stop-on-error	Stop running conformance tests the first time one fails.  Default is to continue through all tests.");
+
+		System.out.println("");
+		System.out.println(" --strict-policies	Require Policies to have no unexpected elements, data in <Content>, etc.  \n" +
+							"			This is the default, but can be used to override Policies when option --lenient is used.");
+		
+		System.out.println("");
+		System.out.println(" --strict-requests	Require Requests to have no unexpected elements, data in <Content>, etc.  \n" +
+							"			This is the default, but can be used to override Requests when option --lenient is used.");
+		
+		System.out.println("");
+		System.out.println(" -t, --tests <list of test names>	A space-separated list of specific tests to be run. \n" +
+							"			These are just the names of the tests as in 'IIA001 IIC178'.  \n" +
+							"			Default is to run all tests in the input directory.");
+		
+		System.out.println("");
+		System.out.println(" -v, --verbose 		The entire expected and actual Response objects in the output.  \n"+
+							"			Default is just a summary line.");
+		
+	}
+	
+	private boolean failed(ConformanceTestResult conformanceTestResult) {
+		ResponseMatchResult responseMatchResult	= conformanceTestResult.getResponseMatchResult();
+		if (responseMatchResult == null) {
+			return true;
+		}
+		if (!responseMatchResult.decisionsMatch() || !responseMatchResult.statusCodesMatch()) {
+			return true;
+		} else if (this.strict) {
+			if (!responseMatchResult.associatedAdviceMatches() ||
+				!responseMatchResult.attributesMatch() ||
+				!responseMatchResult.obligationsMatch() ||
+				!responseMatchResult.policyIdentifiersMatch() ||
+				!responseMatchResult.policySetIdentifiersMatch()
+					) {
+				return true;
+			}
+		}
+		return false;
+	}
+	
+	private void dump(AttributeAssignment attributeAssignment) {
+		this.outputFileWriter.println("\t\t\t\tAttributeAssignment:");
+		if (attributeAssignment.getCategory() != null) {
+			this.outputFileWriter.println("\t\t\t\t\tCategory: " + attributeAssignment.getCategory().stringValue());
+		}
+		if (attributeAssignment.getAttributeId() != null) {
+			this.outputFileWriter.println("\t\t\t\t\tAttributeId: " + attributeAssignment.getAttributeId().stringValue());
+		}
+		if (attributeAssignment.getDataTypeId() != null) {
+			this.outputFileWriter.println("\t\t\t\t\tDataType: " + attributeAssignment.getDataTypeId().stringValue());
+		}
+		if (attributeAssignment.getIssuer() != null) {
+			this.outputFileWriter.println("\t\t\t\t\tIssuer: " + attributeAssignment.getIssuer());
+		}
+		if (attributeAssignment.getAttributeValue() != null && attributeAssignment.getAttributeValue().getValue() != null) {
+			this.outputFileWriter.println("\t\t\t\t\tValue: " + attributeAssignment.getAttributeValue().getValue().toString());
+		}
+	}
+	
+	private void dump(Attribute attribute) {
+		this.outputFileWriter.println("\t\t\t\t\tAttribute: " + (attribute.getAttributeId() == null ? "" : attribute.getAttributeId().stringValue()));
+		if (attribute.getIssuer() != null) {
+			this.outputFileWriter.println("\t\t\t\t\t\tIssuer: " + attribute.getIssuer());
+		}
+		Iterator<AttributeValue<?>> iterAttributeValues	= attribute.getValues().iterator();
+		if (iterAttributeValues.hasNext()) {
+			this.outputFileWriter.println("\t\t\t\t\t\tValues: ");
+			while (iterAttributeValues.hasNext()) {
+				this.outputFileWriter.print("\t\t\t\t\t\t\t");
+				AttributeValue<?> attributeValue	= iterAttributeValues.next();
+				if (attributeValue.getDataTypeId() != null) {
+					this.outputFileWriter.print("DataType: " + attributeValue.getDataTypeId().stringValue() + " ");
+				}
+				if (attributeValue.getValue() != null) {
+					this.outputFileWriter.print("Value: " + attributeValue.getValue().toString());
+				}
+				this.outputFileWriter.println();
+			}
+		}
+	}
+	
+	private void dump(AttributeCategory attributeCategory) {
+		this.outputFileWriter.println("\t\t\tAttributeCategory: " + (attributeCategory.getCategory() == null ? "" : attributeCategory.getCategory().stringValue()));
+		Collection<Attribute> listAttributes	= attributeCategory.getAttributes();
+		if (listAttributes.size() > 0) {
+			this.outputFileWriter.println("\t\t\t\tAttributes:");
+			for (Attribute attribute: listAttributes) {
+				this.dump(attribute);
+			}
+		}
+	}
+	
+	private void dump(Result result) {
+		this.outputFileWriter.println("\t\t======== Result ==========");
+		this.outputFileWriter.println("\t\tDecision: " + (result.getDecision() == null ? "null" : result.getDecision().name()));
+		if (result.getStatus() == null) {
+			this.outputFileWriter.println("\t\tStatus: null");
+		} else {
+			this.outputFileWriter.println("\t\tStatus:");
+			if (result.getStatus().getStatusCode() != null) {
+				this.outputFileWriter.println("\t\t\tStatusCode: " + result.getStatus().getStatusCode().toString());
+			}
+			if (result.getStatus().getStatusMessage() != null) {
+				this.outputFileWriter.println("\t\t\tStatusMessage: " + result.getStatus().getStatusMessage());
+			}
+			if (result.getStatus().getStatusDetail() != null) {
+				this.outputFileWriter.println("\t\t\tStatusDetail: " + result.getStatus().getStatusDetail().toString());
+			}
+		}
+		Collection<Advice> listAdvice	= result.getAssociatedAdvice();
+		if (listAdvice.size() > 0) {
+			this.outputFileWriter.println("\t\tAdvice:");
+			for (Advice advice : listAdvice) {
+				if (advice.getId() != null) {
+					this.outputFileWriter.println("\t\t\tId: " + advice.getId().stringValue());
+				}
+				Collection<AttributeAssignment> attributeAssignments	= advice.getAttributeAssignments();
+				if (attributeAssignments.size() > 0) {
+					this.outputFileWriter.println("\t\t\tAttributeAssignments:");
+					for (AttributeAssignment attributeAssignment: attributeAssignments) {
+						this.dump(attributeAssignment);
+					}
+				}				
+			}
+		}
+		Collection<Obligation> listObligations	= result.getObligations();
+		if (listObligations.size() > 0) {
+			for (Obligation obligation: listObligations) {
+				if (obligation.getId() != null) {
+					this.outputFileWriter.println("\t\t\tId: " + obligation.getId().stringValue());
+				}
+				Collection<AttributeAssignment> attributeAssignments	= obligation.getAttributeAssignments();
+				if (attributeAssignments.size() > 0) {
+					this.outputFileWriter.println("\t\t\tAttributeAssignments:");
+					for (AttributeAssignment attributeAssignment : attributeAssignments) {
+						this.dump(attributeAssignment);
+					}
+				}				
+			}
+		}
+		Collection<AttributeCategory> listAttributeCategories	= result.getAttributes();
+		if (listAttributeCategories.size() > 0) {
+			this.outputFileWriter.println("\t\tAttributes:");
+			for (AttributeCategory attributeCategory : listAttributeCategories) {
+				this.dump(attributeCategory);
+			}
+		}
+		Collection<IdReference> listIdReferences;
+		if ((listIdReferences = result.getPolicyIdentifiers()).size() > 0) {
+			this.outputFileWriter.println("\t\tPolicyIds:");
+			for (IdReference idReference : listIdReferences) {
+				this.outputFileWriter.println("\t\t\t" + idReference.toString());				
+			}
+		}
+		if ((listIdReferences = result.getPolicySetIdentifiers()).size() > 0) {
+			this.outputFileWriter.println("\t\tPolicySetIds:");
+			for (IdReference idReference : listIdReferences) {
+				this.outputFileWriter.println("\t\t\t" + idReference.toString());				
+			}
+		}
+	}
+	
+	private void dump(String label, Response response) {
+		this.outputFileWriter.println("\t========== " + label + "==========");
+		if (response == null) {
+			this.outputFileWriter.println("null");
+			return;
+		}
+		
+		for (Result result : response.getResults()) {
+			this.dump(result);
+		}
+	}
+	
+	private void dump(ConformanceTestResult conformanceTestResult) {
+		
+		ResponseMatchResult responseMatchResult	= conformanceTestResult.getResponseMatchResult();
+		if (this.verbose) {
+			this.outputFileWriter.println("========== Test " + conformanceTestResult.getConformanceTest().getTestName() + " ==========");
+			this.dump("Expected Response", conformanceTestResult.getExpectedResponse());
+			this.dump("Actual Response", conformanceTestResult.getActualResponse());
+			if (responseMatchResult != null) {
+				this.outputFileWriter.println("\t========== Matching ==========");
+				this.outputFileWriter.println("\tDecisions Match? " + responseMatchResult.decisionsMatch());
+				this.outputFileWriter.println("\tStatus Codes Match? " + responseMatchResult.statusCodesMatch());
+				this.outputFileWriter.println("\tAttributes Match? " + responseMatchResult.attributesMatch());
+				this.outputFileWriter.println("\tPolicyIds Match? " + responseMatchResult.policyIdentifiersMatch());
+				this.outputFileWriter.println("\tPolicySetIds Match? " + responseMatchResult.policySetIdentifiersMatch());
+				this.outputFileWriter.println("\tAssociated Advice Match? " + responseMatchResult.associatedAdviceMatches());
+				this.outputFileWriter.println("\tObligations Match? " + responseMatchResult.obligationsMatch());
+				this.outputFileWriter.println("========== End ==========");				
+			}
+		} else {
+			String testName	= conformanceTestResult.getConformanceTest().getTestName();
+			if (responseMatchResult != null) {
+				Iterator<ResultMatchResult> iterResultMatches	= responseMatchResult.getResultMatchResults();
+				if (iterResultMatches == null || !iterResultMatches.hasNext()) {
+					this.outputFileWriter.println(testName);
+				} else {
+					while (iterResultMatches.hasNext()) {
+						ResultMatchResult resultMatchResult	= iterResultMatches.next();
+						this.outputFileWriter.printf("%s,%s,%s,%s,%s,%s,%s,%s,%d,%d\n",
+								testName,
+								resultMatchResult.decisionsMatch(),
+								resultMatchResult.statusCodesMatch(),
+								resultMatchResult.attributesMatch(),
+								resultMatchResult.policyIdentifiersMatch(),
+								resultMatchResult.policySetIdentifiersMatch(),
+								resultMatchResult.associatedAdviceMatches(),
+								resultMatchResult.obligationsMatch(),
+								conformanceTestResult.getFirstCallTime(),
+								conformanceTestResult.getAverageTotalLoopTime()
+								);
+					}
+				}
+			}
+		}
+		this.outputFileWriter.flush();
+	}
+	
+	private boolean run(ConformanceTest conformanceTest) throws Exception {
+		this.testsRun++;
+		ConformanceTestResult conformanceTestResult	= this.testEngine.run(conformanceTest);
+		boolean bFailed								= true;
+		if (conformanceTestResult != null) {
+			ResponseMatchResult responseMatchResult	= conformanceTestResult.getResponseMatchResult();
+			if (responseMatchResult != null) {
+				if (responseMatchResult.decisionsMatch()) {
+					this.decisionsMatch++;
+					this.statusCodesMatch	+= (responseMatchResult.statusCodesMatch() ? 1 : 0);
+					this.attributesMatch	+= (responseMatchResult.attributesMatch() ? 1 : 0);
+					this.policyIdsMatch		+= (responseMatchResult.policyIdentifiersMatch() ? 1 : 0);
+					this.policySetIdsMatch	+= (responseMatchResult.policySetIdentifiersMatch() ? 1 : 0);
+					this.associatedAdviceMatch	+= (responseMatchResult.associatedAdviceMatches() ? 1 : 0);
+					this.obligationsMatch		+= (responseMatchResult.obligationsMatch() ? 1 : 0);
+				}
+				this.unknownFunctions		+= (responseMatchResult.unknownFunction() ? 1 : 0);
+				bFailed	= this.failed(conformanceTestResult);
+				if (bFailed || !this.failuresOnly) {
+					this.dump(conformanceTestResult);
+				}
+			} else if (conformanceTestResult.getError() != null) {
+				this.outputFileWriter.println(conformanceTestResult.getError());
+			}
+		}
+		return (!bFailed || !this.stopOnFirstError);
+	}
+	
+	private void run() throws Exception {
+		long tStart	= System.currentTimeMillis();
+		
+		if (!this.verbose) {
+			this.outputFileWriter.println("Test,Decision,Status,Attributes,PolicyIds,PolicySetIds,Advice,Obligations");
+		}
+		Iterator<ConformanceTest> iterConformanceTests	= this.testSet.getConformanceTests();
+		boolean bContinue								= true;
+		while (bContinue && iterConformanceTests.hasNext()) {
+//			bContinue	= this.run(iterConformanceTests.next());
+			ConformanceTest test = iterConformanceTests.next();
+			if (testNamesToRun.size() > 0) {
+				if ( ! testNamesToRun.contains(test.getTestName())) {
+					continue;
+				}
+			}
+			bContinue	= this.run(test);
+		}
+		
+		long tElapsed	= System.currentTimeMillis() - tStart;
+		
+		if (this.verbose) {
+			this.outputFileWriter.println("Tests run = " + this.testsRun);
+			this.outputFileWriter.println("Decisions match = " + this.decisionsMatch);
+			this.outputFileWriter.println("Status Codes match = " + this.statusCodesMatch);
+			this.outputFileWriter.println("Attributes match = " + this.attributesMatch);
+			this.outputFileWriter.println("PolicyIds match = " + this.policyIdsMatch);
+			this.outputFileWriter.println("PolicySetIds match = " + this.policySetIdsMatch);
+			this.outputFileWriter.println("Associated Advice match = " + this.associatedAdviceMatch);
+			this.outputFileWriter.println("Obligations match = " + this.obligationsMatch);
+			this.outputFileWriter.println("Unknown functions = " + this.unknownFunctions);
+		} else {
+			this.outputFileWriter.printf("Total (%d),%d,%d,%d,%d,%d,%d,%d,%d\n",
+					this.testsRun,
+					this.decisionsMatch,
+					this.statusCodesMatch,
+					this.attributesMatch,
+					this.policyIdsMatch,
+					this.policySetIdsMatch,
+					this.associatedAdviceMatch,
+					this.obligationsMatch,
+					this.unknownFunctions);
+		}
+		
+		if (tElapsed > 0) {
+			long tHours		= tElapsed / (60*60*1000);
+			tElapsed		= tElapsed - tHours * 60 * 60 *1000;
+			long tMinutes	= tElapsed / (60*1000);
+			tElapsed		= tElapsed - tMinutes * 60 * 1000;
+			long tSeconds	= tElapsed / 1000;
+			tElapsed		= tElapsed - tSeconds * 1000;
+			
+			this.outputFileWriter.printf("Elapsed time = %02d:%02d:%02d.%03d\n", tHours, tMinutes, tSeconds, tElapsed);
+			this.outputFileWriter.printf("First decide time in nano-seconds %d\n", this.testEngine.getFirstDecideTime());
+			this.outputFileWriter.printf("Total Multiple decide time in nano-seconds %d\n", this.testEngine.getDecideTimeMultiple());
+			
+			this.outputFileWriter.printf("\nAverage First decide time in nano-seconds %d\n", this.testEngine.getAvgFirstDecideTime());
+			this.outputFileWriter.printf("Average decide time after first call in nano-seconds %d\n", this.testEngine.getAvgDecideTimeMultiple());
+		}
+	}
+	
+	public Conformance() {
+	}
+
+	public static void main(String[] args) {
+		Conformance conformance	= new Conformance();
+		try {
+			if (conformance.init(args)) {
+				conformance.run();
+			}
+			
+		} catch (Exception ex) {
+			ex.printStackTrace(System.err);
+			System.exit(1);
+		} finally {
+			try {
+				conformance.close();
+			} catch (IOException ex) {
+				ex.printStackTrace(System.err);
+			}
+		}
+		System.exit(0);
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformancePIPEngine.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformancePIPEngine.java b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformancePIPEngine.java
new file mode 100755
index 0000000..2d5f0dd
--- /dev/null
+++ b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformancePIPEngine.java
@@ -0,0 +1,233 @@
+/*
+ *                        AT&T - PROPRIETARY
+ *          THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+ *        AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+ *             ACCORDANCE WITH APPLICABLE AGREEMENTS.
+ *
+ *          Copyright (c) 2013 AT&T Knowledge Ventures
+ *              Unpublished and Not for Publication
+ *                     All Rights Reserved
+ */
+package com.att.research.xacmlatt.pdp.test.conformance;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.text.ParseException;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.att.research.xacml.api.Attribute;
+import com.att.research.xacml.api.AttributeValue;
+import com.att.research.xacml.api.DataType;
+import com.att.research.xacml.api.DataTypeException;
+import com.att.research.xacml.api.DataTypeFactory;
+import com.att.research.xacml.api.Identifier;
+import com.att.research.xacml.api.pip.PIPException;
+import com.att.research.xacml.api.pip.PIPFinder;
+import com.att.research.xacml.api.pip.PIPRequest;
+import com.att.research.xacml.api.pip.PIPResponse;
+import com.att.research.xacml.std.IdentifierImpl;
+import com.att.research.xacml.std.StdMutableAttribute;
+import com.att.research.xacml.std.pip.StdPIPResponse;
+import com.att.research.xacml.std.pip.engines.ConfigurableEngine;
+import com.att.research.xacml.util.FactoryException;
+
+/**
+ * ConformancePIPEngine implements the {@link com.att.research.xacml.api.pip.PIPFinder} interface to find attributes
+ * loaded from a text file containing the following fields:
+ * 	category-id,attribute-id,datatype-id,issuer,value
+ * 
+ * @author car
+ * @version $Revision: 1.1 $
+ */
+public class ConformancePIPEngine implements ConfigurableEngine {
+	public static final String PROP_DESCRIPTION	= ".description";
+	public static final String PROP_FILE		= ".file";
+	
+	private static final Log logger	= LogFactory.getLog(ConformancePIPEngine.class);
+	
+	private String name;
+	private String description;
+	private Map<String,PIPResponse> cache	= new HashMap<String,PIPResponse>();
+	private List<Attribute> listAttributes	= new ArrayList<Attribute>();
+	private DataTypeFactory dataTypeFactory;
+	
+	public ConformancePIPEngine() {
+		
+	}
+	
+	protected DataTypeFactory getDataTypeFactory() throws FactoryException {
+		if (this.dataTypeFactory == null) {
+			this.dataTypeFactory	= DataTypeFactory.newInstance();
+		}
+		return this.dataTypeFactory;
+	}
+	
+	protected static String generateKey(PIPRequest pipRequest) {
+		StringBuilder stringBuilder	= new StringBuilder(pipRequest.getCategory().toString());
+		stringBuilder.append('+');
+		stringBuilder.append(pipRequest.getAttributeId().toString());
+		stringBuilder.append('+');
+		stringBuilder.append(pipRequest.getDataTypeId().toString());
+		String issuer	= pipRequest.getIssuer();
+		if (issuer != null) {
+			stringBuilder.append('+');
+			stringBuilder.append(issuer);
+		}
+		return stringBuilder.toString();
+	}
+	
+	protected void store(String[] fields) throws FactoryException {
+		DataTypeFactory thisDataTypeFactory	= this.getDataTypeFactory();
+		Identifier identifierCategory		= new IdentifierImpl(fields[0]);
+		Identifier identifierAttribute		= new IdentifierImpl(fields[1]);
+		Identifier identifierDataType		= new IdentifierImpl(fields[2]);
+		String issuer						= (fields.length == 5 ? fields[3] : null);
+		String value						= fields[fields.length - 1];
+		
+		DataType<?> dataType				= thisDataTypeFactory.getDataType(identifierDataType);
+		if (dataType == null) {
+			logger.error("Unknown data type " + identifierDataType.stringValue());
+			return;
+		}
+		
+		AttributeValue<?> attributeValue	= null;
+		try {
+			attributeValue	= dataType.createAttributeValue(value);
+		} catch (DataTypeException ex) {
+			throw new FactoryException("DataTypeException creating AttributeValue", ex);
+		}
+		Attribute attribute					= new StdMutableAttribute(identifierCategory, identifierAttribute, attributeValue, issuer, false);
+		this.listAttributes.add(attribute);
+	}
+	
+	public void loadAttributes(File fileAttributes) throws IOException, ParseException, FactoryException {
+		if (fileAttributes != null) {
+			if (!fileAttributes.exists()) {
+				throw new FileNotFoundException("Attributes file " + fileAttributes.getAbsolutePath() + " not found.");
+			} else if (!fileAttributes.canRead()) {
+				throw new IOException("Attributes file " + fileAttributes.getAbsolutePath() + " is not readable.");
+			}
+			
+			BufferedReader bufferedReader	= null;
+			try {
+				bufferedReader	= new BufferedReader(new InputStreamReader(new FileInputStream(fileAttributes)));
+				String line;
+				while ((line = bufferedReader.readLine()) != null) {
+					if (line.length() > 0) {
+						String[] fields	= line.split("[|]",-1);
+						if (fields.length < 4) {
+							logger.warn("Not enough fields in record \"" + line + "\"");
+							continue;
+						}
+						this.store(fields);
+						
+					}
+				}
+			} finally {
+				if (bufferedReader != null) {
+					bufferedReader.close();
+				}
+			}
+		}
+	}
+	
+	protected Attribute findAttribute(PIPRequest pipRequest) {
+		Attribute attributeResult			= null;
+		Iterator<Attribute> iterAttributes	= this.listAttributes.iterator();
+		while ((attributeResult == null) && iterAttributes.hasNext()) {
+			Attribute attributeTest	= iterAttributes.next();
+			if (pipRequest.getCategory().equals(attributeTest.getCategory()) &&
+				pipRequest.getAttributeId().equals(attributeTest.getAttributeId()) &&
+				(pipRequest.getIssuer() == null || pipRequest.getIssuer().equals(attributeTest.getIssuer()))) {
+				attributeResult	= attributeTest;
+			}
+		}
+		return attributeResult;
+	}
+
+	@Override
+	public PIPResponse getAttributes(PIPRequest pipRequest, PIPFinder pipFinder) throws PIPException {
+		String pipRequestKey	= generateKey(pipRequest);
+		PIPResponse pipResponse	= this.cache.get(pipRequestKey);
+		if (pipResponse != null) {
+			return pipResponse;
+		}
+		Attribute attributeMatch	= this.findAttribute(pipRequest);
+		if (attributeMatch == null) {
+			return StdPIPResponse.PIP_RESPONSE_EMPTY;
+		}
+		/*
+		 * Iterate through the values and only return the ones that match the requested data type
+		 */
+		List<AttributeValue<?>> matchingValues	= new ArrayList<AttributeValue<?>>();
+		Iterator<AttributeValue<?>> iterAttributeValues	= attributeMatch.getValues().iterator();
+		while (iterAttributeValues.hasNext()) {
+			AttributeValue<?> attributeValue	= iterAttributeValues.next();
+			if (pipRequest.getDataTypeId().equals(attributeValue.getDataTypeId())) {
+				matchingValues.add(attributeValue);
+			}
+		}
+		if (matchingValues.size() > 0) {
+			Attribute attributeResponse	= new StdMutableAttribute(attributeMatch.getCategory(), attributeMatch.getAttributeId(), matchingValues, attributeMatch.getIssuer(), false);
+			pipResponse					= new StdPIPResponse(attributeResponse);
+			this.cache.put(pipRequestKey, pipResponse);
+		}
+		return pipResponse;
+	}
+
+	@Override
+	public String getName() {
+		return this.name;
+	}
+
+	@Override
+	public String getDescription() {
+		return this.description;
+	}
+
+	@Override
+	public void configure(String id, Properties properties) throws PIPException {
+		this.name	= id;
+		this.description	= properties.getProperty(id + PROP_DESCRIPTION);
+		if (this.description == null) {
+			this.description	= "PIPEngine for the Conformance tests that loads attributes from a CSV file";
+		}
+		String pipFile		= properties.getProperty(id + PROP_FILE);
+		if (pipFile != null) {
+			try {
+				this.loadAttributes(new File(pipFile));
+			} catch (Exception ex) {
+				logger.error("Exception loading PIP file " + pipFile, ex);
+				throw new PIPException("Exception loading PIP file " + pipFile, ex);
+			}
+		}
+	}
+
+	@Override
+	public Collection<PIPRequest> attributesRequired() {
+		return Collections.emptyList();
+	}
+
+	@Override
+	public Collection<PIPRequest> attributesProvided() {
+		//
+		// We could return everything in our list
+		//
+		return Collections.emptyList();
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformanceRepository.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformanceRepository.java b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformanceRepository.java
new file mode 100755
index 0000000..f402a73
--- /dev/null
+++ b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformanceRepository.java
@@ -0,0 +1,118 @@
+/*
+ *                        AT&T - PROPRIETARY
+ *          THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+ *        AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+ *             ACCORDANCE WITH APPLICABLE AGREEMENTS.
+ *
+ *          Copyright (c) 2013 AT&T Knowledge Ventures
+ *              Unpublished and Not for Publication
+ *                     All Rights Reserved
+ */
+package com.att.research.xacmlatt.pdp.test.conformance;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Properties;
+
+import com.att.research.xacml.util.StringUtils;
+import com.att.research.xacml.util.XACMLProperties;
+import com.att.research.xacmlatt.pdp.std.StdPolicyFinderFactory;
+
+/**
+ * ConformanceRepository represents one or more policies for a single policy test, which will include one or more root policies, and
+ * zero or more referenced policies.
+ * 
+ * @author car
+ * @version $Revision$
+ */
+public class ConformanceRepository {
+	private List<File> rootPolicies			= new ArrayList<File>();
+	private List<File> referencedPolicies	= new ArrayList<File>();
+	
+	private void setXACMLProperty(String propertyName, List<File> listFiles) {
+		Iterator<File> iterFiles			= listFiles.iterator();
+		StringBuilder stringBuilderIdList	= new StringBuilder();
+		while (iterFiles.hasNext()) {
+			File file	= iterFiles.next();
+			if (stringBuilderIdList.length() > 0) {
+				stringBuilderIdList.append(',');
+			}
+			stringBuilderIdList.append(file.getName());
+			
+			XACMLProperties.setProperty(file.getName() + StdPolicyFinderFactory.PROP_FILE, file.getAbsolutePath());
+		}
+		XACMLProperties.setProperty(propertyName, stringBuilderIdList.toString());
+	}
+	
+	public ConformanceRepository() {
+	}
+	
+	public void setXACMLProperties() {
+		if (this.rootPolicies.size() > 0) {
+			this.setXACMLProperty(XACMLProperties.PROP_ROOTPOLICIES, this.rootPolicies);
+		}
+		if (this.referencedPolicies.size() > 0) {
+			this.setXACMLProperty(XACMLProperties.PROP_REFERENCEDPOLICIES, this.referencedPolicies);
+		}
+	}
+	
+	private void loadProperty(File fileDir, Properties properties, String propertyName, List<File> listFiles) {
+		String fileNameList	= properties.getProperty(propertyName);
+		if (fileNameList != null) {
+			String[] fileNameArray	= fileNameList.split("[,]",0);
+			if (fileNameArray != null && fileNameArray.length > 0) {
+				for (String fileName : fileNameArray) {
+					File file	= new File(fileDir, fileName);
+					if (file.exists() && file.canRead()) {
+						listFiles.add(file);
+					}
+				}
+			}
+		}
+	}
+	
+	public void load(File fileRepository) throws IOException {
+		Properties propertiesRepository	= new Properties();
+		try (InputStream is = new FileInputStream(fileRepository)) {
+			propertiesRepository.load(is);
+		}
+		this.loadProperty(fileRepository.getParentFile(), propertiesRepository, XACMLProperties.PROP_ROOTPOLICIES, this.rootPolicies);
+		this.loadProperty(fileRepository.getParentFile(), propertiesRepository, XACMLProperties.PROP_REFERENCEDPOLICIES, this.referencedPolicies);
+	}
+	
+	public void addRootPolicy(File filePolicy) {
+		this.rootPolicies.add(filePolicy);
+	}
+	
+	public boolean hasRootPolicy() {
+		return (this.rootPolicies.size() > 0);
+	}
+	
+	@Override
+	public String toString() {
+		StringBuilder stringBuilder	= new StringBuilder("{");
+		boolean needComma			= false;
+		
+		if (this.rootPolicies != null && this.rootPolicies.size() > 0) {
+			stringBuilder.append("rootPolicies=");
+			stringBuilder.append(StringUtils.toString(this.rootPolicies.iterator()));
+			needComma	= true;
+		}
+		if (this.referencedPolicies != null && this.referencedPolicies.size() > 0) {
+			if (needComma) {
+				stringBuilder.append(',');
+			}
+			stringBuilder.append("referencedPolicies=");
+			stringBuilder.append(StringUtils.toString(this.referencedPolicies.iterator()));
+			needComma	= true;
+		}
+		stringBuilder.append('}');
+		return stringBuilder.toString();
+	}
+	
+}

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformanceScopeResolver.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformanceScopeResolver.java b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformanceScopeResolver.java
new file mode 100755
index 0000000..8359591
--- /dev/null
+++ b/openaz-xacml-test/src/test/java/com/att/research/xacmlatt/pdp/test/conformance/ConformanceScopeResolver.java
@@ -0,0 +1,112 @@
+/*
+ *                        AT&T - PROPRIETARY
+ *          THIS FILE CONTAINS PROPRIETARY INFORMATION OF
+ *        AT&T AND IS NOT TO BE DISCLOSED OR USED EXCEPT IN
+ *             ACCORDANCE WITH APPLICABLE AGREEMENTS.
+ *
+ *          Copyright (c) 2013 AT&T Knowledge Ventures
+ *              Unpublished and Not for Publication
+ *                     All Rights Reserved
+ */
+package com.att.research.xacmlatt.pdp.test.conformance;
+
+import java.net.URI;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import com.att.research.xacml.api.Attribute;
+import com.att.research.xacml.api.AttributeValue;
+import com.att.research.xacml.api.pdp.ScopeQualifier;
+import com.att.research.xacml.api.pdp.ScopeResolver;
+import com.att.research.xacml.api.pdp.ScopeResolverException;
+import com.att.research.xacml.api.pdp.ScopeResolverResult;
+import com.att.research.xacml.std.StdMutableAttribute;
+import com.att.research.xacml.std.StdScopeResolverResult;
+import com.att.research.xacml.std.StdStatus;
+import com.att.research.xacml.std.StdStatusCode;
+import com.att.research.xacml.std.datatypes.DataTypes;
+
+/**
+ * ConformanceScopeResolver implements {@link com.att.research.xacml.pdp.ScopeResolver} for the conformance tests
+ * using a fixed set of hierarchical resources defined in a map.
+ * 
+ * @author car
+ * @version $Revision$
+ */
+public class ConformanceScopeResolver implements ScopeResolver {
+	private Log logger									= LogFactory.getLog(ConformanceScopeResolver.class);
+	private Map<URI, List<URI>> mapIdentifierToChildren	= new HashMap<URI,List<URI>>();
+	
+	public ConformanceScopeResolver() {
+	}
+	
+	public void add(URI identifierRoot, URI identifierChild) {
+		List<URI> listChildrenRoot	= this.mapIdentifierToChildren.get(identifierRoot);
+		if (listChildrenRoot == null) {
+			listChildrenRoot	= new ArrayList<URI>();
+			this.mapIdentifierToChildren.put(identifierRoot, listChildrenRoot);
+		}
+		listChildrenRoot.add(identifierChild);
+	}
+	
+	private void addChildren(Attribute attributeResourceId, URI urnResourceIdValue, boolean bDescendants, List<Attribute> listAttributes) {
+		List<URI> listChildren	= this.mapIdentifierToChildren.get(urnResourceIdValue);
+		if (listChildren != null) {
+			for (URI uriChild : listChildren) {
+				AttributeValue<URI> attributeValueURI	= null;
+				try {
+					attributeValueURI	= DataTypes.DT_ANYURI.createAttributeValue(uriChild);
+					if (attributeValueURI != null) {
+						listAttributes.add(new StdMutableAttribute(attributeResourceId.getCategory(), attributeResourceId.getAttributeId(), attributeValueURI, attributeResourceId.getIssuer(), attributeResourceId.getIncludeInResults()));
+					}
+				} catch (Exception ex) {
+					this.logger.error("Exception converting URI to an AttributeValue");
+				}
+				if (bDescendants) {
+					this.addChildren(attributeResourceId, uriChild, bDescendants, listAttributes);
+				}
+			}
+		}
+	}
+	
+	private void addChildren(Attribute attributeResourceId, boolean bDescendants, List<Attribute> listAttributes) {
+		/*
+		 * Iterate over the values that are URNs
+		 */
+		Iterator<AttributeValue<URI>> iterAttributeValueURNs	= attributeResourceId.findValues(DataTypes.DT_ANYURI);
+		if (iterAttributeValueURNs != null) {
+			while (iterAttributeValueURNs.hasNext()) {
+				this.addChildren(attributeResourceId, iterAttributeValueURNs.next().getValue(), bDescendants, listAttributes);
+			}
+		}
+	}
+	
+	@Override
+	public ScopeResolverResult resolveScope(Attribute attributeResourceId, ScopeQualifier scopeQualifier) throws ScopeResolverException {
+		List<Attribute> listAttributes	= new ArrayList<Attribute>();
+		switch(scopeQualifier) {
+		case CHILDREN:
+			listAttributes.add(attributeResourceId);
+			this.addChildren(attributeResourceId, false, listAttributes);
+			break;
+		case DESCENDANTS:
+			listAttributes.add(attributeResourceId);
+			this.addChildren(attributeResourceId, true, listAttributes);
+			break;
+		case IMMEDIATE:
+			listAttributes.add(attributeResourceId);
+			break;
+		default:
+			this.logger.error("Unknown ScopeQualifier: " + scopeQualifier.name());
+			return new StdScopeResolverResult(new StdStatus(StdStatusCode.STATUS_CODE_SYNTAX_ERROR, "Unknown ScopeQualifier " + scopeQualifier.name()));
+		}
+		return new StdScopeResolverResult(listAttributes);
+	}
+
+}