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:42 UTC

[42/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-pap-rest/src/main/java/com/att/research/xacml/rest/XACMLPapServlet.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/src/main/java/com/att/research/xacml/rest/XACMLPapServlet.java b/openaz-xacml-pap-rest/src/main/java/com/att/research/xacml/rest/XACMLPapServlet.java
new file mode 100755
index 0000000..e57a36b
--- /dev/null
+++ b/openaz-xacml-pap-rest/src/main/java/com/att/research/xacml/rest/XACMLPapServlet.java
@@ -0,0 +1,1529 @@
+/*
+ *                        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.xacml.rest;
+
+
+import com.att.research.xacml.api.pap.*;
+import com.att.research.xacml.std.pap.StdPDP;
+import com.att.research.xacml.std.pap.StdPDPGroup;
+import com.att.research.xacml.std.pap.StdPDPItemSetChangeNotifier.StdItemSetChangeListener;
+import com.att.research.xacml.std.pap.StdPDPStatus;
+import com.att.research.xacml.util.FactoryException;
+import com.att.research.xacml.util.XACMLProperties;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.google.common.base.Splitter;
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import javax.servlet.Servlet;
+import javax.servlet.ServletConfig;
+import javax.servlet.ServletException;
+import javax.servlet.annotation.WebInitParam;
+import javax.servlet.annotation.WebServlet;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.net.*;
+import java.util.*;
+import java.util.concurrent.CopyOnWriteArrayList;
+
+/**
+ * Servlet implementation class XacmlPapServlet
+ * 
+ * 
+ * @author pameladragosh
+ */
+@WebServlet(
+		description = "Implements the XACML PAP RESTful API.", 
+		urlPatterns = { "/" }, 
+		loadOnStartup=1,
+		initParams = {
+				@WebInitParam(name = "XACML_PROPERTIES_NAME", value = "xacml.pap.properties", description = "The location of the properties file holding configuration information.")
+		})
+
+public class XACMLPapServlet extends HttpServlet implements StdItemSetChangeListener, Runnable {
+	private static final long serialVersionUID = 1L;
+	private static final Log logger	= LogFactory.getLog(XACMLPapServlet.class);
+	
+	/*
+	 * 
+	 * papEngine - This is our engine workhorse that manages the PDP Groups and Nodes.
+	 */
+	private PAPEngine papEngine = null;
+	
+	/*
+	 * This PAP instance's own URL.
+	 * 
+	 * Need this when creating URLs to send to the PDPs so they can GET the Policy files from this process. 
+	 */
+	private static String papURL = null;
+	
+	/*
+	 * List of Admin Console URLs.
+	 * Used to send notifications when configuration changes.
+	 * 
+	 * The CopyOnWriteArrayList *should* protect from concurrency errors.
+	 * This list is seldom changed but often read, so the costs of this approach make sense.
+	 */
+	private static final CopyOnWriteArrayList<String> adminConsoleURLStringList = new CopyOnWriteArrayList<String>();
+	
+	/*
+	 * This thread may be invoked upon startup to initiate sending PDP policy/pip configuration when
+	 * this servlet starts. Its configurable by the admin.
+	 */
+	private Thread initiateThread = null;
+	
+	/*
+	// The heartbeat thread.
+	*/
+	private static Heartbeat heartbeat = null;
+	private static Thread heartbeatThread = null;
+	
+    /**
+     * @see HttpServlet#HttpServlet()
+     */
+    public XACMLPapServlet() {
+        super();
+    }
+
+	/**
+	 * @see Servlet#init(ServletConfig)
+	 */
+	public void init(ServletConfig config) throws ServletException {
+		try {
+			//
+			// Initialize
+			//
+			XACMLRest.xacmlInit(config);
+			//
+			// Load the properties
+			//
+			XACMLRest.loadXacmlProperties(null, null);
+			//
+			// Load our PAP engine, first create a factory
+			//
+			PAPEngineFactory factory = PAPEngineFactory.newInstance(XACMLProperties.getProperty(XACMLProperties.PROP_PAP_PAPENGINEFACTORY));
+			//
+			// The factory knows how to go about creating a PAP Engine
+			//
+			this.papEngine = factory.newEngine();			
+			//
+			// we are about to call the PDPs and give them their configuration.
+			// To do that we need to have the URL of this PAP so we can construct the Policy file URLs
+			//
+			XACMLPapServlet.papURL = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URL);
+			//
+			// Sanity check that a URL was defined somewhere, its essential.
+			//
+			// How to check that its valid? We can validate the form, but since we are in the init() method we
+			// are not fully loaded yet so we really couldn't ping ourself to see if the URL will work. One
+			// will have to look for errors in the PDP logs to determine if they are failing to initiate a
+			// request to this servlet.
+			//
+			if (XACMLPapServlet.papURL == null) {
+				throw new PAPException("The property " + XACMLRestProperties.PROP_PAP_URL + " is not valid: " + XACMLPapServlet.papURL);
+			}
+			//
+			// Configurable - have the PAP servlet initiate sending the latest PDP policy/pip configuration
+			// to all its known PDP nodes.
+			//
+			// Note: parseBoolean will return false if there is no property defined. This is fine for a default.
+			//
+			if (Boolean.parseBoolean(XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_INITIATE_PDP_CONFIG))) {
+				this.initiateThread = new Thread(this);
+				this.initiateThread.start();
+			}
+			//
+			// After startup, the PAP does Heartbeats to each of the PDPs periodically
+			//
+			XACMLPapServlet.heartbeat = new Heartbeat(this.papEngine);
+			XACMLPapServlet.heartbeatThread = new Thread(XACMLPapServlet.heartbeat);
+			XACMLPapServlet.heartbeatThread.start();
+		} catch (FactoryException | PAPException e) {
+			logger.error("Failed to create engine", e);
+			throw new ServletException ("PAP not initialized; error: "+e);
+		} catch (Exception e) {
+			logger.error("Failed to create engine - unexpected error: ", e);
+			throw new ServletException ("PAP not initialized; unexpected error: "+e);		}
+	}
+
+	/**
+	 * Thread used only during PAP startup to initiate change messages to all known PDPs.
+	 * This must be on a separate thread so that any GET requests from the PDPs during this update can be serviced.
+	 */
+	@Override
+	public void run() {
+		//
+		// send the current configuration to all the PDPs that we know about
+		//
+		changed();
+	}
+	
+
+	/**
+	 * @see Servlet#destroy()
+	 * 
+	 * Depending on how this servlet is run, we may or may not care about cleaning up the resources.
+	 * For now we assume that we do care.
+	 */
+	public void destroy() {
+		//
+		// Make sure our threads are destroyed
+		//
+		if (XACMLPapServlet.heartbeatThread != null) {
+			//
+			// stop the heartbeat
+			//
+			try {
+				if (XACMLPapServlet.heartbeat != null) {
+					XACMLPapServlet.heartbeat.terminate();
+				}
+				XACMLPapServlet.heartbeatThread.interrupt();
+				XACMLPapServlet.heartbeatThread.join();
+			} catch (InterruptedException e) {
+				logger.error(e);
+			}
+		}
+		if (this.initiateThread != null) {
+			try {
+				this.initiateThread.interrupt();
+				this.initiateThread.join();
+			} catch (InterruptedException e) {
+				logger.error(e);
+			}
+		}
+	}
+	
+	/**
+	 * 
+	 * Called by:
+	 * 	- PDP nodes to register themselves with the PAP, and
+	 * 	- Admin Console to make changes in the PDP Groups.
+	 * 
+	 * @see HttpServlet#doPost(HttpServletRequest request, HttpServletResponse response)
+	 */
+	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+		try {
+
+			XACMLRest.dumpRequest(request);
+
+			// since getParameter reads the content string, explicitly get the content before doing that.
+			// Simply getting the inputStream seems to protect it against being consumed by getParameter.
+			request.getInputStream();
+			
+			//
+			// Is this from the Admin Console?
+			//
+			String groupId = request.getParameter("groupId");
+			if (groupId != null) {
+				//
+				// this is from the Admin Console, so handle separately
+				//
+				doACPost(request, response, groupId);
+				return;
+			}
+			
+			//
+			//  Request is from a PDP.
+			//	It is coming up and asking for its config
+			//
+			
+			//
+			// Get the PDP's ID
+			//
+			String id = this.getPDPID(request);
+			logger.info("doPost from: " + id);
+			//
+			// Get the PDP Object
+			//
+			PDP pdp = this.papEngine.getPDP(id);
+			//
+			// Is it known?
+			//
+			if (pdp == null) {
+				logger.info("Unknown PDP: " + id);
+				try {
+					this.papEngine.newPDP(id, this.papEngine.getDefaultGroup(), id, "Registered on first startup");
+				} catch (NullPointerException | PAPException e) {
+					logger.error("Failed to create new PDP", e);
+					response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, e.getMessage());
+					return;
+				}
+				// get the PDP we just created
+				pdp = this.papEngine.getPDP(id);
+				if (pdp == null) {
+					String message = "Failed to create new PDP for id: " + id;
+					logger.error(message);
+					response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR, message);
+					return;
+				}
+			}
+			//
+			// Get the PDP's Group
+			//
+			PDPGroup group = this.papEngine.getPDPGroup(pdp);
+			if (group == null) {
+				response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "PDP not associated with any group, even the default");
+				return;
+			}
+			//
+			// Determine what group the PDP node is in and get
+			// its policy/pip properties.
+			//
+			Properties policies = group.getPolicyProperties();
+			Properties pipconfig = group.getPipConfigProperties();
+			//
+			// Get the current policy/pip configuration that the PDP has
+			//
+			Properties pdpProperties = new Properties();
+			pdpProperties.load(request.getInputStream());
+			logger.info("PDP Current Properties: " + pdpProperties.toString());
+			logger.info("Policies: " + (policies != null ? policies.toString() : "null"));
+			logger.info("Pip config: " + (pipconfig != null ? pipconfig.toString() : "null"));
+			//
+			// Validate the node's properties
+			//
+			boolean isCurrent = this.isPDPCurrent(policies, pipconfig, pdpProperties);
+			//
+			// Send back current configuration
+			//
+			if (isCurrent == false) {
+				//
+				// Tell the PDP we are sending back the current policies/pip config
+				//
+				logger.info("PDP configuration NOT current.");
+				if (policies != null) {
+					//
+					// Put URL's into the properties in case the PDP needs to
+					// retrieve them.
+					//
+					this.populatePolicyURL(request.getRequestURL(), policies);
+					//
+					// Copy the properties to the output stream
+					//
+					policies.store(response.getOutputStream(), "");
+				}
+				if (pipconfig != null) {
+					//
+					// Copy the properties to the output stream
+					//
+					pipconfig.store(response.getOutputStream(), "");
+				}
+				//
+				// We are good - and we are sending them information
+				//
+				response.setStatus(HttpServletResponse.SC_OK);
+//TODO - Correct?				
+				setPDPSummaryStatus(pdp, PDPStatus.Status.OUT_OF_SYNCH);
+			} else {
+				//
+				// Tell them they are good
+				//
+				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+				
+ //TODO - Correct?
+				setPDPSummaryStatus(pdp, PDPStatus.Status.UP_TO_DATE);
+
+			}
+			//
+			// tell the AC that something changed
+			//
+			notifyAC();
+		} catch (PAPException e) {
+			logger.debug("POST exception: " + e, e);
+			response.sendError(500, e.getMessage());
+			return;
+		}
+	}
+
+	
+	/**
+	 * @see HttpServlet#doGet(HttpServletRequest request, HttpServletResponse response)
+	 */
+	protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+		try {
+			XACMLRest.dumpRequest(request);
+			
+			// Is this from the Admin Console?
+			String groupId = request.getParameter("groupId");
+			if (groupId != null) {
+				// this is from the Admin Console, so handle separately
+				doACGet(request, response, groupId);
+				return;
+			}
+			//
+			// Get the PDP's ID
+			//
+			String id = this.getPDPID(request);
+			logger.info("doGet from: " + id);
+			//
+			// Get the PDP Object
+			//
+			PDP pdp = this.papEngine.getPDP(id);
+			//
+			// Is it known?
+			//
+			if (pdp == null) {
+				//
+				// Check if request came from localhost
+				//
+				String message = "Unknown PDP: " + id + " from " + request.getRemoteHost() + " us: " + request.getLocalAddr();
+				logger.info(message);
+				if (request.getRemoteHost().equals("localhost") ||
+					request.getRemoteHost().equals("127.0.0.1") ||
+					request.getRemoteHost().equals(request.getLocalAddr())) {
+					//
+					// Return status information - basically all the groups
+					//
+					Set<PDPGroup> groups = papEngine.getPDPGroups();
+					
+					// convert response object to JSON and include in the response
+		            ObjectMapper mapper = new ObjectMapper();
+		            mapper.writeValue(response.getOutputStream(),  groups);
+					response.setHeader("content-type", "application/json");
+					response.setStatus(HttpServletResponse.SC_OK);
+					return;
+				}
+				response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message);
+				return;
+			}
+			//
+			// Get the PDP's Group
+			//
+			PDPGroup group = this.papEngine.getPDPGroup(pdp);
+			if (group == null) {
+				String message = "No group associated with pdp " + pdp.getId();
+				logger.warn(message);
+				response.sendError(HttpServletResponse.SC_UNAUTHORIZED, message);
+				return;
+			}
+			//
+			// Which policy do they want?
+			//
+			String policyId = request.getParameter("id");
+			if (policyId == null) {
+				String message = "Did not specify an id for the policy";
+				logger.warn(message);
+				response.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+				return;
+			}
+			PDPPolicy policy = group.getPolicy(policyId);
+			if (policy == null) {
+				String message = "Unknown policy: " + policyId;
+				logger.warn(message);
+				response.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+				return;
+			}
+			//
+			// Get its stream
+			//
+			try (InputStream is = policy.getStream(); OutputStream os = response.getOutputStream()) {
+				//
+				// Send the policy back
+				//
+				IOUtils.copy(is, os);
+				
+				response.setStatus(HttpServletResponse.SC_OK);
+			} catch (PAPException e) {
+				String message = "Failed to open policy id " + policyId;
+				logger.error(message);
+				response.sendError(HttpServletResponse.SC_NOT_FOUND, message);
+			}
+		}  catch (PAPException e) {
+			logger.error("GET exception: " + e, e);
+			response.sendError(500, e.getMessage());
+			return;
+		}
+	}
+
+	protected String	getPDPID(HttpServletRequest request) {
+		String pdpURL = request.getHeader(XACMLRestProperties.PROP_PDP_HTTP_HEADER_ID);
+		if (pdpURL == null || pdpURL.isEmpty()) {
+			//
+			// Should send back its port for identification
+			//
+			logger.warn("PDP did not send custom header");
+			pdpURL = "";
+		}
+		return  pdpURL;
+	}
+	
+	private boolean isPDPCurrent(Properties policies, Properties pipconfig, Properties pdpProperties) {
+		String localRootPolicies = policies.getProperty(XACMLProperties.PROP_ROOTPOLICIES);
+		String localReferencedPolicies = policies.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES);
+		if (localRootPolicies == null || localReferencedPolicies == null) {
+			logger.warn("Missing property on PAP server: RootPolicies="+localRootPolicies+"  ReferencedPolicies="+localReferencedPolicies);
+			return false;
+		}
+		//
+		// Compare the policies and pipconfig properties to the pdpProperties
+		//
+		try {
+			//
+			// the policy properties includes only xacml.rootPolicies and 
+			// xacml.referencedPolicies without any .url entries
+			//
+			Properties pdpPolicies = XACMLProperties.getPolicyProperties(pdpProperties, false);
+			Properties pdpPipConfig = XACMLProperties.getPipProperties(pdpProperties);
+			if (localRootPolicies.equals(pdpPolicies.getProperty(XACMLProperties.PROP_ROOTPOLICIES)) &&
+					localReferencedPolicies.equals(pdpPolicies.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES)) &&
+					pdpPipConfig.equals(pipconfig)) {
+				//
+				// The PDP is current
+				//
+				return true;
+			}
+		} catch (Exception e) {
+			// we get here if the PDP did not include either xacml.rootPolicies or xacml.pip.engines,
+			// or if there are policies that do not have a corresponding ".url" property.
+			// Either of these cases means that the PDP is not up-to-date, so just drop-through to return false.
+		}
+		return false;
+	}
+
+	private void populatePolicyURL(StringBuffer urlPath, Properties policies) {
+		String lists[] = new String[2];
+		lists[0] = policies.getProperty(XACMLProperties.PROP_ROOTPOLICIES);
+		lists[1] = policies.getProperty(XACMLProperties.PROP_REFERENCEDPOLICIES);
+		for (String list : lists) {
+			if (list != null && list.isEmpty() == false) {
+				for (String id : Splitter.on(',').trimResults().omitEmptyStrings().split(list)) {
+					String url = urlPath + "?id=" + id;
+					logger.info("Policy URL for " + id + ": " + url);
+					policies.setProperty(id + ".url", url);
+				}
+			}
+		}
+	}
+	
+	
+	/**
+	 * @see HttpServlet#doPut(HttpServletRequest request, HttpServletResponse response)
+	 */
+	protected void doPut(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+		XACMLRest.dumpRequest(request);
+		//
+		// since getParameter reads the content string, explicitly get the content before doing that.
+		// Simply getting the inputStream seems to protect it against being consumed by getParameter.
+		//
+		request.getInputStream();
+		//
+		// See if this is Admin Console registering itself with us
+		//
+		String acURLString = request.getParameter("adminConsoleURL");
+		if (acURLString != null) {
+			//
+			// remember this Admin Console for future updates
+			//
+			if ( ! adminConsoleURLStringList.contains(acURLString)) {
+				adminConsoleURLStringList.add(acURLString);
+			}
+			if (logger.isDebugEnabled()) {
+				logger.debug("Admin Console registering with URL: " + acURLString);
+			}
+			response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+			return;
+		}
+		//
+		// Is this some other operation from the Admin Console?
+		//
+		String groupId = request.getParameter("groupId");
+		if (groupId != null) {
+			//
+			// this is from the Admin Console, so handle separately
+			//
+			doACPut(request, response, groupId);
+			return;
+		}
+		//
+		// We do not expect anything from anywhere else.
+		// This method is here in case we ever need to support other operations.
+		//
+		response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Request does not have groupId");
+	}
+	
+	/**
+	 * @see HttpServlet#doDelete(HttpServletRequest request, HttpServletResponse response)
+	 */
+	protected void doDelete(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
+		XACMLRest.dumpRequest(request);
+		//
+		// Is this from the Admin Console?
+		//
+		String groupId = request.getParameter("groupId");
+		if (groupId != null) {
+			//
+			// this is from the Admin Console, so handle separately
+			//
+			doACDelete(request, response, groupId);
+			return;
+		}
+		//
+		// We do not expect anything from anywhere else.
+		// This method is here in case we ever need to support other operations.
+		//
+		response.sendError(HttpServletResponse.SC_BAD_REQUEST, "Request does not have groupId");
+	}
+	//
+	// Admin Console request handling
+	//
+	
+	/**
+	 * Requests from the Admin Console to GET info about the Groups and PDPs
+	 * 
+	 * @param request
+	 * @param response
+	 * @param groupId
+	 * @throws ServletException
+	 * @throws java.io.IOException
+	 */
+	private void doACGet(HttpServletRequest request, HttpServletResponse response, String groupId) throws ServletException, IOException {
+		try {
+			String parameterDefault = request.getParameter("default");
+			String pdpId = request.getParameter("pdpId");
+			String pdpGroup = request.getParameter("getPDPGroup");
+			if ("".equals(groupId)) {
+				// request IS from AC but does not identify a group by name
+				if (parameterDefault != null) {
+					// Request is for the Default group (whatever its id)
+					PDPGroup group = papEngine.getDefaultGroup();
+					
+					// convert response object to JSON and include in the response
+		            ObjectMapper mapper = new ObjectMapper();
+		            mapper.writeValue(response.getOutputStream(),  group);
+		            
+					if (logger.isDebugEnabled()) {
+						logger.debug("GET Default group req from '" + request.getRequestURL() + "'");
+					}
+					response.setStatus(HttpServletResponse.SC_OK);
+					response.setHeader("content-type", "application/json");
+					response.getOutputStream().close();
+					return;
+					
+				} else if (pdpId != null) {
+					// Request is related to a PDP
+					if (pdpGroup == null) {
+						// Request is for the PDP itself
+						// Request is for the (unspecified) group containing a given PDP
+						PDP pdp = papEngine.getPDP(pdpId);
+						
+						// convert response object to JSON and include in the response
+			            ObjectMapper mapper = new ObjectMapper();
+			            mapper.writeValue(response.getOutputStream(),  pdp);
+			            
+						if (logger.isDebugEnabled()) {
+							logger.debug("GET pdp '" + pdpId + "' req from '" + request.getRequestURL() + "'");
+						}
+						response.setStatus(HttpServletResponse.SC_OK);
+						response.setHeader("content-type", "application/json");
+						response.getOutputStream().close();
+						return;
+					
+					} else {
+						// Request is for the (unspecified) group containing a given PDP
+						PDP pdp = papEngine.getPDP(pdpId);
+						PDPGroup group = papEngine.getPDPGroup(pdp);
+						
+						// convert response object to JSON and include in the response
+			            ObjectMapper mapper = new ObjectMapper();
+			            mapper.writeValue(response.getOutputStream(),  group);
+			            
+						if (logger.isDebugEnabled()) {
+							logger.debug("GET PDP '" + pdpId + "' Group req from '" + request.getRequestURL() + "'");
+						}
+						response.setStatus(HttpServletResponse.SC_OK);
+						response.setHeader("content-type", "application/json");
+						response.getOutputStream().close();
+						return;
+					}
+					
+				} else {
+					// request is for top-level properties about all groups
+					Set<PDPGroup> groups = papEngine.getPDPGroups();
+					
+					// convert response object to JSON and include in the response
+		            ObjectMapper mapper = new ObjectMapper();
+		            mapper.writeValue(response.getOutputStream(),  groups);
+					
+//TODO
+// In "notification" section, ALSO need to tell AC about other changes (made by other ACs)?'
+//TODO add new PDP notification (or just "config changed" notification) in appropriate place
+		            if (logger.isDebugEnabled()) {
+		            	logger.debug("GET All groups req");
+		            }
+					response.setStatus(HttpServletResponse.SC_OK);
+					response.setHeader("content-type", "application/json");
+					response.getOutputStream().close();
+					return;
+				}
+			}
+			
+			// for all other GET operations the group must exist before the operation can be done
+			PDPGroup group = papEngine.getGroup(groupId);
+			if (group == null) {
+				logger.error("Unknown groupId '" + groupId + "'");
+				response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unknown groupId '" + groupId +"'");
+				return;
+			}
+			
+			
+			// Figure out which request this is based on the parameters
+			String policyId = request.getParameter("policyId");
+			
+			if (policyId != null) {
+//				// retrieve a policy
+//				PDPPolicy policy = papEngine.getPDPPolicy(policyId);
+//				
+//				// convert response object to JSON and include in the response
+//	            ObjectMapper mapper = new ObjectMapper();
+//	            mapper.writeValue(response.getOutputStream(),  pdp);
+//	            
+//	        	logger.debug("GET group '" + group.getId() + "' req from '" + request.getRequestURL() + "'");
+//				response.setStatus(HttpServletResponse.SC_OK);
+//				response.setHeader("content-type", "application/json");
+//				response.getOutputStream().close();
+//				return;
+				response.sendError(HttpServletResponse.SC_BAD_REQUEST, "GET Policy not implemented");
+				
+			} else {
+				// No other parameters, so return the identified Group
+				
+				// convert response object to JSON and include in the response
+	            ObjectMapper mapper = new ObjectMapper();
+	            mapper.writeValue(response.getOutputStream(),  group);
+	            
+	            if (logger.isDebugEnabled()) {
+	            	logger.debug("GET group '" + group.getId() + "' req from '" + request.getRequestURL() + "'");
+	            }
+				response.setStatus(HttpServletResponse.SC_OK);
+				response.setHeader("content-type", "application/json");
+				response.getOutputStream().close();
+				return;
+			}
+			
+			//
+			// Currently there are no other GET calls from the AC.
+			// The AC uses the "GET All Groups" operation to fill its local cache and uses that cache for all other GETs without calling the PAP.
+			// Other GETs that could be called:
+			//				Specific Group	(groupId=<groupId>)
+			//				A Policy		(groupId=<groupId> policyId=<policyId>)
+			//				A PDP			(groupId=<groupId> pdpId=<pdpId>)
+		
+	//TODO - implement other GET operations if needed
+	
+			logger.error("UNIMPLEMENTED ");
+			response.sendError(HttpServletResponse.SC_BAD_REQUEST, "UNIMPLEMENTED");
+		} catch (PAPException e) {
+			logger.error("AC Get exception: " + e, e);
+			response.sendError(500, e.getMessage());
+			return;
+		}
+		
+	}
+
+		
+	/**
+	 * Requests from the Admin Console for operations not on single specific objects
+	 * 
+	 * @param request
+	 * @param response
+	 * @param groupId
+	 * @throws ServletException
+	 * @throws java.io.IOException
+	 */
+	private void doACPost(HttpServletRequest request, HttpServletResponse response, String groupId) throws ServletException, IOException {
+		try {
+			String groupName = request.getParameter("groupName");
+			String groupDescription = request.getParameter("groupDescription");
+			if (groupName != null && groupDescription != null) {
+				// Args:	      group=<groupId> groupName=<name> groupDescription=<description>            <= create a new group
+				String unescapedName = URLDecoder.decode(groupName, "UTF-8");
+				String unescapedDescription = URLDecoder.decode(groupDescription, "UTF-8");
+				try {
+					papEngine.newGroup(unescapedName, unescapedDescription);
+				} catch (Exception e) {
+					logger.error("Unable to create new group: " + e.getLocalizedMessage());
+					response.sendError(500, "Unable to create new group '" + groupId + "'");
+					return;
+				}
+				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+				if (logger.isDebugEnabled()) {
+					logger.debug("New Group '" + groupId + "' created");
+				}
+				// tell the Admin Consoles there is a change
+				notifyAC();
+				// new group by definition has no PDPs, so no need to notify them of changes
+				return;
+			}
+			
+			// for all remaining POST operations the group must exist before the operation can be done
+			PDPGroup group = papEngine.getGroup(groupId);
+			if (group == null) {
+				logger.error("Unknown groupId '" + groupId + "'");
+				response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unknown groupId '" + groupId +"'");
+				return;
+			}
+			
+			// determine the operation needed based on the parameters in the request
+			if (request.getParameter("policyId") != null) {
+				//	Args:        group=<groupId> policy=<policyId>		<= copy file
+				// copy a policy from the request contents into a file in the group's directory on this machine
+				String policyId = request.getParameter("policyId");
+				try {
+					((StdPDPGroup) group).copyPolicyToFile(policyId, request.getInputStream());
+				} catch (Exception e) {
+					String message = "Policy '" + policyId + "' not copied to group '" + groupId +"': " + e;
+					logger.error(message);
+					response.sendError(500, message);
+					return;
+				}
+				// policy file copied ok
+				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+				if (logger.isDebugEnabled()) {
+					logger.debug("policy '" + policyId + "' copied to directory for group '" + groupId + "'");
+				}
+				return;
+				
+			} else if (request.getParameter("default") != null) {
+				// Args:       group=<groupId> default=true               <= make default
+				// change the current default group to be the one identified in the request.
+				//
+				// This is a POST operation rather than a PUT "update group" because of the side-effect that the current default group is also changed.
+				// It should never be the case that multiple groups are currently marked as the default, but protect against that anyway.
+				try {
+					papEngine.SetDefaultGroup(group);
+				} catch (Exception e) {
+					logger.error("Unable to set group: " + e.getLocalizedMessage());
+					response.sendError(500, "Unable to set group '" + groupId + "' to default");
+					return;
+				}
+				
+				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+				if (logger.isDebugEnabled()) {
+					logger.debug("Group '" + groupId + "' set to be default");
+				}
+				// Notify the Admin Consoles that something changed
+				// For now the AC cannot handle anything more detailed than the whole set of PDPGroups, so just notify on that
+//TODO - Future: FIGURE OUT WHAT LEVEL TO NOTIFY: 2 groups or entire set - currently notify AC to update whole configuration of all groups
+				notifyAC();
+				// This does not affect any PDPs in the existing groups, so no need to notify them of this change
+				return;
+				
+			} else if (request.getParameter("pdpId") != null) {
+				// Args:       group=<groupId> pdpId=<pdpId>               <= move PDP to group
+				String pdpId = request.getParameter("pdpId");
+				PDP pdp = papEngine.getPDP(pdpId);
+				
+				PDPGroup originalGroup = papEngine.getPDPGroup(pdp);
+				
+				papEngine.movePDP(pdp, group);
+				
+				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+				if (logger.isDebugEnabled()) {
+					logger.debug("PDP '" + pdp.getId() +"' moved to group '" + group.getId() + "' set to be default");
+				}
+				
+				// update the status of both the original group and the new one
+				((StdPDPGroup)originalGroup).resetStatus();
+				((StdPDPGroup)group).resetStatus();
+				
+				// Notify the Admin Consoles that something changed
+				// For now the AC cannot handle anything more detailed than the whole set of PDPGroups, so just notify on that
+				notifyAC();
+				// Need to notify the PDP that it's config may have changed
+				pdpChanged(pdp);
+				return;
+				
+				
+			}
+		} catch (PAPException e) {
+			logger.error("AC POST exception: " + e, e);
+			response.sendError(500, e.getMessage());
+			return;
+		}
+	}
+
+	/**
+	 * Requests from the Admin Console to create new items or update existing ones
+	 * 
+	 * @param request
+	 * @param response
+	 * @param groupId
+	 * @throws ServletException
+	 * @throws java.io.IOException
+	 */
+	private void doACPut(HttpServletRequest request, HttpServletResponse response, String groupId) throws ServletException, IOException {
+		try {
+			
+			
+			// for PUT operations the group may or may not need to exist before the operation can be done
+			PDPGroup group = papEngine.getGroup(groupId);
+	
+			// determine the operation needed based on the parameters in the request
+
+			// for remaining operations the group must exist before the operation can be done
+			if (group == null) {
+				logger.error("Unknown groupId '" + groupId + "'");
+				response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unknown groupId '" + groupId +"'");
+				return;
+			}
+			if (request.getParameter("policy") != null) {
+	//        group=<groupId> policy=<policyId> contents=policy file               <= Create new policy file in group dir, or replace it if it already exists (do not touch properties)
+	//TODO - currently this is done by the AC, but it should be done here by getting the policy file out of the contents and saving to disk
+				logger.error("PARTIALLY IMPLEMENTED!!!  ACTUAL CHANGES SHOULD BE MADE BY PAP SERVLET!!! ");
+				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+				return;
+			} else if (request.getParameter("pdpId") != null) {
+				// ARGS:        group=<groupId> pdpId=<pdpId/URL>          <= create a new PDP or Update an Existing one
+				
+				String pdpId = request.getParameter("pdpId");
+				
+            	// get the request content into a String
+            	String json = null;
+    			// read the inputStream into a buffer (trick found online scans entire input looking for end-of-file)
+    		    java.util.Scanner scanner = new java.util.Scanner(request.getInputStream());
+    		    scanner.useDelimiter("\\A");
+    		    json =  scanner.hasNext() ? scanner.next() : "";
+    		    scanner.close();
+    		    logger.info("JSON request from AC: " + json);
+            	
+            	// convert Object sent as JSON into local object
+	            ObjectMapper mapper = new ObjectMapper();
+				
+	            Object objectFromJSON = mapper.readValue(json, StdPDP.class);
+
+				if (pdpId == null ||
+						objectFromJSON == null ||
+						! (objectFromJSON instanceof StdPDP) ||
+						((StdPDP)objectFromJSON).getId() == null ||
+						! ((StdPDP)objectFromJSON).getId().equals(pdpId)) {
+					logger.error("PDP new/update had bad input. pdpId=" + pdpId + " objectFromJSON="+objectFromJSON);
+					response.sendError(500, "Bad input, pdpid="+pdpId+" object="+objectFromJSON);
+				}
+				StdPDP pdp = (StdPDP) objectFromJSON;
+				
+				if (papEngine.getPDP(pdpId) == null) {
+					// this is a request to create a new PDP object
+					papEngine.newPDP(pdp.getId(), group, pdp.getName(), pdp.getDescription());
+				} else {
+					// this is a request to update the pdp
+					papEngine.updatePDP(pdp);
+				}
+
+				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+				if (logger.isDebugEnabled()) {
+					logger.debug("PDP '" + pdpId + "' created/updated");
+				}
+				
+				// adjust the group's state including the new PDP
+				((StdPDPGroup)group).resetStatus();
+				
+				// tell the Admin Consoles there is a change
+				notifyAC();
+				// this might affect the PDP, so notify it of the change
+				pdpChanged(pdp);
+				return;
+			} else if (request.getParameter("pipId") != null) {
+	//                group=<groupId> pipId=<pipEngineId> contents=pip properties              <= add a PIP to pip config, or replace it if it already exists (lenient operation) 
+	//TODO
+				logger.error("UNIMPLEMENTED ");
+				response.sendError(HttpServletResponse.SC_BAD_REQUEST, "UNIMPLEMENTED");
+				return;
+			} else {
+				// Assume that this is an update of an existing PDP Group
+				// ARGS:        group=<groupId>         <= Update an Existing Group
+								
+            	// get the request content into a String
+            	String json = null;
+    			// read the inputStream into a buffer (trick found online scans entire input looking for end-of-file)
+    		    java.util.Scanner scanner = new java.util.Scanner(request.getInputStream());
+    		    scanner.useDelimiter("\\A");
+    		    json =  scanner.hasNext() ? scanner.next() : "";
+    		    scanner.close();
+    		    logger.info("JSON request from AC: " + json);
+            	
+            	// convert Object sent as JSON into local object
+	            ObjectMapper mapper = new ObjectMapper();
+	            
+	            Object objectFromJSON  = mapper.readValue(json, StdPDPGroup.class);
+
+				if (objectFromJSON == null ||
+						! (objectFromJSON instanceof StdPDPGroup) ||
+						! ((StdPDPGroup)objectFromJSON).getId().equals(group.getId())) {
+					logger.error("Group update had bad input. id=" + group.getId() + " objectFromJSON="+objectFromJSON);
+					response.sendError(500, "Bad input, id="+group.getId() +" object="+objectFromJSON);
+				}
+				
+				// The Path on the PAP side is not carried on the RESTful interface with the AC
+				// (because it is local to the PAP)
+				// so we need to fill that in before submitting the group for update
+				((StdPDPGroup)objectFromJSON).setDirectory(((StdPDPGroup)group).getDirectory());
+
+				papEngine.updateGroup((StdPDPGroup)objectFromJSON);
+
+
+				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+				if (logger.isDebugEnabled()) {
+					logger.debug("Group '" + group.getId() + "' updated");
+				}
+				// tell the Admin Consoles there is a change
+				notifyAC();
+				// Group changed, which might include changing the policies
+				groupChanged(group);
+				return;
+			}
+		} catch (PAPException e) {
+			logger.error("AC PUT exception: " + e, e);
+			response.sendError(500, e.getMessage());
+			return;
+		}
+	}
+	
+	/**
+	 * Requests from the Admin Console to delete/remove items
+	 * 
+	 * @param request
+	 * @param response
+	 * @param groupId
+	 * @throws ServletException
+	 * @throws java.io.IOException
+	 */
+	private void doACDelete(HttpServletRequest request, HttpServletResponse response, String groupId) throws ServletException, IOException {
+		try {
+			// for all DELETE operations the group must exist before the operation can be done
+			PDPGroup group = papEngine.getGroup(groupId);
+			if (group == null) {
+				logger.error("Unknown groupId '" + groupId + "'");
+				response.sendError(HttpServletResponse.SC_NOT_FOUND, "Unknown groupId '" + groupId +"'");
+				return;
+			}
+			// determine the operation needed based on the parameters in the request
+			if (request.getParameter("policy") != null) {
+	//        group=<groupId> policy=<policyId>  [delete=<true|false>]       <= delete policy file from group
+	//TODO
+				logger.error("UNIMPLEMENTED ");
+				response.sendError(HttpServletResponse.SC_BAD_REQUEST, "UNIMPLEMENTED");
+				return;
+			} else if (request.getParameter("pdpId") != null) {
+				// ARGS:        group=<groupId> pdpId=<pdpId>                  <= delete PDP 
+				String pdpId = request.getParameter("pdpId");
+				PDP pdp = papEngine.getPDP(pdpId);
+				
+				papEngine.removePDP(pdp);
+				
+				// adjust the status of the group, which may have changed when we removed this PDP
+				((StdPDPGroup)group).resetStatus();
+				
+				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+				notifyAC();
+
+				// update the PDP and tell it that it has NO Policies (which prevents it from serving PEP Requests)
+				pdpChanged(pdp);
+				return;
+			} else if (request.getParameter("pipId") != null) {
+	//        group=<groupId> pipId=<pipEngineId> <= delete PIP config for given engine
+	//TODO
+				logger.error("UNIMPLEMENTED ");
+				response.sendError(HttpServletResponse.SC_BAD_REQUEST, "UNIMPLEMENTED");
+				return;
+			} else {
+				// ARGS:      group=<groupId> movePDPsToGroupId=<movePDPsToGroupId>            <= delete a group and move all its PDPs to the given group
+				String moveToGroupId = request.getParameter("movePDPsToGroupId");
+				PDPGroup moveToGroup = null;
+				if (moveToGroupId != null) {
+					moveToGroup = papEngine.getGroup(moveToGroupId);
+				}
+				
+				// get list of PDPs in the group being deleted so we can notify them that they got changed
+				Set<PDP> movedPDPs = new HashSet<PDP>();
+				movedPDPs.addAll(group.getPdps());
+				
+				// do the move/remove
+				papEngine.removeGroup(group, moveToGroup);
+				
+				response.setStatus(HttpServletResponse.SC_NO_CONTENT);
+				notifyAC();
+				// notify any PDPs in the removed set that their config may have changed
+				for (PDP pdp : movedPDPs) {
+					pdpChanged(pdp);
+				}
+				return;
+			}
+
+		} catch (PAPException e) {
+			logger.error("AC DELETE exception: " + e, e);
+			response.sendError(500, e.getMessage());
+			return;
+		}
+	}
+
+	//
+	// Heartbeat thread - periodically check on PDPs' status
+	//
+	
+	/**
+	 * Heartbeat with all known PDPs.
+	 * 
+	 * Implementation note:
+	 * 
+	 * The PDPs are contacted Sequentially, not in Parallel.
+	 * 
+	 * If we did this in parallel using multiple threads we would simultaneously use
+	 * 		- 1 thread and
+	 * 		- 1 connection
+	 * for EACH PDP.
+	 * This could become a resource problem since we already use multiple threads and connections for updating the PDPs
+	 * when user changes occur.
+	 * Using separate threads can also make it tricky dealing with timeouts on PDPs that are non-responsive.
+	 * 
+	 * The Sequential operation does a heartbeat request to each PDP one at a time.
+	 * This has the flaw that any PDPs that do not respond will hold up the entire heartbeat sequence until they timeout.
+	 * If there are a lot of non-responsive PDPs and the timeout is large-ish (the default is 20 seconds)
+	 * it could take a long time to cycle through all of the PDPs.
+	 * That means that this may not notice a PDP being down in a predictable time.
+	 * 
+	 * @author glenngriffin
+	 *
+	 */
+	private class Heartbeat implements Runnable {
+		private PAPEngine papEngine;
+		private Set<PDP> pdps = new HashSet<PDP>();
+		private int heartbeatInterval;
+		private int heartbeatTimeout;
+		
+		public volatile boolean isRunning = false;
+		
+		public synchronized boolean isRunning() {
+			return this.isRunning;
+		}
+		
+		public synchronized void terminate() {
+			this.isRunning = false;
+		}
+		
+		public Heartbeat(PAPEngine engine) {
+			this.papEngine = engine;
+			this.heartbeatInterval = Integer.parseInt(XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_HEARTBEAT_INTERVAL, "10000"));
+			this.heartbeatTimeout = Integer.parseInt(XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_HEARTBEAT_TIMEOUT, "10000"));
+		}
+
+		@Override
+		public void run() {
+			//
+			// Set ourselves as running
+			//
+			synchronized(this) {
+				this.isRunning = true;
+			}
+			HashMap<String, URL> idToURLMap = new HashMap<String, URL>();
+			try {
+				while (this.isRunning()) {
+					// Wait the given time
+					Thread.sleep(heartbeatInterval);
+					
+					// get the list of PDPs (may have changed since last time)
+					pdps.clear();
+					synchronized(papEngine) {
+						try {
+							for (PDPGroup g : papEngine.getPDPGroups()) {
+								for (PDP p : g.getPdps()) {
+									pdps.add(p);
+								}
+							}
+						} catch (PAPException e) {
+							logger.error("Heartbeat unable to read PDPs from PAPEngine: " + e.getMessage(), e);
+						}
+					}
+					//
+					// Check for shutdown
+					//
+					if (this.isRunning() == false) {
+						logger.info("isRunning is false, getting out of loop.");
+						break;
+					}
+					
+					// try to get the summary status from each PDP
+					boolean changeSeen = false;
+					for (PDP pdp : pdps) {
+						//
+						// Check for shutdown
+						//
+						if (this.isRunning() == false) {
+							logger.info("isRunning is false, getting out of loop.");
+							break;
+						}
+						// the id of the PDP is its url (though we add a query parameter)
+						URL pdpURL = idToURLMap.get(pdp.getId());
+						if (pdpURL == null) {
+							// haven't seen this PDP before
+							String fullURLString = null;
+							try {
+								fullURLString = pdp.getId() + "?type=hb";
+								pdpURL = new URL(fullURLString);
+								idToURLMap.put(pdp.getId(), pdpURL);
+							} catch (MalformedURLException e) {
+								logger.error("PDP id '" + fullURLString + "' is not a valid URL: " + e, e);
+								continue;
+							}
+						}
+						
+						// Do a GET with type HeartBeat
+						String newStatus = "";
+						
+						HttpURLConnection connection = null;
+						try {
+
+							//
+							// Open up the connection
+							//
+							connection = (HttpURLConnection)pdpURL.openConnection();
+							//
+							// Setup our method and headers
+							//
+				            connection.setRequestMethod("GET");
+				            connection.setConnectTimeout(heartbeatTimeout);
+				            //
+				            // Do the connect
+				            //
+				            connection.connect();
+				            if (connection.getResponseCode() == 204) {
+				            	newStatus = connection.getHeaderField(XACMLRestProperties.PROP_PDP_HTTP_HEADER_HB);
+								if (logger.isDebugEnabled()) {
+									logger.debug("Heartbeat '" + pdp.getId() + "' status='" + newStatus + "'");
+								}
+				            } else {
+				            	// anything else is an unexpected result
+				            	newStatus = PDPStatus.Status.UNKNOWN.toString();
+				            	logger.error("Heartbeat connect response code " + connection.getResponseCode() + ": " + pdp.getId());
+				            }
+			            } catch (UnknownHostException e) {
+			            	newStatus = PDPStatus.Status.NO_SUCH_HOST.toString();
+			            	logger.error("Heartbeat '" + pdp.getId() + "' NO_SUCH_HOST");
+						} catch (SocketTimeoutException e) {
+			            	newStatus = PDPStatus.Status.CANNOT_CONNECT.toString();
+			            	logger.error("Heartbeat '" + pdp.getId() + "' connection timeout: " + e );
+						} catch (ConnectException e) {
+			            	newStatus = PDPStatus.Status.CANNOT_CONNECT.toString();
+			            	logger.error("Heartbeat '" + pdp.getId() + "' cannot connect: " + e );
+						} catch (Exception e) {
+			            	newStatus = PDPStatus.Status.UNKNOWN.toString();
+			            	logger.error("Heartbeat '" + pdp.getId() + "' connect exception: " + e, e);
+						} finally {
+							// cleanup the connection
+							connection.disconnect();
+						}
+
+						if ( ! pdp.getStatus().getStatus().toString().equals(newStatus)) {
+							if (logger.isDebugEnabled()) {
+								logger.debug("previous status='" + pdp.getStatus().getStatus()+"'  new Status='" + newStatus + "'");
+							}
+			            	try {
+								setPDPSummaryStatus(pdp, newStatus);
+							} catch (PAPException e) {
+								logger.error("Unable to set state for PDP '" + pdp.getId() + "': " + e, e);
+							}
+							changeSeen = true;
+						}
+						
+					}
+					//
+					// Check for shutdown
+					//
+					if (this.isRunning() == false) {
+						logger.info("isRunning is false, getting out of loop.");
+						break;
+					}
+
+					// if any of the PDPs changed state, tell the ACs to update
+					if (changeSeen) {
+						notifyAC();
+					}
+					
+				}
+			} catch (InterruptedException e) {
+				logger.error("Heartbeat interrupted.  Shutting down");
+				this.terminate();
+			}
+		}
+	}
+	
+	
+	//
+	// HELPER to change Group status when PDP status is changed
+	//
+	// (Must NOT be called from a method that is synchronized on the papEngine or it may deadlock)
+	//
+	
+	private void setPDPSummaryStatus(PDP pdp, PDPStatus.Status newStatus) throws PAPException {
+		setPDPSummaryStatus(pdp, newStatus.toString());
+	}
+
+	private void setPDPSummaryStatus(PDP pdp, String newStatus) throws PAPException {
+    	synchronized(papEngine) {
+    		StdPDPStatus status = (StdPDPStatus)pdp.getStatus();
+    		status.setStatus(PDPStatus.Status.valueOf(newStatus));
+    		((StdPDP)pdp).setStatus(status);
+    		
+    		// now adjust the group
+    		StdPDPGroup group = (StdPDPGroup)papEngine.getPDPGroup(pdp);
+    		// if the PDP was just deleted it may transiently exist but not be in a group
+    		if (group != null) {
+    			group.resetStatus();
+    		}
+    	}
+	}
+	
+	
+	//
+	// Callback methods telling this servlet to notify PDPs of changes made by the PAP StdEngine
+	//	in the PDP group directories
+	//
+	
+	@Override
+	public void changed() {
+		// all PDPs in all groups need to be updated/sync'd
+		Set<PDPGroup> groups;
+		try {
+			groups = papEngine.getPDPGroups();
+		} catch (PAPException e) {
+			logger.error("getPDPGroups failed: " + e.getLocalizedMessage());
+			throw new RuntimeException("Unable to get Groups: " + e);
+		}
+		for (PDPGroup group : groups) {
+			groupChanged(group);
+		}
+	}
+
+	@Override
+	public void groupChanged(PDPGroup group) {
+		// all PDPs within one group need to be updated/sync'd
+		for (PDP pdp : group.getPdps()) {
+			pdpChanged(pdp);
+		}
+	}
+
+	@Override
+	public void pdpChanged(PDP pdp) {
+		// kick off a thread to do an event notification for each PDP.
+		// This needs to be on a separate thread so that PDPs that do not respond (down, non-existent, etc)
+		// do not block the PSP response to the AC, which would freeze the GUI until all PDPs sequentially respond or time-out.
+		Thread t = new Thread(new UpdatePDPThread(pdp));
+		t.start();
+	}
+	
+	private class UpdatePDPThread implements Runnable {
+		private PDP pdp;
+
+		// remember which PDP to notify
+		public UpdatePDPThread(PDP pdp) {
+			this.pdp = pdp;
+		}
+
+		public void run() {
+			// send the current configuration to one PDP
+			HttpURLConnection connection = null;
+			try {
+				
+				//
+				// the Id of the PDP is its URL
+				//
+				if (logger.isDebugEnabled()) {
+					logger.debug("creating url for id '" + pdp.getId() + "'");
+				}
+	//TODO - currently always send both policies and pips.  Do we care enough to add code to allow sending just one or the other?
+	//TODO		(need to change "cache=", implying getting some input saying which to change)
+				URL url = new URL(pdp.getId() + "?cache=all");
+				
+				//
+				// Open up the connection
+				//
+				connection = (HttpURLConnection)url.openConnection();
+				//
+				// Setup our method and headers
+				//
+	            connection.setRequestMethod("PUT");
+	//			connection.setRequestProperty("Accept", "text/x-java-properties");
+	            connection.setRequestProperty("Content-Type", "text/x-java-properties");
+	//            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.
+	            //
+	//TODO - is this needed for a PUT?  seems better to leave in for now?
+//	            connection.setInstanceFollowRedirects(false);
+	            //
+	            // PLD - MUST be able to handle re-directs.
+	            //
+	            connection.setInstanceFollowRedirects(true);
+				connection.setDoOutput(true);
+	//			connection.setDoInput(true);
+	    		try (OutputStream os = connection.getOutputStream()) {
+	
+	    			PDPGroup group = papEngine.getPDPGroup(pdp);
+	    			// if the PDP was just deleted, there is no group, but we want to send an update anyway
+	    			if (group == null) {
+	    				// create blank properties files
+	    				Properties policyProperties = new Properties();
+	    				policyProperties.put(XACMLProperties.PROP_ROOTPOLICIES, "");
+	    				policyProperties.put(XACMLProperties.PROP_REFERENCEDPOLICIES, "");
+	    				policyProperties.store(os, "");
+	    				
+	    				Properties pipProps = new Properties();
+						pipProps.setProperty(XACMLProperties.PROP_PIP_ENGINES, "");
+						pipProps.store(os, "");
+	    				
+	    			} else {
+	    				// send properties from the current group
+		    			group.getPolicyProperties().store(os, "");
+		    			Properties policyLocations = new Properties();
+		    			for (PDPPolicy policy : group.getPolicies()) {
+						   policyLocations.put(policy.getId() + ".url", XACMLPapServlet.papURL + "?id=" + policy.getId());
+		    			}
+		    			policyLocations.store(os, "");
+		    			group.getPipConfigProperties().store(os, "");
+	    			}
+		
+	    		} catch (Exception e) {
+	    			logger.error("Failed to send property file to " + pdp.getId(), e);
+	    			// Since this is a server-side error, it probably does not reflect a problem on the client,
+	    			// so do not change the PDP status.
+	    			return;
+	    		}
+	            //
+	            // Do the connect
+	            //
+	            connection.connect();
+	            if (connection.getResponseCode() == 204) {
+	            	logger.info("Success. We are configured correctly.");
+					setPDPSummaryStatus(pdp, PDPStatus.Status.UP_TO_DATE);
+	            } else if (connection.getResponseCode() == 200) {
+	            	logger.info("Success. PDP needs to update its configuration.");
+					setPDPSummaryStatus(pdp, PDPStatus.Status.OUT_OF_SYNCH);
+	            } else {
+	            	logger.warn("Failed: " + connection.getResponseCode() + "  message: " + connection.getResponseMessage());
+					setPDPSummaryStatus(pdp, PDPStatus.Status.UNKNOWN);
+	            }
+			} catch (Exception e) {
+				logger.error("Unable to sync config with PDP '" + pdp.getId() + "': " + e, e);
+				try {
+					setPDPSummaryStatus(pdp, PDPStatus.Status.UNKNOWN);
+				} catch (PAPException e1) {
+					logger.error("Unable to set status of PDP '" + pdp.getId() + "' to UNKNOWN: " + e, e);
+				}
+			} finally {
+				// cleanup the connection
+				connection.disconnect();
+				
+	            // tell the AC to update it's status info
+	            notifyAC();
+			}
+			
+		}
+	}
+
+	//
+	// RESTful Interface from PAP to ACs notifying them of changes
+	//
+	
+	private void notifyAC() {
+		// kick off a thread to do one event notification for all registered ACs
+		// This needs to be on a separate thread so that ACs can make calls back to PAP to get the updated Group data
+		// as part of processing this message on their end.
+		Thread t = new Thread(new NotifyACThread());
+		t.start();
+	}
+	
+	private class NotifyACThread implements Runnable {
+	
+		public void run() {
+			List<String> disconnectedACs = new ArrayList<String>();
+// logger.debug("LIST SIZE="+adminConsoleURLStringList.size());
+			
+			// There should be no Concurrent exception here because the list is a CopyOnWriteArrayList.
+			// The "for each" loop uses the collection's iterator under the covers, so it should be correct.
+			for (String acURL : adminConsoleURLStringList) {
+				HttpURLConnection connection = null;
+				try {
+					
+					acURL += "?PAPNotification=true";
+					
+//TODO - Currently we just tell AC that "Something changed" without being specific.  Do we want to tell it which group/pdp changed?
+//TODO - If so, put correct parameters into the Query string here
+					acURL += "&objectType=all" + "&action=update";
+	
+					if (logger.isDebugEnabled()) {
+						logger.debug("creating url for id '" + acURL + "'");
+					}
+//TODO - currently always send both policies and pips.  Do we care enough to add code to allow sending just one or the other?
+//TODO		(need to change "cache=", implying getting some input saying which to change)
+					
+					URL url = new URL(acURL );
+					
+					//
+					// Open up the connection
+					//
+					connection = (HttpURLConnection)url.openConnection();
+					//
+					// Setup our method and headers
+					//
+		            connection.setRequestMethod("PUT");
+		            connection.setRequestProperty("Content-Type", "text/x-java-properties");
+		            //
+		            // 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.
+		            //
+		//TODO - is this needed for a PUT?  seems better to leave in for now?
+		            connection.setInstanceFollowRedirects(false);
+					//
+					// Do not include any data in the PUT because this is just a
+					// notification to the AC.
+					// The AC will use GETs back to the PAP to get what it needs
+					// to fill in the screens.
+					//
+					
+		            //
+		            // Do the connect
+		            //
+		            connection.connect();
+		            if (connection.getResponseCode() == 204) {
+		            	logger.info("Success. We updated correctly.");
+		            } else {
+		            	logger.warn("Failed: " + connection.getResponseCode() + "  message: " + connection.getResponseMessage());
+		            }
+					
+				} catch (Exception e) {
+					logger.error("Unable to sync config AC '" + acURL + "': " + e, e);
+					disconnectedACs.add(acURL);
+				} finally {
+					// cleanup the connection
+					connection.disconnect();
+				}
+			}
+			
+			// remove any ACs that are no longer connected
+			if (disconnectedACs.size() > 0) {
+				adminConsoleURLStringList.removeAll(disconnectedACs);
+			}
+
+		}
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/src/main/resources/log4j.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/src/main/resources/log4j.properties b/openaz-xacml-pap-rest/src/main/resources/log4j.properties
new file mode 100755
index 0000000..b45fa2f
--- /dev/null
+++ b/openaz-xacml-pap-rest/src/main/resources/log4j.properties
@@ -0,0 +1,22 @@
+#
+# Use this properties for debugging and development.
+#
+#
+# Set root logger level to DEBUG and its only appender to A1.
+log4j.rootLogger=DEBUG, MAIN_LOG
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.MAIN_LOG=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.MAIN_LOG.layout=org.apache.log4j.PatternLayout
+log4j.appender.MAIN_LOG.layout.ConversionPattern=%d{yyyy_MM_dd_HH_mm_ss_SSS} [%t] %-5p %l- %m%n
+
+#
+# This is specifically for Xacml request/response logging
+#
+log4j.logger.xacml.request=INFO, REQUEST_LOG
+
+log4j.appender.REQUEST_LOG=org.apache.log4j.ConsoleAppender
+log4j.appender.REQUEST_LOG.layout=org.apache.log4j.PatternLayout
+log4j.appender.REQUEST_LOG.layout.ConversionPattern=%d{yyyy_MM_dd_HH_mm_ss_SSS} %m%n
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$1.class
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$1.class b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$1.class
new file mode 100644
index 0000000..352e4c6
Binary files /dev/null and b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$1.class differ

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$Heartbeat.class
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$Heartbeat.class b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$Heartbeat.class
new file mode 100644
index 0000000..aac4b91
Binary files /dev/null and b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$Heartbeat.class differ

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$NotifyACThread.class
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$NotifyACThread.class b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$NotifyACThread.class
new file mode 100644
index 0000000..3d756fd
Binary files /dev/null and b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$NotifyACThread.class differ

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$UpdatePDPThread.class
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$UpdatePDPThread.class b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$UpdatePDPThread.class
new file mode 100644
index 0000000..ffd1592
Binary files /dev/null and b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet$UpdatePDPThread.class differ

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet.class
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet.class b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet.class
new file mode 100644
index 0000000..d8d63b5
Binary files /dev/null and b/openaz-xacml-pap-rest/target/classes/com/att/research/xacml/rest/XACMLPapServlet.class differ

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/classes/log4j.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/classes/log4j.properties b/openaz-xacml-pap-rest/target/classes/log4j.properties
new file mode 100644
index 0000000..b45fa2f
--- /dev/null
+++ b/openaz-xacml-pap-rest/target/classes/log4j.properties
@@ -0,0 +1,22 @@
+#
+# Use this properties for debugging and development.
+#
+#
+# Set root logger level to DEBUG and its only appender to A1.
+log4j.rootLogger=DEBUG, MAIN_LOG
+
+# A1 is set to be a ConsoleAppender.
+log4j.appender.MAIN_LOG=org.apache.log4j.ConsoleAppender
+
+# A1 uses PatternLayout.
+log4j.appender.MAIN_LOG.layout=org.apache.log4j.PatternLayout
+log4j.appender.MAIN_LOG.layout.ConversionPattern=%d{yyyy_MM_dd_HH_mm_ss_SSS} [%t] %-5p %l- %m%n
+
+#
+# This is specifically for Xacml request/response logging
+#
+log4j.logger.xacml.request=INFO, REQUEST_LOG
+
+log4j.appender.REQUEST_LOG=org.apache.log4j.ConsoleAppender
+log4j.appender.REQUEST_LOG.layout=org.apache.log4j.PatternLayout
+log4j.appender.REQUEST_LOG.layout.ConversionPattern=%d{yyyy_MM_dd_HH_mm_ss_SSS} %m%n
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/maven-archiver/pom.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/maven-archiver/pom.properties b/openaz-xacml-pap-rest/target/maven-archiver/pom.properties
new file mode 100644
index 0000000..022f696
--- /dev/null
+++ b/openaz-xacml-pap-rest/target/maven-archiver/pom.properties
@@ -0,0 +1,5 @@
+#Generated by Maven
+#Tue Apr 07 07:42:37 EDT 2015
+version=0.0.1-SNAPSHOT
+groupId=org.openliberty.openaz
+artifactId=openaz-xacml-pap-rest

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst b/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
new file mode 100644
index 0000000..4cfb3ef
--- /dev/null
+++ b/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/createdFiles.lst
@@ -0,0 +1,5 @@
+com/att/research/xacml/rest/XACMLPapServlet$Heartbeat.class
+com/att/research/xacml/rest/XACMLPapServlet$1.class
+com/att/research/xacml/rest/XACMLPapServlet.class
+com/att/research/xacml/rest/XACMLPapServlet$UpdatePDPThread.class
+com/att/research/xacml/rest/XACMLPapServlet$NotifyACThread.class

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst b/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
new file mode 100644
index 0000000..9a51391
--- /dev/null
+++ b/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/compile/default-compile/inputFiles.lst
@@ -0,0 +1 @@
+/Users/ajith/IdeaProjects/openaz/openaz-xacml-pap-rest/src/main/java/com/att/research/xacml/rest/XACMLPapServlet.java

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst b/openaz-xacml-pap-rest/target/maven-status/maven-compiler-plugin/testCompile/default-testCompile/inputFiles.lst
new file mode 100644
index 0000000..e69de29

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/target/openaz-xacml-pap-rest-0.0.1-SNAPSHOT.jar
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/target/openaz-xacml-pap-rest-0.0.1-SNAPSHOT.jar b/openaz-xacml-pap-rest/target/openaz-xacml-pap-rest-0.0.1-SNAPSHOT.jar
new file mode 100644
index 0000000..d8cdce7
Binary files /dev/null and b/openaz-xacml-pap-rest/target/openaz-xacml-pap-rest-0.0.1-SNAPSHOT.jar differ

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pap-rest/xacml.pap.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-rest/xacml.pap.properties b/openaz-xacml-pap-rest/xacml.pap.properties
new file mode 100755
index 0000000..05fc0c5
--- /dev/null
+++ b/openaz-xacml-pap-rest/xacml.pap.properties
@@ -0,0 +1,35 @@
+#
+# This is our factory that will create our engine
+#
+xacml.PAP.papEngineFactory=com.att.research.xacml.std.pap.StdEngineFactory
+
+#
+# Where we store our PAP PDP Group/Node information
+#
+xacml.pap.pdps=pdps
+#
+# Need the PAP's url (how PDPs will reach it) configured here
+# because we need it to generate the URLs of the Policy Files
+# sent to the PDPs in the configuration when the PAP is first brought up.
+# (In other cases, such as the PDP calling the PAP, we could generate this URL, 
+# but for startup there is no other way to get it.)
+#
+#
+xacml.rest.pap.url=http://localhost:9090/pap/
+
+#
+# Upon startup, have the PAP servlet send latest configuration information to all
+# the PDP nodes it knows about.
+#
+xacml.rest.pap.initiate.pdp=true
+#
+# Heartbeat from PAP to PDPs
+#
+# How much time (in milliseconds) between heartbeats
+# (i.e. the time between completing the heartbeat with all PDPs and starting the next cycle)
+#
+xacml.rest.pap.heartbeat.interval=10000
+#
+# Heartbeat connection timeout (in milliseconds)
+#
+xacml.rest.pap.heartbeat.timeout=10000
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pdp-rest/WebContent/META-INF/MANIFEST.MF
----------------------------------------------------------------------
diff --git a/openaz-xacml-pdp-rest/WebContent/META-INF/MANIFEST.MF b/openaz-xacml-pdp-rest/WebContent/META-INF/MANIFEST.MF
new file mode 100755
index 0000000..58630c0
--- /dev/null
+++ b/openaz-xacml-pdp-rest/WebContent/META-INF/MANIFEST.MF
@@ -0,0 +1,2 @@
+Manifest-Version: 1.0
+

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pdp-rest/WebContent/WEB-INF/lib/sqljdbc4.jar
----------------------------------------------------------------------
diff --git a/openaz-xacml-pdp-rest/WebContent/WEB-INF/lib/sqljdbc4.jar b/openaz-xacml-pdp-rest/WebContent/WEB-INF/lib/sqljdbc4.jar
new file mode 100755
index 0000000..d6b7f6d
Binary files /dev/null and b/openaz-xacml-pdp-rest/WebContent/WEB-INF/lib/sqljdbc4.jar differ

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pdp-rest/config/xacml.pip.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pdp-rest/config/xacml.pip.properties b/openaz-xacml-pdp-rest/config/xacml.pip.properties
new file mode 100755
index 0000000..0c16eb3
--- /dev/null
+++ b/openaz-xacml-pdp-rest/config/xacml.pip.properties
@@ -0,0 +1,219 @@
+# PIP Engine Definition
+#
+xacml.pip.engines=csv1,csv2,hyper1,sql1,ldap1
+
+csv1.classname=com.att.research.xacml.std.pip.engines.csv.CSVEngine
+csv1.name=Master
+csv1.description=Sean Lahman Basebase stats - Player names, DOB, and biographical info
+csv1.issuer=com:att:research:xacml:test:csv
+csv1.source=../XACML-TEST/testsets/pip/configurable-csv/adminDB/Master.txt
+csv1.maxsize=4000000
+csv1.delimiter=,
+csv1.quote="
+csv1.skip=0
+
+csv1.resolvers=data
+
+csv1.resolver.data.classname=com.att.research.xacml.std.pip.engines.csv.ConfigurableCSVResolver
+csv1.resolver.data.name=Player Resolver
+csv1.resolver.data.description=This resolver finds player information in the Master table.
+csv1.resolver.data.fields=firstname,lastname,deathyear,deathmonth,deathday,debut,finalgame
+csv1.resolver.data.field.firstname.column=16
+csv1.resolver.data.field.firstname.id=com:att:research:xacml:test:csv:subject:firstname
+csv1.resolver.data.field.firstname.datatype=http://www.w3.org/2001/XMLSchema#string
+csv1.resolver.data.field.firstname.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.lastname.column=17
+csv1.resolver.data.field.lastname.id=com:att:research:xacml:test:csv:subject:lastname
+csv1.resolver.data.field.lastname.datatype=http://www.w3.org/2001/XMLSchema#string
+csv1.resolver.data.field.lastname.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.deathyear.column=10
+csv1.resolver.data.field.deathyear.id=com:att:research:xacml:test:csv:subject:deathyear
+csv1.resolver.data.field.deathyear.datatype=http://www.w3.org/2001/XMLSchema#integer
+csv1.resolver.data.field.deathyear.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.deathmonth.column=11
+csv1.resolver.data.field.deathmonth.id=com:att:research:xacml:test:csv:subject:deathmonth
+csv1.resolver.data.field.deathmonth.datatype=http://www.w3.org/2001/XMLSchema#integer
+csv1.resolver.data.field.deathmonth.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.deathday.column=12
+csv1.resolver.data.field.deathday.id=com:att:research:xacml:test:csv:subject:deathday
+csv1.resolver.data.field.deathday.datatype=http://www.w3.org/2001/XMLSchema#integer
+csv1.resolver.data.field.deathday.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.debut.column=25
+csv1.resolver.data.field.debut.id=com:att:research:xacml:test:csv:subject:debut
+csv1.resolver.data.field.debut.datatype=http://www.w3.org/2001/XMLSchema#date
+csv1.resolver.data.field.debut.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.field.finalgame.column=26
+csv1.resolver.data.field.finalgame.id=com:att:research:xacml:test:csv:subject:finalgame
+csv1.resolver.data.field.finalgame.datatype=http://www.w3.org/2001/XMLSchema#date
+csv1.resolver.data.field.finalgame.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv1.resolver.data.parameters=playerid
+csv1.resolver.data.parameter.playerid.column=1
+csv1.resolver.data.parameter.playerid.id=urn:oasis:names:tc:xacml:1.0:subject:subject-id
+csv1.resolver.data.parameter.playerid.datatype=http://www.w3.org/2001/XMLSchema#string
+csv1.resolver.data.parameter.playerid.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+
+csv2.classname=com.att.research.xacml.std.pip.engines.csv.CSVEngine
+csv2.name=Appearances
+csv2.description=Sean Lahman Basebase stats - Player appearances for a team in a given year.
+#csv2.issuer=
+csv2.source=../XACML-TEST/testsets/pip/configurable-csv/adminDB/Appearances.txt
+csv2.maxsize=4000000
+csv2.delimiter=,
+csv2.quote="
+csv2.skip=0
+
+csv2.resolvers=data
+
+csv2.resolver.data.classname=com.att.research.xacml.std.pip.engines.csv.ConfigurableCSVResolver
+csv2.resolver.data.name=Appearance Resolver
+csv2.resolver.data.description=This resolver returns all the appearances for a player from the appearance table.
+csv2.resolver.data.fields=appearance
+csv2.resolver.data.field.appearance.column=0
+csv2.resolver.data.field.appearance.id=com:att:research:xacml:test:csv:subject:appearance
+csv2.resolver.data.field.appearance.datatype=http://www.w3.org/2001/XMLSchema#integer
+csv2.resolver.data.field.appearance.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+csv2.resolver.data.field.appearance.issuer=com:att:research:xacml:test:csv
+
+csv2.resolver.data.parameters=playerid
+csv2.resolver.data.parameter.playerid.column=3
+csv2.resolver.data.parameter.playerid.id=urn:oasis:names:tc:xacml:1.0:subject:subject-id
+csv2.resolver.data.parameter.playerid.datatype=http://www.w3.org/2001/XMLSchema#string
+csv2.resolver.data.parameter.playerid.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+#csv1.resolver.data.parameter.playerid.issuer=
+
+hyper1.classname=com.att.research.xacml.std.pip.engines.csv.HyperCSVEngine
+hyper1.name=World Marriage Age Limits
+hyper1.description=Minimum age for female/male marriages with or without their parental consent.
+hyper1.source=../XACML-TEST/testsets/pip/configurable-csv-hyper/marriage.csv
+hyper1.target=marriage
+hyper1.definition=country VARCHAR(80) PRIMARY KEY, wofemale INT, womale INT, wfemale INT, wmale INT, year INT, source VARCHAR(20)
+
+hyper1.resolvers=age_consent
+
+hyper1.resolver.age_consent.classname=com.att.research.xacml.std.pip.engines.jdbc.ConfigurableJDBCResolver
+hyper1.resolver.age_consent.name=Ages
+hyper1.resolver.age_consent.description=This returns all the age's for consent or no consent for a country.
+hyper1.resolver.age_consent.select=SELECT wofemale,womale,wfemale,wmale FROM marriage WHERE country=?
+hyper1.resolver.age_consent.fields=wofemale,womale,wfemale,wmale
+
+hyper1.resolver.age_consent.field.wofemale.id=com:att:research:xacml:test:csv:country:no-consent:female
+hyper1.resolver.age_consent.field.wofemale.datatype=http://www.w3.org/2001/XMLSchema#integer
+hyper1.resolver.age_consent.field.wofemale.category=com:att:research:xacml:test:csv:category:country
+hyper1.resolver.age_consent.field.wofemale.issuer=com:att:research:xacml:test:csv
+
+hyper1.resolver.age_consent.field.womale.id=com:att:research:xacml:test:csv:country:no-consent:male
+hyper1.resolver.age_consent.field.womale.datatype=http://www.w3.org/2001/XMLSchema#integer
+hyper1.resolver.age_consent.field.womale.category=com:att:research:xacml:test:csv:category:country
+hyper1.resolver.age_consent.field.womale.issuer=com:att:research:xacml:test:csv
+
+hyper1.resolver.age_consent.field.wfemale.id=com:att:research:xacml:test:csv:country:consent:female
+hyper1.resolver.age_consent.field.wfemale.datatype=http://www.w3.org/2001/XMLSchema#integer
+hyper1.resolver.age_consent.field.wfemale.category=com:att:research:xacml:test:csv:category:country
+hyper1.resolver.age_consent.field.wfemale.issuer=com:att:research:xacml:test:csv
+
+hyper1.resolver.age_consent.field.wmale.id=com:att:research:xacml:test:csv:country:consent:male
+hyper1.resolver.age_consent.field.wmale.datatype=http://www.w3.org/2001/XMLSchema#integer
+hyper1.resolver.age_consent.field.wmale.category=com:att:research:xacml:test:csv:category:country
+hyper1.resolver.age_consent.field.wmale.issuer=com:att:research:xacml:test:csv
+
+hyper1.resolver.age_consent.parameters=country
+hyper1.resolver.age_consent.parameter.country.id=com:att:research:xacml:test:csv:country:name
+hyper1.resolver.age_consent.parameter.country.datatype=http://www.w3.org/2001/XMLSchema#string
+hyper1.resolver.age_consent.parameter.country.category=com:att:research:xacml:test:csv:category:country
+#hyper1.resolver.age_consent.parameter.country.issuer=
+
+sql1.classname=com.att.research.xacml.std.pip.engines.jdbc.JDBCEngine
+sql1.name=World
+sql1.description=World Database from MySQL website. Copyright Statistics Finland, http://www.stat.fi/worldinfigures.
+# This will be the default issuer for the resolvers. NOTE: Issuer only used for attributes provided by the engine.
+sql1.issuer=com:att:research:xacml:test:sql
+#
+# This is the configuration for JDBC. You will have to setup the database and run the data\world*.sql script to
+# create the tables and load the data. 
+#
+sql1.type=jdbc
+sql1.jdbc.driver=org.postgresql.Driver
+sql1.jdbc.url=jdbc:postgresql://localhost:5432/world
+sql1.jdbc.conn.user=sa
+sql1.jdbc.conn.password=
+#
+# This is the configuration for JNDI datasource.
+#
+#sql1.type=jndi
+#sql1.datasource=jdbc/xacml
+
+sql1.resolvers=langer
+
+sql1.resolver.langer.classname=com.att.research.xacml.std.pip.engines.jdbc.ConfigurableJDBCResolver
+sql1.resolver.langer.name=Language
+sql1.resolver.langer.description=This returns the language for a city.
+sql1.resolver.langer.select=SELECT language FROM city INNER JOIN countrylanguage ON city.countrycode = countrylanguage.countrycode WHERE name=?
+sql1.resolver.langer.fields=language
+sql1.resolver.langer.field.language.id=com:att:research:xacml:test:sql:resource:city:language
+sql1.resolver.langer.field.language.datatype=http://www.w3.org/2001/XMLSchema#string
+sql1.resolver.langer.field.language.category=urn:oasis:names:tc:xacml:3.0:attribute-category:resource
+#You can override the default issuer that is set in the JDBCEngine definition if you want.
+#sql1.resolver.langer.field.language.issuer=com:att:research:xacml:test:sql
+sql1.resolver.langer.parameters=name
+sql1.resolver.langer.parameter.name.id=urn:oasis:names:tc:xacml:1.0:resource:resource-id
+sql1.resolver.langer.parameter.name.datatype=http://www.w3.org/2001/XMLSchema#string
+sql1.resolver.langer.parameter.name.category=urn:oasis:names:tc:xacml:3.0:attribute-category:resource
+
+
+ldap1.classname=com.att.research.xacml.std.pip.engines.ldap.LDAPEngine
+ldap1.name=LDAP PIP
+ldap1.description=The LDAP containing the seven seas sample LDIF data.
+ldap1.issuer=com:att:research:xacml:test:ldap
+ldap1.java.naming.factory.initial=com.sun.jndi.ldap.LdapCtxFactory
+#
+# NOTE: You will have to setup a local LDAP server and load the data\apache-ds-tutorial.ldif before
+# this example will work.
+#
+ldap1.java.naming.provider.url=ldap://localhost:10389
+#ldap.java.naming.security.principal=
+#ldap.java.naming.security.credentials=
+ldap1.scope=subtree
+
+ldap1.resolvers=dn,ship
+
+ldap1.resolver.dn.classname=com.att.research.xacml.std.pip.engines.ldap.ConfigurableLDAPResolver
+ldap1.resolver.dn.name=Domain Names
+ldap1.resolver.dn.description=Find all the dn's for the subject id
+ldap1.resolver.dn.base=o=sevenseas
+ldap1.resolver.dn.base.parameters=
+ldap1.resolver.dn.filter=(|(uid=${uid})(mail=${uid}))
+ldap1.resolver.dn.filter.parameters=uid
+ldap1.resolver.dn.filter.parameters.uid.id=urn:oasis:names:tc:xacml:1.0:subject:subject-id
+ldap1.resolver.dn.filter.parameters.uid.datatype=http://www.w3.org/2001/XMLSchema#string
+ldap1.resolver.dn.filter.parameters.uid.category=urn:oasis:names:tc:xacml:1.0:subject-category:access-subject
+#ldap1.resolver.dn.filter.parameters.uid.issuer=com:att:research:xacml:test:ldap
+ldap1.resolver.dn.filter.view=dn
+ldap1.resolver.dn.filter.view.dn.id=com:att:research:xacml:test:ldap:subject:dn
+ldap1.resolver.dn.filter.view.dn.datatype=http://www.w3.org/2001/XMLSchema#string
+ldap1.resolver.dn.filter.view.dn.category=urn:oasis:names:tc:xacml:3.0:attribute-category:resource
+ldap1.resolver.dn.filter.view.dn.issuer=com:att:research:xacml:test:ldap
+
+ldap1.resolver.ship.classname=com.att.research.xacml.std.pip.engines.ldap.ConfigurableLDAPResolver
+ldap1.resolver.ship.name=Ship Resolver
+ldap1.resolver.ship.description=This resolves a subject's dn to a ship.
+ldap1.resolver.ship.base=o=sevenseas
+ldap1.resolver.ship.base.parameters=
+ldap1.resolver.ship.filter=uniquemember=${dn}
+ldap1.resolver.ship.filter.parameters=dn
+ldap1.resolver.ship.filter.parameters.dn.id=com:att:research:xacml:test:ldap:subject:dn
+ldap1.resolver.ship.filter.parameters.dn.datatype=http://www.w3.org/2001/XMLSchema#string
+ldap1.resolver.ship.filter.parameters.dn.category=urn:oasis:names:tc:xacml:3.0:attribute-category:resource
+ldap1.resolver.ship.filter.parameters.dn.issuer=com:att:research:xacml:test:ldap
+ldap1.resolver.ship.filter.view=cn
+ldap1.resolver.ship.filter.view.cn.id=com:att:research:xacml:test:ldap:subject:ship
+ldap1.resolver.ship.filter.view.cn.datatype=http://www.w3.org/2001/XMLSchema#string
+ldap1.resolver.ship.filter.view.cn.category=urn:oasis:names:tc:xacml:3.0:attribute-category:resource
+ldap1.resolver.ship.filter.view.cn.issuer=com:att:research:xacml:test:ldap
+

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pdp-rest/config/xacml.policy.properties
----------------------------------------------------------------------
diff --git a/openaz-xacml-pdp-rest/config/xacml.policy.properties b/openaz-xacml-pdp-rest/config/xacml.policy.properties
new file mode 100755
index 0000000..d42c84c
--- /dev/null
+++ b/openaz-xacml-pdp-rest/config/xacml.policy.properties
@@ -0,0 +1,8 @@
+xacml.rootPolicies=annotation,ldap,csv,csvhyper,sql
+xacml.referencedPolicies=
+
+annotation.file=../XACML-TEST/testsets/annotation/AnnotationPolicy.v1.xml
+ldap.file=../XACML-TEST/testsets/pip/configurable-ldap/LDAP-Seven-Seas-v1.xml
+csv.file=../XACML-TEST/testsets/pip/configurable-csv/CSV-Baseball-Hall-Of-Fame-v1.xml
+csvhyper.file=../XACML-TEST/testsets/pip/configurable-csv-hyper/CSV-Legal-Age-Marriage-v1.xml
+sql.file=../XACML-TEST/testsets/pip/configurable-sql/SQL-World-Languages-v1.xml

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/94fcdd90/openaz-xacml-pdp-rest/pom.xml
----------------------------------------------------------------------
diff --git a/openaz-xacml-pdp-rest/pom.xml b/openaz-xacml-pdp-rest/pom.xml
new file mode 100755
index 0000000..edb97b4
--- /dev/null
+++ b/openaz-xacml-pdp-rest/pom.xml
@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <parent>
+        <artifactId>openaz</artifactId>
+        <groupId>org.openliberty.openaz</groupId>
+        <version>0.0.1-SNAPSHOT</version>
+    </parent>
+    <modelVersion>4.0.0</modelVersion>
+    <artifactId>openaz-xacml-pdp-rest</artifactId>
+
+    <dependencies>
+        <dependency>
+            <groupId>org.openliberty.openaz</groupId>
+            <artifactId>openaz-xacml</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openliberty.openaz</groupId>
+            <artifactId>openaz-xacml-rest</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.openliberty.openaz</groupId>
+            <artifactId>openaz-xacml-pdp</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>javax.servlet</groupId>
+            <artifactId>javax.servlet-api</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-logging</groupId>
+            <artifactId>commons-logging</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>log4j</groupId>
+            <artifactId>log4j</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.httpcomponents</groupId>
+            <artifactId>httpclient</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.google.guava</groupId>
+            <artifactId>guava</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.fasterxml.jackson.core</groupId>
+            <artifactId>jackson-databind</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>postgresql</groupId>
+            <artifactId>postgresql</artifactId>
+        </dependency>
+        <dependency>
+            <groupId>org.hsqldb</groupId>
+            <artifactId>hsqldb</artifactId>
+        </dependency>
+    </dependencies>
+
+</project>
\ No newline at end of file