You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openaz.apache.org by pd...@apache.org on 2016/03/17 02:15:33 UTC

[11/23] incubator-openaz git commit: Ported original att source to openaz This Closes #3

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/648d0c0d/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/OnDemandFileDownloader.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/OnDemandFileDownloader.java b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/OnDemandFileDownloader.java
new file mode 100644
index 0000000..ba8cd82
--- /dev/null
+++ b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/OnDemandFileDownloader.java
@@ -0,0 +1,65 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+
+package org.apache.openaz.xacml.admin.util;
+
+import java.io.IOException;
+
+import com.vaadin.server.FileDownloader;
+import com.vaadin.server.StreamResource;
+import com.vaadin.server.StreamResource.StreamSource;
+import com.vaadin.server.VaadinRequest;
+import com.vaadin.server.VaadinResponse;
+import com.vaadin.ui.UI;
+
+public class OnDemandFileDownloader extends FileDownloader {
+	private static final long serialVersionUID = 1L;
+	private final OnDemandStreamResource resource;
+
+	public interface OnDemandStreamResource extends StreamSource {
+		String getFilename ();
+	}
+
+  public OnDemandFileDownloader(OnDemandStreamResource resource) {
+		super(new StreamResource(resource, ""));
+		this.resource = resource;
+		if (this.resource == null) {
+			throw new NullPointerException("Can't send null resource");
+		}
+	}
+
+	@Override
+	public boolean handleConnectorRequest(VaadinRequest request,
+			VaadinResponse response, String path) throws IOException {
+		this.getResource().setFilename(this.resource.getFilename());
+		return super.handleConnectorRequest(request, response, path);
+	}
+
+	private StreamResource getResource() {
+		StreamResource resource = null;
+		UI.getCurrent().getSession().lock();
+		try {
+			resource = (StreamResource) this.getResource("dl");
+		} finally {
+			UI.getCurrent().getSession().unlock();
+		}
+		return resource;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/648d0c0d/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/RESTfulPAPEngine.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/RESTfulPAPEngine.java b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/RESTfulPAPEngine.java
new file mode 100644
index 0000000..3958100
--- /dev/null
+++ b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/RESTfulPAPEngine.java
@@ -0,0 +1,495 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+
+package org.apache.openaz.xacml.admin.util;
+
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.net.URLEncoder;
+import java.util.Collections;
+import java.util.Set;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.openaz.xacml.api.pap.PAPEngine;
+import org.apache.openaz.xacml.api.pap.PAPException;
+import org.apache.openaz.xacml.api.pap.PDP;
+import org.apache.openaz.xacml.api.pap.PDPGroup;
+import org.apache.openaz.xacml.api.pap.PDPPolicy;
+import org.apache.openaz.xacml.api.pap.PDPStatus;
+import org.apache.openaz.xacml.rest.XACMLRestProperties;
+import org.apache.openaz.xacml.std.pap.StdPDP;
+import org.apache.openaz.xacml.std.pap.StdPDPGroup;
+import org.apache.openaz.xacml.std.pap.StdPDPItemSetChangeNotifier;
+import org.apache.openaz.xacml.std.pap.StdPDPPolicy;
+import org.apache.openaz.xacml.std.pap.StdPDPStatus;
+import org.apache.openaz.xacml.util.XACMLProperties;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.CollectionType;
+
+/**
+ * Implementation of the PAPEngine interface that communicates with a PAP engine in a remote servlet
+ * through a RESTful interface
+ * 
+ * @author glenngriffin
+ *
+ */
+public class RESTfulPAPEngine extends StdPDPItemSetChangeNotifier implements PAPEngine {
+	private static final Log logger	= LogFactory.getLog(RESTfulPAPEngine.class);
+
+	//
+	// URL of the PAP Servlet that this Admin Console talks to
+	//
+	private String papServletURLString;
+	
+	/**
+	 * Set up link with PAP Servlet and get our initial set of Groups
+	 * @throws Exception 
+	 */
+	public RESTfulPAPEngine (String myURLString) throws PAPException, IOException  {
+		//
+		// Get our URL to the PAP servlet
+		//
+		this.papServletURLString = XACMLProperties.getProperty(XACMLRestProperties.PROP_PAP_URL);
+		if (this.papServletURLString == null || this.papServletURLString.length() == 0) {
+			String message = "The property 'POLICYENGINE_ADMIN_ACTIVE' was not set during installation.  Admin Console cannot call PAP.";
+			logger.error(message);
+			throw new PAPException(message);
+		}
+
+		//
+		// register this Admin Console with the PAP Servlet to get updates
+		//
+		Object newURL = sendToPAP("PUT", null, null, null, "adminConsoleURL=" + myURLString);
+		if (newURL != null) {
+			// assume this was a re-direct and try again
+			logger.warn("Redirecting to '" + newURL + "'");
+			this.papServletURLString = (String)newURL;
+			newURL = sendToPAP("PUT", null, null, null, "adminConsoleURL=" + myURLString);
+			if (newURL != null) {
+				logger.error("Failed to redirect to " + this.papServletURLString);
+				throw new PAPException("Failed to register with PAP");
+			}
+		}
+	}
+	
+
+	//
+	// High-level commands used by the Admin Console code through the PAPEngine Interface
+	//
+	
+	@Override
+	public PDPGroup getDefaultGroup() throws PAPException {
+		PDPGroup newGroup = (PDPGroup)sendToPAP("GET", null, null, StdPDPGroup.class, "groupId=", "default=");
+		return newGroup;
+	}
+
+	@Override
+	public void SetDefaultGroup(PDPGroup group) throws PAPException {
+		sendToPAP("POST", null, null, null, "groupId=" + group.getId(), "default=true");
+	}
+
+	@Override
+	public Set<PDPGroup> getPDPGroups() throws PAPException {
+		Set<PDPGroup> newGroupSet;
+		newGroupSet = (Set<PDPGroup>) this.sendToPAP("GET", null, Set.class, StdPDPGroup.class, "groupId=");
+		return Collections.unmodifiableSet(newGroupSet);
+	}
+
+
+	@Override
+	public PDPGroup getGroup(String id) throws PAPException {
+		PDPGroup newGroup = (PDPGroup)sendToPAP("GET", null, null, StdPDPGroup.class, "groupId=" + id);
+		return newGroup;
+	}
+
+	@Override
+	public void newGroup(String name, String description)
+			throws PAPException, NullPointerException {
+		String escapedName = null;
+		String escapedDescription = null;
+		try {
+			escapedName = URLEncoder.encode(name, "UTF-8");
+			escapedDescription = URLEncoder.encode(description, "UTF-8");
+		} catch (UnsupportedEncodingException e) {
+			throw new PAPException("Unable to send name or description to PAP: " + e.getMessage());
+		}
+		
+		this.sendToPAP("POST", null, null, null, "groupId=", "groupName="+escapedName, "groupDescription=" + escapedDescription);
+	}
+	
+	
+	/**
+	 * Update the configuration on the PAP for a single Group.
+	 * 
+	 * @param group
+	 * @return
+	 * @throws PAPException
+	 */
+	public void updateGroup(PDPGroup group) throws PAPException {
+
+		try {
+			
+			//
+			// ASSUME that all of the policies mentioned in this group are already located in the correct directory on the PAP!
+			//
+			// Whenever a Policy is added to the group, that file must be automatically copied to the PAP from the Workspace.
+			// 
+			
+			
+//			// Copy all policies from the local machine's workspace to the PAP's PDPGroup directory.
+//			// This is not efficient since most of the policies will already exist there.
+//			// However, the policy files are (probably!) not too huge, and this is a good way to ensure that any corrupted files on the PAP get refreshed.
+//			
+//TODO WRONG!!!!	The policy.getStream() is based on the location in the PAP directory, not the Workspace.
+//TODO 
+//			for (PDPPolicy policy : group.getPolicies()) {
+//				try (InputStream is = policy.getStream()) {
+//				copyFile(policy.getId(), group, is);
+//				}
+//			}
+			
+			// now update the group object on the PAP
+			
+			sendToPAP("PUT", group, null, null, "groupId=" + group.getId());
+		} catch (Exception e) {
+			String message = "Unable to PUT policy '" + group.getId() + "', e:" + e;
+			logger.error(message, e);
+			throw new PAPException(message);
+		}
+	}
+	
+	
+	@Override
+	public void removeGroup(PDPGroup group, PDPGroup newGroup)
+			throws PAPException, NullPointerException {
+		String moveToGroupString = null;
+		if (newGroup != null) {
+			moveToGroupString = "movePDPsToGroupId=" + newGroup.getId();
+		}
+		sendToPAP("DELETE", null, null, null, "groupId=" + group.getId(), moveToGroupString);
+	}
+	
+	@Override
+	public PDPGroup getPDPGroup(PDP pdp) throws PAPException {
+		return getPDPGroup(pdp.getId());
+	}
+
+	@Override
+	public PDPGroup getPDPGroup(String pdpId) throws PAPException {
+		PDPGroup newGroup = (PDPGroup)sendToPAP("GET", null, null, StdPDPGroup.class, "groupId=", "pdpId=" + pdpId, "getPDPGroup=");
+		return newGroup;
+	}
+
+	@Override
+	public PDP getPDP(String pdpId) throws PAPException {
+		PDP newPDP = (PDP)sendToPAP("GET", null, null, StdPDP.class, "groupId=", "pdpId=" + pdpId);
+		return newPDP;
+	}
+
+	@Override
+	public void newPDP(String id, PDPGroup group, String name, String description) throws PAPException,
+			NullPointerException {
+		StdPDP newPDP = new StdPDP(id, name, description);
+		sendToPAP("PUT", newPDP, null, null, "groupId=" + group.getId(), "pdpId=" + id);
+	}
+
+	
+	@Override
+	public void movePDP(PDP pdp, PDPGroup newGroup) throws PAPException {
+		sendToPAP("POST", null, null, null, "groupId=" + newGroup.getId(), "pdpId=" + pdp.getId());
+	}
+
+	@Override
+	public void updatePDP(PDP pdp) throws PAPException {
+		PDPGroup group = getPDPGroup(pdp);
+		sendToPAP("PUT", pdp, null, null, "groupId=" + group.getId(), "pdpId=" + pdp.getId());
+	}
+	
+	@Override
+	public void removePDP(PDP pdp) throws PAPException {
+		PDPGroup group = getPDPGroup(pdp);
+		sendToPAP("DELETE", null, null, null, "groupId=" + group.getId(), "pdpId=" + pdp.getId());
+	}
+
+	
+
+	@Override
+	public void publishPolicy(String id, String name, boolean isRoot,
+			InputStream policy, PDPGroup group) throws PAPException {
+		
+//TODO - this method should take as input a Policy object, add it to the group, then call updateGroup
+//TODO - ?? Where does the Policy object (with the version info) get created?
+
+		// copy the (one) file into the target directory on the PAP servlet
+		copyFile(id, group, policy);
+		
+		// adjust the local copy of the group to include the new policy
+		PDPPolicy pdpPolicy = new StdPDPPolicy(id, isRoot, name);
+		group.getPolicies().add(pdpPolicy);
+		
+		// tell the PAP servlet to include the policy in the configuration
+		updateGroup(group);
+	}
+	
+	
+	
+	/**
+	 * Copy a single Policy file from the input stream to the PAP Servlet.
+	 * Either this works (silently) or it throws an exception.
+	 * 
+	 * @param policyId
+	 * @param group
+	 * @param policy
+	 * @return
+	 * @throws PAPException
+	 */
+	public void copyFile(String policyId, PDPGroup group, InputStream policy) throws PAPException {
+		// send the policy file to the PAP Servlet
+		try {
+			sendToPAP("POST", policy, null, null, "groupId=" + group.getId(), "policyId="+policyId);
+		} catch (Exception e) {
+			String message = "Unable to PUT policy '" + policyId + "', e:" + e;
+			logger.error(message, e);
+			throw new PAPException(message);
+		}
+	}
+	
+
+	@Override
+	public void	copyPolicy(PDPPolicy policy, PDPGroup group) throws PAPException {
+		if (policy == null || group == null) {
+			throw new PAPException("Null input policy="+policy+"  group="+group);
+		}
+		try (InputStream is = new FileInputStream(new File(policy.getLocation())) ) {
+			copyFile(policy.getId(), group, is );
+		} catch (Exception e) {
+			String message = "Unable to PUT policy '" + policy.getId() + "', e:" + e;
+			logger.error(message, e);
+			throw new PAPException(message);
+		}
+	}
+
+
+	
+	
+	@Override
+	public void	removePolicy(PDPPolicy policy, PDPGroup group) throws PAPException {
+		throw new PAPException("NOT IMPLEMENTED");
+
+	}
+
+	
+	
+	/**
+	 * Special operation - Similar to the normal PAP operations but this one contacts the PDP directly
+	 * to get detailed status info.
+	 * 
+	 * @param pdp
+	 * @return
+	 * @throws PAPException 
+	 */
+	@Override
+	public PDPStatus getStatus(PDP pdp) throws PAPException {
+		StdPDPStatus status = (StdPDPStatus)sendToPAP("GET", pdp, null, StdPDPStatus.class);
+		return status;
+	}
+	
+	
+	
+	
+	//
+	// Internal Operations called by the PAPEngine Interface methods
+	//
+	
+	/**
+	 * Send a request to the PAP Servlet and get the response.
+	 * 
+	 * The content is either an InputStream to be copied to the Request OutputStream
+	 * 	OR it is an object that is to be encoded into JSON and pushed into the Request OutputStream.
+	 * 
+	 * The Request parameters may be encoded in multiple "name=value" sets, or parameters may be combined by the caller.
+	 * 
+	 * @param method
+	 * @param content	- EITHER an InputStream OR an Object to be encoded in JSON
+	 * @param collectionTypeClass
+	 * @param responseContentClass
+	 * @param parameters
+	 * @return
+	 * @throws Exception
+	 */
+	private Object sendToPAP(String method, Object content, Class collectionTypeClass, Class responseContentClass, String... parameters ) throws PAPException {
+		HttpURLConnection connection = null;
+		try {
+			String fullURL = papServletURLString;
+			if (parameters != null && parameters.length > 0) {
+				String queryString = "";
+				for (String p : parameters) {
+					queryString += "&" + p;
+				}
+				fullURL += "?" + queryString.substring(1);
+			}
+			
+			// special case - Status (actually the detailed status) comes from the PDP directly, not the PAP
+			if (method.equals("GET") &&
+					content instanceof PDP &&
+					responseContentClass == StdPDPStatus.class) {
+				// Adjust the url and properties appropriately
+				fullURL = ((PDP)content).getId() + "?type=Status";
+				content = null;
+			}
+			
+			
+			URL url = new URL(fullURL);
+
+			//
+			// Open up the connection
+			//
+			connection = (HttpURLConnection)url.openConnection();
+			//
+			// Setup our method and headers
+			//
+            connection.setRequestMethod(method);
+//				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.
+            //
+            connection.setInstanceFollowRedirects(false);
+			connection.setDoOutput(true);
+			connection.setDoInput(true);
+			if (content != null) {
+				if (content instanceof InputStream) {
+		    		try {
+		    			//
+		    			// Send our current policy configuration
+		    			//
+		    			try (OutputStream os = connection.getOutputStream()) {
+		    				int count = IOUtils.copy((InputStream)content, os);
+		    				if (logger.isDebugEnabled()) {
+		    					logger.debug("copied to output, bytes="+count);
+		    				}
+		    			}
+		    		} catch (Exception e) {
+		    			logger.error("Failed to write content in '" + method + "'", e);
+		    			throw e;
+		    		}
+				} else {
+					// The content is an object to be encoded in JSON
+		            ObjectMapper mapper = new ObjectMapper();
+		            mapper.writeValue(connection.getOutputStream(),  content);
+				}
+			}
+            //
+            // Do the connect
+            //
+            connection.connect();
+            if (connection.getResponseCode() == 204) {
+            	logger.info("Success - no content.");
+            	return null;
+            } else if (connection.getResponseCode() == 200) {
+            	logger.info("Success. We have a return object.");
+            	
+            	// get the response 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(connection.getInputStream());
+    		    scanner.useDelimiter("\\A");
+    		    json =  scanner.hasNext() ? scanner.next() : "";
+    		    scanner.close();
+    		    logger.info("JSON response from PAP: " + json);
+            	
+            	// convert Object sent as JSON into local object
+	            ObjectMapper mapper = new ObjectMapper();
+	            
+	            if (collectionTypeClass != null) {
+	            	// collection of objects expected
+	            	final CollectionType javaType = 
+	            	      mapper.getTypeFactory().constructCollectionType(collectionTypeClass, responseContentClass);
+
+	            	Object objectFromJSON = mapper.readValue(json, javaType);
+					return objectFromJSON;
+	            } else {
+	            	// single value object expected
+		            Object objectFromJSON = mapper.readValue(json, responseContentClass);
+					return objectFromJSON;
+	            }
+
+            } else if (connection.getResponseCode() >= 300 && connection.getResponseCode()  <= 399) {
+            	// redirection
+            	String newURL = connection.getHeaderField("Location");
+            	if (newURL == null) {
+            		logger.error("No Location header to redirect to when response code="+connection.getResponseCode());
+            		throw new IOException("No redirect Location header when response code="+connection.getResponseCode());
+            	}
+            	int qIndex = newURL.indexOf("?");
+            	if (qIndex > 0) {
+            		newURL = newURL.substring(0, qIndex);
+            	}
+            	logger.info("Redirect seen.  Redirecting " + fullURL + " to " + newURL);
+            	return newURL;
+            } else {
+            	logger.warn("Unexpected response code: " + connection.getResponseCode() + "  message: " + connection.getResponseMessage());
+            	throw new IOException("Server Response: " + connection.getResponseCode() + ": " + connection.getResponseMessage());
+            }
+
+		} catch (Exception e) {
+			logger.error("HTTP Request/Response to PAP: " + e,e);
+			throw new PAPException("Request/Response threw :" + e);
+		} finally {
+			// cleanup the connection
+				if (connection != null) {
+				try {
+					// For some reason trying to get the inputStream from the connection
+					// throws an exception rather than returning null when the InputStream does not exist.
+					InputStream is = null;
+					try {
+						is = connection.getInputStream();
+					} catch (Exception e1) { //NOPMD
+						// ignore this
+					}
+					if (is != null) {
+						is.close();
+					}
+
+				} catch (IOException ex) {
+					logger.error("Failed to close connection: " + ex, ex);
+				}
+				connection.disconnect();
+			}
+		}
+	}
+}
+
+

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/648d0c0d/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLConstants.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLConstants.java b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLConstants.java
new file mode 100644
index 0000000..28bf377
--- /dev/null
+++ b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLConstants.java
@@ -0,0 +1,237 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+
+package org.apache.openaz.xacml.admin.util;
+
+import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.openaz.xacml.api.Identifier;
+import org.apache.openaz.xacml.api.XACML3;
+
+public class XACMLConstants {
+
+	public static final Set<Identifier> SUBJECT_CATEGORIES = new HashSet<Identifier>();
+	public static final Set<Identifier> ACTION_CATEGORIES = new HashSet<Identifier>();
+	public static final Set<Identifier> RESOURCE_CATEGORIES = new HashSet<Identifier>();
+	public static final Set<Identifier> ENVIRONMENT_CATEGORIES = new HashSet<Identifier>();
+	
+	public static final Set<Identifier> CATEGORIES = new HashSet<Identifier>();
+	static {
+		SUBJECT_CATEGORIES.addAll(Arrays.asList(XACML3.ID_SUBJECT_CATEGORY_ACCESS_SUBJECT,
+												XACML3.ID_SUBJECT_CATEGORY_CODEBASE,
+												XACML3.ID_SUBJECT_CATEGORY_INTERMEDIARY_SUBJECT,
+												XACML3.ID_SUBJECT_CATEGORY_RECIPIENT_SUBJECT,
+												XACML3.ID_SUBJECT_CATEGORY_REQUESTING_MACHINE)
+												);
+		
+		ACTION_CATEGORIES.addAll(Arrays.asList(
+												XACML3.ID_ATTRIBUTE_CATEGORY_ACTION)
+												);
+		
+		RESOURCE_CATEGORIES.addAll(Arrays.asList(
+												XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE)
+												);
+		
+		ENVIRONMENT_CATEGORIES.addAll(Arrays.asList(
+												XACML3.ID_ATTRIBUTE_CATEGORY_ENVIRONMENT)
+												);
+		
+		CATEGORIES.addAll(SUBJECT_CATEGORIES);
+		CATEGORIES.addAll(ACTION_CATEGORIES);
+		CATEGORIES.addAll(RESOURCE_CATEGORIES);
+		CATEGORIES.addAll(ENVIRONMENT_CATEGORIES);
+		
+	}
+	public static final Set<Identifier> DATATYPES = new HashSet<Identifier>();
+	static {
+		DATATYPES.addAll(Arrays.asList(XACML3.ID_DATATYPE_STRING,
+										XACML3.ID_DATATYPE_BOOLEAN,
+										XACML3.ID_DATATYPE_INTEGER,
+										XACML3.ID_DATATYPE_DOUBLE,
+										XACML3.ID_DATATYPE_TIME,
+										XACML3.ID_DATATYPE_DATE,
+										XACML3.ID_DATATYPE_DATETIME,
+										XACML3.ID_DATATYPE_DAYTIMEDURATION,
+										XACML3.ID_DATATYPE_YEARMONTHDURATION,
+										XACML3.ID_DATATYPE_ANYURI,
+										XACML3.ID_DATATYPE_HEXBINARY,
+										XACML3.ID_DATATYPE_BASE64BINARY,
+										XACML3.ID_DATATYPE_RFC822NAME,
+										XACML3.ID_DATATYPE_X500NAME,
+										XACML3.ID_DATATYPE_IPADDRESS,
+										XACML3.ID_DATATYPE_DNSNAME));
+	}
+	
+	public static final Set<Identifier> POLICY_ALGORITHMS = new HashSet<Identifier>();
+	static {
+		POLICY_ALGORITHMS.addAll(Arrays.asList(
+				XACML3.ID_POLICY_DENY_OVERRIDES,
+				XACML3.ID_POLICY_DENY_UNLESS_PERMIT,
+				XACML3.ID_POLICY_FIRST_APPLICABLE,
+				XACML3.ID_POLICY_ON_PERMIT_APPLY_SECOND,
+				XACML3.ID_POLICY_ONLY_ONE_APPLICABLE,
+				XACML3.ID_POLICY_ORDERED_DENY_OVERRIDES,
+				XACML3.ID_POLICY_ORDERED_PERMIT_OVERRIDES,
+				XACML3.ID_POLICY_PERMIT_OVERRIDES,
+				XACML3.ID_POLICY_PERMIT_UNLESS_DENY
+				));
+	}
+	
+	public static final Set<Identifier> RULE_ALGORITHMS = new HashSet<Identifier>();
+	static {
+		RULE_ALGORITHMS.addAll(Arrays.asList(
+				XACML3.ID_RULE_DENY_OVERRIDES,
+				XACML3.ID_RULE_DENY_UNLESS_PERMIT,
+				XACML3.ID_RULE_FIRST_APPLICABLE,
+				XACML3.ID_RULE_ONLY_ONE_APPLICABLE,
+				XACML3.ID_RULE_ORDERED_DENY_OVERRIDES,
+				XACML3.ID_RULE_ORDERED_PERMIT_OVERRIDES,
+				XACML3.ID_RULE_PERMIT_OVERRIDES,
+				XACML3.ID_RULE_PERMIT_UNLESS_DENY
+				));
+	}
+
+	public static final Set<Identifier> STANDARD_ATTRIBUTES = new HashSet<Identifier>();
+	static {
+		STANDARD_ATTRIBUTES.addAll(Arrays.asList(
+				XACML3.ID_SUBJECT_SUBJECT_ID,
+				XACML3.ID_SUBJECT_SUBJECT_ID_QUALIFIER,
+				XACML3.ID_SUBJECT_KEY_INFO,
+				XACML3.ID_SUBJECT_AUTHENTICATION_TIME,
+				XACML3.ID_SUBJECT_AUTHENTICATION_METHOD,
+				XACML3.ID_SUBJECT_REQUEST_TIME,
+				XACML3.ID_SUBJECT_SESSION_START_TIME,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_IP_ADDRESS,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_DNS_NAME,
+				XACML3.ID_SUBJECT_ROLE,
+				XACML3.ID_ACTION_ACTION_ID,
+				XACML3.ID_ACTION_IMPLIED_ACTION,
+				XACML3.ID_RESOURCE_RESOURCE_ID,
+				XACML3.ID_RESOURCE_RESOURCE_LOCATION,
+				XACML3.ID_RESOURCE_SIMPLE_FILE_NAME,
+				XACML3.ID_ENVIRONMENT_CURRENT_DATE,
+				XACML3.ID_ENVIRONMENT_CURRENT_TIME,
+				XACML3.ID_ENVIRONMENT_CURRENT_DATETIME
+				));
+	}
+	
+	public static final Map<Identifier, Set<Identifier>> MAP_STANDARD_CATEGORIES = new HashMap<Identifier, Set<Identifier>>();
+	static {
+		MAP_STANDARD_CATEGORIES.put(XACML3.ID_SUBJECT_CATEGORY_ACCESS_SUBJECT, new HashSet<Identifier>(Arrays.asList(
+				XACML3.ID_SUBJECT_SUBJECT_ID,
+				XACML3.ID_SUBJECT_SUBJECT_ID_QUALIFIER,
+				XACML3.ID_SUBJECT_KEY_INFO,
+				XACML3.ID_SUBJECT_AUTHENTICATION_TIME,
+				XACML3.ID_SUBJECT_AUTHENTICATION_METHOD,
+				XACML3.ID_SUBJECT_REQUEST_TIME,
+				XACML3.ID_SUBJECT_SESSION_START_TIME,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_IP_ADDRESS,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_DNS_NAME,
+				XACML3.ID_SUBJECT_ROLE
+				)));
+		MAP_STANDARD_CATEGORIES.put(XACML3.ID_SUBJECT_CATEGORY_CODEBASE, new HashSet<Identifier>(Arrays.asList(
+				XACML3.ID_SUBJECT_SUBJECT_ID,
+				XACML3.ID_SUBJECT_SUBJECT_ID_QUALIFIER,
+				XACML3.ID_SUBJECT_KEY_INFO,
+				XACML3.ID_SUBJECT_AUTHENTICATION_TIME,
+				XACML3.ID_SUBJECT_AUTHENTICATION_METHOD,
+				XACML3.ID_SUBJECT_REQUEST_TIME,
+				XACML3.ID_SUBJECT_SESSION_START_TIME,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_IP_ADDRESS,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_DNS_NAME,
+				XACML3.ID_SUBJECT_ROLE
+				)));
+		MAP_STANDARD_CATEGORIES.put(XACML3.ID_SUBJECT_CATEGORY_INTERMEDIARY_SUBJECT, new HashSet<Identifier>(Arrays.asList(
+				XACML3.ID_SUBJECT_SUBJECT_ID,
+				XACML3.ID_SUBJECT_SUBJECT_ID_QUALIFIER,
+				XACML3.ID_SUBJECT_KEY_INFO,
+				XACML3.ID_SUBJECT_AUTHENTICATION_TIME,
+				XACML3.ID_SUBJECT_AUTHENTICATION_METHOD,
+				XACML3.ID_SUBJECT_REQUEST_TIME,
+				XACML3.ID_SUBJECT_SESSION_START_TIME,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_IP_ADDRESS,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_DNS_NAME,
+				XACML3.ID_SUBJECT_ROLE
+				)));
+		MAP_STANDARD_CATEGORIES.put(XACML3.ID_SUBJECT_CATEGORY_RECIPIENT_SUBJECT, new HashSet<Identifier>(Arrays.asList(
+				XACML3.ID_SUBJECT_SUBJECT_ID,
+				XACML3.ID_SUBJECT_SUBJECT_ID_QUALIFIER,
+				XACML3.ID_SUBJECT_KEY_INFO,
+				XACML3.ID_SUBJECT_AUTHENTICATION_TIME,
+				XACML3.ID_SUBJECT_AUTHENTICATION_METHOD,
+				XACML3.ID_SUBJECT_REQUEST_TIME,
+				XACML3.ID_SUBJECT_SESSION_START_TIME,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_IP_ADDRESS,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_DNS_NAME,
+				XACML3.ID_SUBJECT_ROLE
+				)));
+		MAP_STANDARD_CATEGORIES.put(XACML3.ID_SUBJECT_CATEGORY_REQUESTING_MACHINE, new HashSet<Identifier>(Arrays.asList(
+				XACML3.ID_SUBJECT_SUBJECT_ID,
+				XACML3.ID_SUBJECT_SUBJECT_ID_QUALIFIER,
+				XACML3.ID_SUBJECT_KEY_INFO,
+				XACML3.ID_SUBJECT_AUTHENTICATION_TIME,
+				XACML3.ID_SUBJECT_AUTHENTICATION_METHOD,
+				XACML3.ID_SUBJECT_REQUEST_TIME,
+				XACML3.ID_SUBJECT_SESSION_START_TIME,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_IP_ADDRESS,
+				XACML3.ID_SUBJECT_AUTHN_LOCALITY_DNS_NAME,
+				XACML3.ID_SUBJECT_ROLE
+				)));
+		MAP_STANDARD_CATEGORIES.put(XACML3.ID_ATTRIBUTE_CATEGORY_ACTION, new HashSet<Identifier>(Arrays.asList(
+				XACML3.ID_ACTION_ACTION_ID,
+				XACML3.ID_ACTION_IMPLIED_ACTION
+				)));
+		MAP_STANDARD_CATEGORIES.put(XACML3.ID_ATTRIBUTE_CATEGORY_RESOURCE, new HashSet<Identifier>(Arrays.asList(
+				XACML3.ID_RESOURCE_RESOURCE_ID,
+				XACML3.ID_RESOURCE_RESOURCE_LOCATION,
+				XACML3.ID_RESOURCE_SIMPLE_FILE_NAME
+				)));
+		MAP_STANDARD_CATEGORIES.put(XACML3.ID_ATTRIBUTE_CATEGORY_ENVIRONMENT, new HashSet<Identifier>(Arrays.asList(
+				XACML3.ID_ENVIRONMENT_CURRENT_DATE,
+				XACML3.ID_ENVIRONMENT_CURRENT_TIME,
+				XACML3.ID_ENVIRONMENT_CURRENT_DATETIME
+				)));
+	}
+	
+	public static String extractShortName(String xacmlID) {
+		if (xacmlID == null) {
+			return null;
+		}
+		if (xacmlID.startsWith("http:")) {
+			String [] parts = xacmlID.split("[#]");
+			if (parts != null && parts.length > 0) {
+				return parts[parts.length - 1];
+			}
+			return null;
+		}
+		if (xacmlID.startsWith("urn") || xacmlID.contains(":")) {
+			String[] parts = xacmlID.split("[:]");
+			
+			if (parts != null && parts.length > 0) {
+				return parts[parts.length - 1];
+			}
+		}	
+		return null;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/648d0c0d/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLFunctionValidator.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLFunctionValidator.java b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLFunctionValidator.java
new file mode 100644
index 0000000..7ef7e60
--- /dev/null
+++ b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLFunctionValidator.java
@@ -0,0 +1,506 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+
+package org.apache.openaz.xacml.admin.util;
+
+import java.util.List;
+
+import javax.xml.bind.JAXBElement;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import org.apache.openaz.xacml.admin.jpa.Datatype;
+import org.apache.openaz.xacml.admin.jpa.FunctionArgument;
+import org.apache.openaz.xacml.admin.jpa.FunctionDefinition;
+import org.apache.openaz.xacml.api.XACML3;
+import org.apache.openaz.xacml.std.IdentifierImpl;
+
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.ApplyType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeAssignmentExpressionType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeDesignatorType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeSelectorType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.AttributeValueType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.ConditionType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.FunctionType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableDefinitionType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.VariableReferenceType;
+
+public class XACMLFunctionValidator {
+	private static Log logger	= LogFactory.getLog(XACMLFunctionValidator.class);
+	
+	public static boolean	validNumberOfArguments(ApplyType apply) {
+		try {
+			//
+			// Sanity check
+			//
+			if (apply == null) {
+				throw new IllegalArgumentException("Must supply a non-null apply object.");
+			}
+			//
+			// Get the function
+			//
+			FunctionDefinition function = JPAUtils.getFunctionIDMap().get(apply.getFunctionId());
+			if (function == null) {
+				throw new Exception("Invalid function id: " + apply.getFunctionId());
+			}
+			//
+			// Now check argument list, do we have the minimum?
+			//
+			List<JAXBElement<?>> applyArgs = apply.getExpression();
+			if (applyArgs.isEmpty()) {
+				//
+				// May not need any args
+				//
+				if (function.getArgLb() > 0) {
+					throw new Exception ("Number of Args mismatch, expecting at least " + 
+							function.getArgLb() + " arguments but have zero.");
+				}
+			} else {
+				if (applyArgs.size() < function.getArgLb()) {
+					throw new Exception ("Number of Args mismatch, expecting at least " + 
+										function.getArgLb() + " arguments but have " + applyArgs.size());
+				}
+			}
+			//
+			// Is there an upper bound?
+			//
+			if (function.getArgUb() != -1 && applyArgs.size() != function.getArgUb()) {
+				throw new Exception ("Number of Args mismatch, expecting at most " + 
+						function.getArgUb() + " arguments but have " + applyArgs.size());
+			}
+		} catch (Exception e) {
+			logger.error("Number of arguments incorrect: " + e);
+			return false;
+		}
+		return true;
+	}
+	
+	public static boolean	canHaveMoreArguments(ConditionType condition) {
+		if (condition.getExpression() == null || condition.getExpression().getValue() == null) {
+			return true;
+		}
+		Object expression = condition.getExpression().getValue();
+		if (expression instanceof ApplyType) {
+			return XACMLFunctionValidator.canHaveMoreArguments((ApplyType) expression);
+		}
+		return false;
+	}
+	
+	public static boolean canHaveMoreArguments(VariableDefinitionType variable) {
+		if (variable.getExpression() == null || variable.getExpression().getValue() == null) {
+			return true;
+		}
+		Object expression = variable.getExpression().getValue();
+		if (expression instanceof ApplyType) {
+			return XACMLFunctionValidator.canHaveMoreArguments((ApplyType) expression);
+		}
+		return false;
+	}
+	
+	public static boolean canHaveMoreArguments(AttributeAssignmentExpressionType assignment) {
+		if (assignment.getExpression() == null || assignment.getExpression().getValue() == null) {
+			return true;
+		}
+		Object expression = assignment.getExpression().getValue();
+		if (expression instanceof ApplyType) {
+			return XACMLFunctionValidator.canHaveMoreArguments((ApplyType) expression);
+		}
+		return false;
+	}
+	
+	public static boolean	canHaveMoreArguments(ApplyType apply) {
+		//
+		// Sanity check
+		//
+		if (apply == null) {
+			throw new IllegalArgumentException("Must supply a non-null apply object.");
+		}
+		//
+		// Get the function
+		//
+		FunctionDefinition function = JPAUtils.getFunctionIDMap().get(apply.getFunctionId());
+		if (function == null) {
+			throw new IllegalArgumentException("Invalid function id: " + apply.getFunctionId());
+		}
+		//
+		// Is there no upper bound?
+		//
+		if (function.getArgUb() == -1) {
+			//
+			// No limit to the number of arguments
+			//
+			return true;
+		}
+		//
+		// There is an upper bound - have we met it?
+		//
+		List<JAXBElement<?>> applyArgs = apply.getExpression();
+		if (applyArgs.size() < function.getArgUb()) {
+			//
+			// We have not met the upper bound, so yes we can
+			// add more arguments.
+			//
+			return true;
+		}
+		return false;
+	}
+	
+	public static boolean	isFunctionAvailable(FunctionDefinition function, ApplyType apply, FunctionArgument argument) {
+		//
+		// Does it return correct datatype?
+		//
+		if (argument != null) {
+			//
+			// Does it match?
+			//
+			if (function.getDatatypeBean().getXacmlId().equals(argument.getDatatypeBean().getXacmlId())) {
+				return false;
+			}
+			if (function.getIsBagReturn() != argument.getIsBag()) {
+				return false;
+			}
+		}
+		//
+		// Check each argument
+		//
+		int i;
+		for (i = 0; i < apply.getExpression().size(); i++) {
+			//
+			// Get the argument
+			//
+			JAXBElement<?> arg = apply.getExpression().get(i);
+			//
+			// Get what the argument should be
+			//
+			FunctionArgument functionArg = XACMLFunctionValidator.getFunctionArgument(i + 1, function);
+			//
+			// Sanity check
+			//
+			if (arg == null || arg.getValue() == null || functionArg == null) {
+				//
+				// Something screwy with the list, just return not available. They will have
+				// delete items that are bad.
+				//
+				return false;
+			}
+			//
+			// Does it match?
+			//
+			if (XACMLFunctionValidator.isArgumentValid(arg.getValue(), functionArg) == false) {
+				return false;
+			}
+		}
+		//
+		// Done checking, we don't care if there are more args needed. Just want to know if this
+		// function can fit with the existing arguments.
+		//
+		return true;
+	}
+	
+	public static FunctionDefinition	validateApply(ApplyType apply, FunctionArgument argument) {
+		//
+		// Sanity check
+		//
+		if (apply == null) {
+			throw new IllegalArgumentException("Must supply a non-null apply object.");
+		}
+		try {
+			//
+			// Get the function
+			//
+			FunctionDefinition function = JPAUtils.getFunctionIDMap().get(apply.getFunctionId());
+			if (function == null) {
+				throw new Exception("Invalid function id: " + apply.getFunctionId());
+			}
+			//
+			// Does it return correct datatype?
+			//
+			if (argument != null) {
+				//
+				// Does it match?
+				//
+				if (argument.getDatatypeBean() != null) {
+					if (! function.getDatatypeBean().getXacmlId().equals(argument.getDatatypeBean().getXacmlId())) {
+						throw new Exception("Function return datatype(" + function.getDatatypeBean() + 
+								") does not match expected argument datatype (" + argument.getDatatypeBean());
+					}
+				} else {
+					if (logger.isDebugEnabled()) {
+						logger.debug("Argument datatype bean is null - any datatype should work.");
+					}
+				}
+				if (function.getIsBagReturn() != argument.getIsBag()) {
+					throw new Exception("Function is bag (" + function.getIsBagReturn() + 
+							") does not match argument isBag(" + argument.getIsBag());
+				}
+			}
+			//
+			// Now check argument list, do we have the minimum?
+			//
+			List<JAXBElement<?>> applyArgs = apply.getExpression();
+			if (applyArgs == null) {
+				//
+				// May not need any args
+				//
+				if (function.getArgLb() > 0) {
+					throw new Exception ("Number of Args mismatch, expecting at least " + 
+							function.getArgLb() + " arguments but have zero.");
+				}
+			} else {
+				if (applyArgs.size() < function.getArgLb()) {
+					throw new Exception ("Number of Args mismatch, expecting at least " + 
+										function.getArgLb() + " arguments but have " + applyArgs.size());
+				}
+			}
+			//
+			// Is there an upper bound?
+			//
+			if (function.getArgUb() != -1 && applyArgs.size() != function.getArgUb()) {
+				throw new Exception ("Number of Args mismatch, expecting at most " + 
+						function.getArgUb() + " arguments but have " + applyArgs.size());
+			}
+			//
+			// Check each argument
+			//
+			int i;
+			for (i = 0; i < applyArgs.size(); i++) {
+				//
+				// Get the argument
+				//
+				JAXBElement<?> arg = applyArgs.get(i);
+				//
+				// Get what the argument should be
+				//
+				FunctionArgument functionArg = XACMLFunctionValidator.getFunctionArgument(i + 1, function);
+				//
+				// Sanity check
+				//
+				if (arg == null || arg.getValue() == null || functionArg == null) {
+					throw new NullPointerException("An argument is null: Element" + arg + " Function Arg: " + functionArg);
+				}
+				//
+				// Does it match?
+				//
+				if (XACMLFunctionValidator.isArgumentValid(arg.getValue(), functionArg) == false) {
+					throw new Exception("Invalid Argument: " + arg.getValue());
+				}
+			}
+			//
+			// Done checking, just return the function which has the datatype
+			// and if it is a bag.
+			//
+			return function;
+		} catch(Exception e) {
+			logger.error("Function is not valid: " + apply.getFunctionId() + " argument: " + argument + " " + e);
+			return null;
+		}
+	}
+	
+	private static boolean isArgumentValid(Object value, FunctionArgument functionArg) {
+		if (value instanceof ApplyType) {
+			//
+			// Recursively validate the Apply.
+			//
+			FunctionDefinition function = XACMLFunctionValidator.validateApply((ApplyType) value, functionArg);
+			if (functionArg.getDatatypeBean() == null || function.getDatatypeBean().getId() == functionArg.getDatatypeBean().getId()) {
+				if (function.getIsBagReturn() == functionArg.getIsBag()) {
+					return true;
+				}
+				logger.error("isBag does not match: " + function.getIsBagReturn() + " " + functionArg.getIsBag());
+			}
+			logger.error("Datatypes do not match: " + function.getDatatypeBean().getShortName() + " " + functionArg.getDatatypeBean().getShortName());
+		} else if (value instanceof AttributeValueType) {
+			AttributeValueType val = (AttributeValueType) value;
+			Datatype datatype = JPAUtils.findDatatype(new IdentifierImpl(val.getDataType()));
+			if (functionArg.getDatatypeBean() == null || datatype.getId() == functionArg.getDatatypeBean().getId()) {
+				//
+				// TODO Is bag?
+				//
+				return true;
+			}
+			logger.error("Datatypes do not match: " + datatype.getShortName() + " " + functionArg.getDatatypeBean().getShortName());
+		} else if (value instanceof AttributeDesignatorType) {
+			AttributeDesignatorType designator = (AttributeDesignatorType) value;
+			Datatype datatype = JPAUtils.findDatatype(new IdentifierImpl(designator.getDataType()));
+			if (functionArg.getDatatypeBean() == null || datatype.getId() == functionArg.getDatatypeBean().getId()) {
+				//
+				// TODO Is bag?
+				//
+				return true;
+			}
+			logger.error("Datatypes do not match: " + datatype.getShortName() + " " + functionArg.getDatatypeBean().getShortName());
+		} else if (value instanceof AttributeSelectorType) {
+			AttributeSelectorType selector = (AttributeSelectorType) value;
+			Datatype datatype = JPAUtils.findDatatype(new IdentifierImpl(selector.getDataType()));
+			if (functionArg.getDatatypeBean() == null || datatype.getId() == functionArg.getDatatypeBean().getId()) {
+				//
+				// TODO Is bag?
+				//
+				return true;
+			}
+			logger.error("Datatypes do not match: " + datatype.getShortName() + " " + functionArg.getDatatypeBean().getShortName());
+		} else if (value instanceof VariableReferenceType) {
+			//
+			// We can't verify this at this time.
+			// The user can define variables in other parts of the policy file
+			// or another policy file. This should be flagged if the user performs
+			// simulation and other testing on the policy before deployment.
+			//
+			return true;
+		} else if (value instanceof FunctionType) {
+			//
+			// Does this function exist?
+			//
+			FunctionDefinition function = JPAUtils.findFunction(((FunctionType) value).getFunctionId());
+			if (function == null) {
+				//
+				// Could not find function
+				//
+				logger.warn("Could not find function in our database: " + ((FunctionType) value).getFunctionId());
+				return false;
+			}
+			//
+			// Does this function return the correct data type?
+			//
+			if (functionArg.getDatatypeBean() == null || function.getDatatypeBean().getId() == functionArg.getDatatypeBean().getId()) {
+				return true;
+			}			
+			logger.error("Datatypes do not match: " + function.getDatatypeBean().getShortName() + " " + functionArg.getDatatypeBean().getShortName());
+		}		
+		return false;
+	}
+
+	public static FunctionArgument getFunctionArgument(int index, FunctionDefinition function) {
+		if (index < 1) {
+			throw new IllegalArgumentException("The index must be 1-based");
+		}
+		//
+		// Setup what the actual lower bound and upper bounds are
+		// within the list.
+		//
+//		int lowerBound = function.getArgLb();
+//		if (lowerBound == 0) {
+//			lowerBound = 1;
+//		}
+		int upperBound = function.getArgUb();
+		if (upperBound == -1) {
+			upperBound = function.getFunctionArguments().size();
+		}
+		//
+		// The list may not be sorted, so make sure we find
+		// the actual argument index
+		//
+		int argumentIndex = index;
+		if (index >= upperBound) {
+			argumentIndex = upperBound;
+		}
+		for (FunctionArgument arg : function.getFunctionArguments()) {
+			if (arg.getArgIndex() == argumentIndex) {
+				return arg;
+			}
+		}
+		return null;
+	}
+
+	public static boolean validateCondition(ConditionType condition) {
+		if (condition.getExpression() == null) {
+			return false;
+		}
+		Object expression = condition.getExpression().getValue();
+		if (expression instanceof ApplyType) {
+			FunctionDefinition function = XACMLFunctionValidator.validateApply((ApplyType) expression, null);
+			if (function == null) {
+				return false;
+			}
+			if (function.isBagReturn()) {
+				return false;
+			}
+			if (function.getDatatypeBean() == null) {
+				return false;
+			}
+			return function.getDatatypeBean().getXacmlId().equals(XACML3.ID_DATATYPE_BOOLEAN.stringValue());
+		}
+		if (expression instanceof AttributeDesignatorType) {
+			return ((AttributeDesignatorType) expression).getDataType().equals(XACML3.ID_DATATYPE_BOOLEAN.stringValue());
+		}
+		if (expression instanceof AttributeSelectorType) {
+			return ((AttributeSelectorType) expression).getDataType().equals(XACML3.ID_DATATYPE_BOOLEAN.stringValue());
+		}
+		if (expression instanceof AttributeValueType) {
+			return ((AttributeValueType) expression).getDataType().equals(XACML3.ID_DATATYPE_BOOLEAN.stringValue());
+		}
+		if (expression instanceof VariableReferenceType) {
+			//
+			// Really unknown - the variable may or may not have been defined
+			//
+			return true;
+		}
+		return false;
+	}
+
+	public static boolean validateVariable(VariableDefinitionType variable) {
+		if (variable.getExpression() == null) {
+			return false;
+		}
+		Object expression = variable.getExpression().getValue();
+		if (expression instanceof ApplyType) {
+			FunctionDefinition function = XACMLFunctionValidator.validateApply((ApplyType) expression, null);
+			if (function == null) {
+				return false;
+			}
+			return true;
+		}
+		if (expression instanceof AttributeDesignatorType) {
+			return true;
+		}
+		if (expression instanceof AttributeSelectorType) {
+			return true;
+		}
+		if (expression instanceof AttributeValueType) {
+			return true;
+		}
+		if (expression instanceof VariableReferenceType) {
+			return true;
+		}
+		return false;
+	}
+
+	public static boolean validateAssignment(AttributeAssignmentExpressionType assignmentExpression) {
+		if (assignmentExpression.getExpression() == null) {
+			return false;
+		}
+		Object expression = assignmentExpression.getExpression().getValue();
+		if (expression instanceof ApplyType) {
+			FunctionDefinition function = XACMLFunctionValidator.validateApply((ApplyType) expression, null);
+			if (function == null) {
+				return false;
+			}
+			//
+			// TODO
+			//
+		}
+		//
+		// TODO
+		//
+		return true;
+	}
+
+}

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/648d0c0d/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLPolicyImporter.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLPolicyImporter.java b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLPolicyImporter.java
new file mode 100644
index 0000000..8a7c714
--- /dev/null
+++ b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/util/XACMLPolicyImporter.java
@@ -0,0 +1,495 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+
+package org.apache.openaz.xacml.admin.util;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.AdviceExpressionType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.EffectType;
+import oasis.names.tc.xacml._3_0.core.schema.wd_17.ObligationExpressionType;
+
+import org.apache.openaz.xacml.admin.XacmlAdminUI;
+import org.apache.openaz.xacml.admin.jpa.Attribute;
+import org.apache.openaz.xacml.admin.jpa.Category;
+import org.apache.openaz.xacml.admin.jpa.ConstraintType;
+import org.apache.openaz.xacml.admin.jpa.ConstraintValue;
+import org.apache.openaz.xacml.admin.jpa.Datatype;
+import org.apache.openaz.xacml.admin.jpa.Obadvice;
+import org.apache.openaz.xacml.api.Advice;
+import org.apache.openaz.xacml.api.AttributeValue;
+import org.apache.openaz.xacml.api.Identifier;
+import org.apache.openaz.xacml.api.Obligation;
+import org.apache.openaz.xacml.api.XACML3;
+import org.apache.openaz.xacml.util.XACMLObjectCopy;
+import org.apache.openaz.xacml.util.XACMLPolicyAggregator;
+import org.apache.openaz.xacml.util.XACMLPolicyScanner.CallbackResult;
+import com.vaadin.addon.jpacontainer.JPAContainer;
+import com.vaadin.addon.jpacontainer.provider.CachingMutableLocalEntityProvider;
+import com.vaadin.data.Buffered.SourceException;
+import com.vaadin.data.Validator.InvalidValueException;
+import com.vaadin.ui.UI;
+
+public class XACMLPolicyImporter extends XACMLPolicyAggregator {
+	private static Log logger	= LogFactory.getLog(XACMLPolicyImporter.class);
+	
+	public enum OPTION {
+		/*
+		 * Overwrite any existing ID
+		 */
+		OVERWRITE_EXISTING,
+		/*
+		 * Update an existing ID - only ADDs information
+		 */
+		UPDATE_EXISTING,
+		/*
+		 * Does not change an existing ID
+		 */
+		DONOTCHANGE_EXISTING
+	}
+	
+	public boolean	importAttributes = true;
+	public boolean	ignoreStandardAttributes = true;
+	public boolean	addConstraints = true;
+	public OPTION	attributeOption = OPTION.OVERWRITE_EXISTING;
+	public boolean	importObligations = true;
+	public OPTION	obligationOption = OPTION.OVERWRITE_EXISTING;
+	public boolean	importAdvice = true;
+	public OPTION	adviceOption = OPTION.OVERWRITE_EXISTING;
+
+	public XACMLPolicyImporter() {
+	}
+
+	public boolean isImportAttributes() {
+		return importAttributes;
+	}
+
+	public void setImportAttributes(boolean importAttributes) {
+		this.importAttributes = importAttributes;
+	}
+
+	public boolean isIgnoreStandardAttributes() {
+		return ignoreStandardAttributes;
+	}
+
+	public void setIgnoreStandardAttributes(boolean ignoreStandardAttributes) {
+		this.ignoreStandardAttributes = ignoreStandardAttributes;
+	}
+
+	public boolean isAddConstraints() {
+		return addConstraints;
+	}
+
+	public void setAddConstraints(boolean addConstraints) {
+		this.addConstraints = addConstraints;
+	}
+
+	public OPTION getAttributeOption() {
+		return attributeOption;
+	}
+
+	public void setAttributeOption(OPTION attributeOption) {
+		this.attributeOption = attributeOption;
+	}
+
+	public boolean isImportObligations() {
+		return importObligations;
+	}
+
+	public void setImportObligations(boolean importObligations) {
+		this.importObligations = importObligations;
+	}
+
+	public OPTION getObligationOption() {
+		return obligationOption;
+	}
+
+	public void setObligationOption(OPTION obligationOption) {
+		this.obligationOption = obligationOption;
+	}
+
+	public boolean isImportAdvice() {
+		return importAdvice;
+	}
+
+	public void setImportAdvice(boolean importAdvice) {
+		this.importAdvice = importAdvice;
+	}
+
+	public OPTION getAdviceOption() {
+		return adviceOption;
+	}
+
+	public void setAdviceOption(OPTION adviceOption) {
+		this.adviceOption = adviceOption;
+	}
+	@Override
+	public CallbackResult onObligation(Object parent, ObligationExpressionType expression, Obligation obligation) {
+		if (importObligations) {
+			super.onObligation(parent, expression, obligation);
+		}
+		return CallbackResult.CONTINUE;
+	}
+	
+	@Override
+	public CallbackResult onAttribute(Object parent, Object container, org.apache.openaz.xacml.api.Attribute attribute) {
+		if (importAttributes) {
+			super.onAttribute(parent, container, attribute);
+		}
+		return CallbackResult.CONTINUE;
+	}
+	
+	@Override
+	public CallbackResult onAdvice(Object parent, AdviceExpressionType expression, Advice advice) {
+		if (importAdvice) {
+			super.onAdvice(parent, expression, advice);
+		}
+		return CallbackResult.CONTINUE;
+	}
+
+	@Override
+	public void onFinishScan(Object root) {
+		if (this.importAttributes && this.doImportAttributes() > 0) {
+			((XacmlAdminUI)UI.getCurrent()).refreshAttributes();
+		}
+		int changes = 0;
+		if (this.importObligations) {
+			changes += this.doImportObligations();
+		}
+		if (this.importAdvice) {
+			changes += this.doImportAdvice();
+		}
+		//
+		// If changes were made, we need to tell the UI so the
+		// dictionary can refresh.
+		//
+		if (changes > 0) {
+			((XacmlAdminUI)UI.getCurrent()).refreshObadvice();
+		}
+	}
+	
+	@SuppressWarnings("unchecked")
+	protected int doImportAttributes() {
+		int changes = 0;
+		//
+		// Get our attributes. This container is modifiable.
+		//
+		JPAContainer<Attribute> attributes = ((XacmlAdminUI)UI.getCurrent()).getAttributes();
+		//
+		// Get mutable entity providers for these.
+		//
+		JPAContainer<Category> categories = new JPAContainer<Category>(Category.class);
+		categories.setEntityProvider(new CachingMutableLocalEntityProvider<Category>(Category.class, ((XacmlAdminUI)UI.getCurrent()).getEntityManager()));
+
+		JPAContainer<Datatype> datatypes = new JPAContainer<Datatype>(Datatype.class);
+		datatypes.setEntityProvider(new CachingMutableLocalEntityProvider<Datatype>(Datatype.class, ((XacmlAdminUI)UI.getCurrent()).getEntityManager()));
+		//
+		// Work the map
+		//
+		for (Identifier cat : this.attributeMap.keySet()) {
+			//
+			// Does category exist?
+			//
+			Category category = JPAUtils.findCategory(cat);
+			if (category == null) {
+				//
+				// This should rarely happen, but is possible since XACML 3.0
+				// you can define your own categories.
+				//
+				logger.warn("New category: " + cat);
+				category = new Category(cat, Category.CUSTOM);
+				String shortName = XACMLConstants.extractShortName(cat.stringValue());
+				if (shortName != null) {
+					category.setShortName(shortName);
+				} else {
+					category.setShortName(category.getXacmlId());
+				}
+				//
+				// Make sure the grouping is ok
+				//
+				if (category.getGrouping() == null) {
+					category.setGrouping(category.getShortName());
+				}
+				//
+				// Add it in
+				//
+				categories.addEntity(category);
+				//
+				// Tell the RO to update itself.
+				//
+				((XacmlAdminUI)UI.getCurrent()).getCategories().refresh();
+			}
+			Map<Identifier, Map<Identifier, Set<AttributeValue<?>>>> map = this.attributeMap.get(cat);
+			for (Identifier dt : map.keySet()) {
+				//
+				// Does datatype exist?
+				//
+				Datatype datatype = JPAUtils.findDatatype(dt);
+				if (datatype == null) {
+					//
+					// This should rarely happen, but is possible since XACML 3.0
+					// you can define new datatypes.
+					//
+					logger.warn("New datatype: " + dt);
+					datatype = new Datatype(dt, Datatype.CUSTOM);
+					String shortName = XACMLConstants.extractShortName(dt.stringValue());
+					if (shortName != null) {
+						datatype.setShortName(shortName);
+					} else {
+						datatype.setShortName(datatype.getXacmlId());
+					}
+					//
+					// Add it in
+					//
+					datatypes.addEntity(datatype);
+					//
+					// Tell the Read-Only property to update itself.
+					//
+					((XacmlAdminUI)UI.getCurrent()).getDatatypes().refresh();
+				}
+				//
+				// Iterate the attributes
+				//
+				for (Identifier id : map.get(dt).keySet()) {
+					//
+					// Do we ignore it if its standard?
+					//
+					if (! this.ignoreStandardAttributes || 
+						XACMLConstants.STANDARD_ATTRIBUTES.contains(id) == false) {
+						//
+						// Does it already exist?
+						//
+						Attribute newAttribute = null;
+						Attribute currentAttribute = JPAUtils.findAttribute(category, datatype, id.stringValue());
+						//
+						// Support for an existing attribute
+						//
+						if (currentAttribute != null) {
+							if (this.attributeOption == OPTION.OVERWRITE_EXISTING) {
+								newAttribute = currentAttribute;
+								newAttribute.setConstraintType(null);
+								newAttribute.removeAllConstraintValues();
+							} else if (this.attributeOption == OPTION.DONOTCHANGE_EXISTING) {
+								logger.info("Do not change existing: " + currentAttribute);
+								continue;
+							} else if (this.attributeOption == OPTION.UPDATE_EXISTING) {
+								newAttribute = currentAttribute;
+							}
+						} else {
+							//
+							// Create our new attribute
+							//
+							newAttribute = new Attribute(id.stringValue(), ((XacmlAdminUI)UI.getCurrent()).getUserid());
+							newAttribute.setCategoryBean(category);
+							newAttribute.setDatatypeBean(datatype);
+						}
+						//
+						// Get all the values
+						//
+						Set<AttributeValue<?>> values = map.get(dt).get(id);
+						//
+						// Do we have more than 1? Also, omit boolean datatype which
+						// doesn't make any sense to enumerate.
+						//
+						if (values.size() > 1 && dt.equals(XACML3.ID_DATATYPE_BOOLEAN) == false) {
+							//
+							// We have a lot of possible values, add as an enumeration
+							//
+							newAttribute.setConstraintType(JPAUtils.findConstraintType(ConstraintType.ENUMERATION_TYPE));
+							for (AttributeValue<?> value : values) {
+								Object val = value.getValue();
+								String content;
+								if (val instanceof Collection) {
+									content = XACMLObjectCopy.getContent((List<Object>) value.getValue());
+								} else {
+									content = val.toString();
+								}
+								//
+								// Check if we should add it in
+								//
+								boolean add = true;
+								//
+								// If we are updating an existing, we can really only do this for enumerations,
+								// its impossible to resolve a regular expression or range.
+								//
+								if (currentAttribute != null && this.attributeOption == OPTION.UPDATE_EXISTING &&
+											newAttribute.getConstraintType().getConstraintType().equals(ConstraintType.ENUMERATION_TYPE)) {
+									//
+									// Make sure it isn't there already, no duplicates.
+									//
+									for (ConstraintValue currentConstraintValue : newAttribute.getConstraintValues()) {
+										if (currentConstraintValue.getValue().equals(content)) {
+											add = false;
+											break;
+										}
+									}
+								}
+								if (add && content.isEmpty() == false) {
+									ConstraintValue newValue = new ConstraintValue("Enumeration", content);
+									newValue.setAttribute(newAttribute);
+									newAttribute.addConstraintValue(newValue);
+								}
+							}
+						}
+						//
+						// Add it
+						//
+						if (newAttribute != null) {
+							if (newAttribute.getId() == 0) {
+								logger.info("Adding new attribute");
+								if (attributes.addEntity(newAttribute) == null) {
+									logger.error("Failed to add new attribute: " + newAttribute);
+								} else {
+									changes++;
+								}
+							} else {
+								logger.info("Updating attribute " + newAttribute);
+								try {
+									attributes.commit();
+									changes++;
+								} catch (SourceException | InvalidValueException e) {
+									logger.error("Update failed: " + e.getLocalizedMessage());
+								}
+							}
+						}
+					}
+				}
+			}
+		}
+		return changes;
+	}
+
+	protected int doImportObligations() {
+		int changes = 0;
+		JPAContainer<Obadvice> oa = ((XacmlAdminUI)UI.getCurrent()).getObadvice();
+		for (Identifier id : this.obligationMap.keySet()) {
+			for (EffectType effect : this.obligationMap.get(id).keySet()) {
+				for (Obligation obligation : this.obligationMap.get(id).get(effect)) {
+					Obadvice newObligation = null;
+					Obadvice currentObligation = JPAUtils.findObligation(obligation.getId(), effect);
+					//
+					// Does it exist?
+					//
+					if (currentObligation != null) {
+						if (this.obligationOption == OPTION.OVERWRITE_EXISTING) {
+							newObligation = currentObligation;
+							newObligation.removeAllExpressions();
+						} else if (this.obligationOption == OPTION.DONOTCHANGE_EXISTING) {
+							continue;
+						} else if (this.obligationOption == OPTION.UPDATE_EXISTING) {
+							newObligation = currentObligation;
+						}
+					} else {
+						//
+						// Create new one
+						//
+						newObligation = new Obadvice(obligation.getId(), ((XacmlAdminUI)UI.getCurrent()).getUserid());
+						newObligation.setFulfillOn((effect == EffectType.PERMIT ? Obadvice.EFFECT_PERMIT : Obadvice.EFFECT_DENY));
+					}
+					//
+					// TODO add the expressions
+					//
+					
+					//
+					// Add it in
+					//
+					if (newObligation != null) {
+						if (newObligation.getId() == 0) {
+							logger.info("Adding obligation " + newObligation);
+							oa.addEntity(newObligation);
+							changes++;
+						} else {
+							logger.info("Updating obligation " + newObligation);
+							try {
+								oa.commit();
+								changes++;
+							} catch (SourceException | InvalidValueException e) {
+								logger.error("Update obligation failed " + e.getLocalizedMessage());
+							}
+						}
+					}
+				}
+			}
+		}
+		return changes;
+	}
+	
+	protected int doImportAdvice() {
+		int changes = 0;
+		JPAContainer<Obadvice> oa = ((XacmlAdminUI)UI.getCurrent()).getObadvice();
+		for (Identifier id : this.adviceMap.keySet()) {
+			for (EffectType effect : this.adviceMap.get(id).keySet()) {
+				for (Advice advice : this.adviceMap.get(id).get(effect)) {
+					Obadvice newAdvice = null;
+					Obadvice currentAdvice = JPAUtils.findAdvice(advice.getId(), effect);
+					//
+					// Does it exist?
+					//
+					if (currentAdvice != null) {
+						if (this.adviceOption == OPTION.OVERWRITE_EXISTING) {
+							newAdvice = currentAdvice;
+							newAdvice.removeAllExpressions();
+						} else if (this.adviceOption == OPTION.DONOTCHANGE_EXISTING) {
+							continue;
+						} else if (this.adviceOption == OPTION.UPDATE_EXISTING) {
+							newAdvice = currentAdvice;
+						}
+					} else {
+						//
+						// Create new one
+						//
+						newAdvice = new Obadvice(advice.getId(), ((XacmlAdminUI)UI.getCurrent()).getUserid());
+						newAdvice.setType(Obadvice.ADVICE);
+						newAdvice.setFulfillOn((effect == EffectType.PERMIT ? Obadvice.EFFECT_PERMIT : Obadvice.EFFECT_DENY));
+					}
+					//
+					// TODO add the expressions
+					//
+					
+					//
+					// Add it in
+					//
+					if (newAdvice != null) {
+						if (newAdvice.getId() == 0) {
+							logger.info("Adding advice " + newAdvice);
+							oa.addEntity(newAdvice);
+							changes++;
+						} else {
+							logger.info("Updating advice " + newAdvice);
+							try {
+								oa.commit();
+								changes++;
+							} catch (SourceException | InvalidValueException e) {
+								logger.error("Update advice failed " + e.getLocalizedMessage());
+							}
+						}
+					}
+				}
+			}
+		}
+		return changes;
+	}
+}

http://git-wip-us.apache.org/repos/asf/incubator-openaz/blob/648d0c0d/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/view/components/AttributeDictionarySelectorComponent.java
----------------------------------------------------------------------
diff --git a/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/view/components/AttributeDictionarySelectorComponent.java b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/view/components/AttributeDictionarySelectorComponent.java
new file mode 100644
index 0000000..0dd7f32
--- /dev/null
+++ b/openaz-xacml-pap-admin/src/main/java/org/apache/openaz/xacml/admin/view/components/AttributeDictionarySelectorComponent.java
@@ -0,0 +1,290 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ *
+ */
+
+package org.apache.openaz.xacml.admin.view.components;
+
+import org.apache.openaz.xacml.admin.XacmlAdminUI;
+import org.apache.openaz.xacml.admin.components.AttributeDictionary;
+import org.apache.openaz.xacml.admin.jpa.Attribute;
+import org.apache.openaz.xacml.admin.jpa.Category;
+import org.apache.openaz.xacml.admin.jpa.Datatype;
+import org.apache.openaz.xacml.admin.view.events.AttributeChangedEventListener;
+import org.apache.openaz.xacml.admin.view.events.AttributeChangedEventNotifier;
+import com.vaadin.addon.jpacontainer.JPAContainer;
+import com.vaadin.addon.jpacontainer.provider.CachingLocalEntityProvider;
+import com.vaadin.annotations.AutoGenerated;
+import com.vaadin.data.Property.ValueChangeEvent;
+import com.vaadin.data.Property.ValueChangeListener;
+import com.vaadin.data.util.filter.Compare;
+import com.vaadin.ui.AbstractSelect.ItemCaptionMode;
+import com.vaadin.ui.Alignment;
+import com.vaadin.ui.Button;
+import com.vaadin.ui.Button.ClickEvent;
+import com.vaadin.ui.Button.ClickListener;
+import com.vaadin.ui.ComboBox;
+import com.vaadin.ui.CustomComponent;
+import com.vaadin.ui.HorizontalLayout;
+import com.vaadin.ui.ListSelect;
+import com.vaadin.ui.UI;
+import com.vaadin.ui.VerticalLayout;
+
+public class AttributeDictionarySelectorComponent extends CustomComponent implements AttributeChangedEventNotifier {
+
+	/*- VaadinEditorProperties={"grid":"RegularGrid,20","showGrid":true,"snapToGrid":true,"snapToObject":true,"movingGuides":false,"snappingDistance":10} */
+
+	@AutoGenerated
+	private VerticalLayout mainLayout;
+	@AutoGenerated
+	private ListSelect listSelectAttribute;
+	@AutoGenerated
+	private HorizontalLayout horizontalLayout_2;
+	@AutoGenerated
+	private Button buttonNewAttribute;
+	@AutoGenerated
+	private ComboBox comboBoxCategoryFilter;
+	/**
+	 * 
+	 */
+	private static final long serialVersionUID = 1L;
+	private final AttributeDictionarySelectorComponent self = this;
+	private final Datatype datatype;
+	private final Attribute initialAttribute;
+	private final BasicNotifier notifier = new BasicNotifier();
+	private static final JPAContainer<Category>	categories = new JPAContainer<Category>(Category.class);
+	private static final JPAContainer<Attribute>	attributes = new JPAContainer<Attribute>(Attribute.class);
+	static {
+		attributes.setEntityProvider(new CachingLocalEntityProvider<Attribute>(Attribute.class, ((XacmlAdminUI)UI.getCurrent()).getEntityManager()));
+		categories.setEntityProvider(new CachingLocalEntityProvider<Category>(Category.class, ((XacmlAdminUI)UI.getCurrent()).getEntityManager()));
+		attributes.sort(new String[]{"xacmlId"}, new boolean[]{true});
+		categories.sort(new String[]{"xacmlId"}, new boolean[]{true});
+	}
+	/**
+	 * The constructor should first build the main layout, set the
+	 * composition root and then do any custom initialization.
+	 *
+	 * The constructor will not be automatically regenerated by the
+	 * visual editor.
+	 */
+	public AttributeDictionarySelectorComponent(Datatype datatype, Attribute initialAttribute) {
+		buildMainLayout();
+		setCompositionRoot(mainLayout);
+		//
+		// Save pointer and finish container initialization
+		//
+		this.datatype = datatype;
+		this.initialAttribute = initialAttribute;
+		//
+		// Initialize
+		//
+		this.initializeCategoryFilter();
+		this.initializeAttributes();
+		this.initializeButtons();
+		//
+		// Set our focus
+		//
+		this.listSelectAttribute.focus();
+	}
+	
+	protected void initializeCategoryFilter() {
+		//
+		// Remove any filters
+		//
+		AttributeDictionarySelectorComponent.categories.removeAllContainerFilters();
+		//
+		// Initialize data source and GUI properties
+		//
+		this.comboBoxCategoryFilter.setContainerDataSource(AttributeDictionarySelectorComponent.categories);
+		this.comboBoxCategoryFilter.setItemCaptionMode(ItemCaptionMode.PROPERTY);
+		this.comboBoxCategoryFilter.setItemCaptionPropertyId("xacmlId");
+		this.comboBoxCategoryFilter.setImmediate(true);
+		//
+		// Respond to events
+		//
+		this.comboBoxCategoryFilter.addValueChangeListener(new ValueChangeListener() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void valueChange(ValueChangeEvent event) {
+				//
+				// Clear any existing filters
+				//
+				AttributeDictionarySelectorComponent.attributes.removeAllContainerFilters();
+				//
+				// Get the current selection
+				//
+				Object id = self.comboBoxCategoryFilter.getValue();
+				//
+				// Is anything currently selected?
+				//
+				if (id != null) {
+					//
+					// Yes - add the new filter into the container
+					//
+					AttributeDictionarySelectorComponent.attributes.addContainerFilter(new Compare.Equal("categoryBean", AttributeDictionarySelectorComponent.categories.getItem(id).getEntity()));
+				}
+			}
+		});
+	}
+	
+	protected void initializeAttributes() {
+		//
+		// Remove any filters
+		//
+		AttributeDictionarySelectorComponent.attributes.removeAllContainerFilters();
+		//
+		// Initialize data source and GUI properties
+		//
+		this.listSelectAttribute.setContainerDataSource(AttributeDictionarySelectorComponent.attributes);
+		this.listSelectAttribute.setItemCaptionMode(ItemCaptionMode.PROPERTY);
+		this.listSelectAttribute.setItemCaptionPropertyId("xacmlId");
+		this.listSelectAttribute.setImmediate(true);
+		this.listSelectAttribute.setHeight(7, Unit.EM);
+		//
+		// Filter by datatype
+		//
+		if (this.datatype != null) {
+			AttributeDictionarySelectorComponent.attributes.addContainerFilter(new Compare.Equal("datatypeBean", this.datatype));
+		}
+		//
+		// Is there a default selection?  Is there an id?
+		//
+		if (this.initialAttribute != null && this.initialAttribute.getId() != 0) {
+			this.listSelectAttribute.select(this.initialAttribute.getId());
+		}
+		//
+		// Respond to events
+		//
+		this.listSelectAttribute.addValueChangeListener(new ValueChangeListener() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void valueChange(ValueChangeEvent event) {
+				self.fireAttributeChanged(self.getAttribute());
+			}
+		});
+	}
+	
+	protected void initializeButtons() {
+		this.buttonNewAttribute.addClickListener(new ClickListener() {
+			private static final long serialVersionUID = 1L;
+
+			@Override
+			public void buttonClick(ClickEvent event) {
+				AttributeDictionary.createNewAttributeWindow();
+			}
+		});
+	}
+	
+	@Override
+	public void commit() {
+		this.listSelectAttribute.commit();
+	}
+	
+	@Override
+	public Attribute getAttribute() {
+		Object id = this.listSelectAttribute.getValue();
+		if (id == null) {
+			return null;
+		}
+		return AttributeDictionarySelectorComponent.attributes.getItem(id).getEntity();
+	}
+
+	@Override
+	public boolean addListener(AttributeChangedEventListener listener) {
+		return this.notifier.addListener(listener);
+	}
+
+	@Override
+	public boolean removeListener(AttributeChangedEventListener listener) {
+		return this.notifier.removeListener(listener);
+	}
+
+	@Override
+	public void fireAttributeChanged(Attribute attribute) {
+		this.notifier.fireAttributeChanged(attribute);
+	}
+
+	@AutoGenerated
+	private VerticalLayout buildMainLayout() {
+		// common part: create layout
+		mainLayout = new VerticalLayout();
+		mainLayout.setImmediate(false);
+		mainLayout.setWidth("-1px");
+		mainLayout.setHeight("-1px");
+		mainLayout.setMargin(true);
+		mainLayout.setSpacing(true);
+		
+		// top-level component properties
+		setWidth("-1px");
+		setHeight("-1px");
+		
+		// horizontalLayout_2
+		horizontalLayout_2 = buildHorizontalLayout_2();
+		mainLayout.addComponent(horizontalLayout_2);
+		
+		// listSelectAttribute
+		listSelectAttribute = new ListSelect();
+		listSelectAttribute.setCaption("Dictionary Attributes");
+		listSelectAttribute.setImmediate(false);
+		listSelectAttribute.setWidth("100.0%");
+		listSelectAttribute.setHeight("-1px");
+		listSelectAttribute.setInvalidAllowed(false);
+		listSelectAttribute.setRequired(true);
+		mainLayout.addComponent(listSelectAttribute);
+		mainLayout.setExpandRatio(listSelectAttribute, 1.0f);
+		
+		return mainLayout;
+	}
+
+	@AutoGenerated
+	private HorizontalLayout buildHorizontalLayout_2() {
+		// common part: create layout
+		horizontalLayout_2 = new HorizontalLayout();
+		horizontalLayout_2.setImmediate(false);
+		horizontalLayout_2.setWidth("-1px");
+		horizontalLayout_2.setHeight("-1px");
+		horizontalLayout_2.setMargin(false);
+		horizontalLayout_2.setSpacing(true);
+		
+		// comboBoxCategoryFilter
+		comboBoxCategoryFilter = new ComboBox();
+		comboBoxCategoryFilter.setCaption("Filter Category");
+		comboBoxCategoryFilter.setImmediate(false);
+		comboBoxCategoryFilter.setWidth("-1px");
+		comboBoxCategoryFilter.setHeight("-1px");
+		horizontalLayout_2.addComponent(comboBoxCategoryFilter);
+		horizontalLayout_2.setExpandRatio(comboBoxCategoryFilter, 1.0f);
+		
+		// buttonNewAttribute
+		buttonNewAttribute = new Button();
+		buttonNewAttribute.setCaption("New Attribute");
+		buttonNewAttribute.setImmediate(true);
+		buttonNewAttribute
+				.setDescription("Click to create a new attribute in the dictionary.");
+		buttonNewAttribute.setWidth("-1px");
+		buttonNewAttribute.setHeight("-1px");
+		horizontalLayout_2.addComponent(buttonNewAttribute);
+		horizontalLayout_2.setComponentAlignment(buttonNewAttribute,
+				new Alignment(10));
+		
+		return horizontalLayout_2;
+	}
+
+}