This repository has been archived on 2018-08-08. You can view files and clone it, but cannot push or open issues or pull requests.
SecureBPMN/src/com.sun.xacml/src/main/java/com/sun/xacml/ConfigurationStore.java

1409 lines
45 KiB
Java

/*
* @(#)ConfigurationStore.java
*
* Copyright 2004-2006 Sun Microsystems, Inc. All Rights Reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* 1. Redistribution of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* 2. Redistribution in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the name of Sun Microsystems, Inc. or the names of contributors may
* be used to endorse or promote products derived from this software without
* specific prior written permission.
*
* This software is provided "AS IS," without a warranty of any kind. ALL
* EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND WARRANTIES, INCLUDING
* ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
* OR NON-INFRINGEMENT, ARE HEREBY EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN")
* AND ITS LICENSORS SHALL NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE
* AS A RESULT OF USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS
* DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR ANY LOST
* REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, CONSEQUENTIAL,
* INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND REGARDLESS OF THE THEORY
* OF LIABILITY, ARISING OUT OF THE USE OF OR INABILITY TO USE THIS SOFTWARE,
* EVEN IF SUN HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
*
* You acknowledge that this software is not designed or intended for use in
* the design, construction, operation or maintenance of any nuclear facility.
*/
package com.sun.xacml;
import com.sun.xacml.attr.AttributeDesignator;
import com.sun.xacml.attr.AttributeFactory;
import com.sun.xacml.attr.AttributeFactoryProxy;
import com.sun.xacml.attr.AttributeProxy;
import com.sun.xacml.attr.BaseAttributeFactory;
import com.sun.xacml.attr.StandardAttributeFactory;
import com.sun.xacml.combine.BaseCombiningAlgFactory;
import com.sun.xacml.combine.CombiningAlgFactory;
import com.sun.xacml.combine.CombiningAlgFactoryProxy;
import com.sun.xacml.combine.CombiningAlgorithm;
import com.sun.xacml.combine.StandardCombiningAlgFactory;
import com.sun.xacml.cond.BaseFunctionFactory;
import com.sun.xacml.cond.BasicFunctionFactoryProxy;
import com.sun.xacml.cond.Function;
import com.sun.xacml.cond.FunctionProxy;
import com.sun.xacml.cond.FunctionFactory;
import com.sun.xacml.cond.FunctionFactoryProxy;
import com.sun.xacml.cond.StandardFunctionFactory;
import com.sun.xacml.cond.cluster.FunctionCluster;
import com.sun.xacml.finder.AttributeFinder;
import com.sun.xacml.finder.AttributeFinderModule;
import com.sun.xacml.finder.PolicyFinder;
import com.sun.xacml.finder.PolicyFinderModule;
import com.sun.xacml.finder.RequiredAttributesModule;
import com.sun.xacml.finder.ResourceFinder;
import com.sun.xacml.finder.ResourceFinderModule;
import com.sun.xacml.finder.RevocationFinder;
import com.sun.xacml.finder.RevocationFinderModule;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.apache.log4j.Logger;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.xml.sax.SAXException;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* This class supports run-time loading of configuration data. It loads the
* configurations from an XML file that conforms to the configuration schema.
* By design this class does not get used automatically, nor does it change
* the state of the system directly. A programmer must choose to support this
* mechanism in their program, and then must explicitly use loaded elements.
* This way, the programmer still has full control over their security model,
* but also has the convenience of re-using a common configuration
* mechanism. See http://sunxacml.sourceforge.net/schema/config-0.4.xsd for
* the valid schema.
* <p>
* Note that becuase this doesn't tie directly into the rest of the code, you
* are still free to design your own run-time configuration mechanisms. This
* is simply provided as a convenience, and so that all programmers can start
* from a common point.
*
* @since 1.2
* @author Seth Proctor
*/
public class ConfigurationStore
{
/**
* Property used to specify the configuration file.
*/
public static final String PDP_CONFIG_PROPERTY =
"com.sun.xacml.PDPConfigFile";
// pdp elements
private PDPConfig defaultPDPConfig;
private HashMap<String, PDPConfig> pdpConfigMap;
// attribute factory elements
private AttributeFactory defaultAttributeFactory;
private HashMap<String, AttributeFactory> attributeMap;
// combining algorithm factory elements
private CombiningAlgFactory defaultCombiningFactory;
private HashMap<String, CombiningAlgFactory> combiningMap;
// function factory elements
private FunctionFactoryProxy defaultFunctionFactoryProxy;
private HashMap<String, FunctionFactoryProxy> functionMap;
private Map<String, Object> customAttr = new HashMap<String, Object>();
// the classloader we'll use for loading classes
private ClassLoader loader;
// // directory as base, where all files are searched
// @Deprecated
// private static String static_baseDir;
//
private String baseDir;
public static final String BASEDIR = "BASEDIR";
// the logger we'll use for all messages
private static final Logger logger =
Logger.getLogger(ConfigurationStore.class.getName());
/**
* Default constructor. This constructor uses the
* <code>PDP_CONFIG_PROPERTY</code> property to load the configuration.
* If the property isn't set, if it names a file that can't be accessed,
* or if the file is invalid, then an exception is thrown.
*
* @throws ParsingException if anything goes wrong during the parsing
* of the configuration file, the class loading,
* or the factory and pdp setup
*/
public ConfigurationStore() throws ParsingException {
String configFile = System.getProperty(PDP_CONFIG_PROPERTY);
// make sure that the right property was set
if (configFile == null) {
logger.warn("A property defining a config file was expected, " +
"but none was provided");
throw new ParsingException("Config property " +
PDP_CONFIG_PROPERTY +
" needs to be set");
}
try {
//setupConfig(new File(static_baseDir + configFile));
setupConfig(new File(configFile));
} catch (ParsingException pe) {
logger.error("Runtime config file (" + configFile + ") couldn't be loaded" +
" so no configurations will be available", pe);
throw pe;
}
}
/**
* Constructor that explicitly specifies the configuration file to load.
* This is useful if your security model doesn't allow the use of
* properties, if you don't want to use a property to specify a
* configuration file, or if you want to use more then one configuration
* file. If the file can't be accessed, or if the file is invalid, then
* an exception is thrown.
*
* @param configFile The configuration file.
*
* @throws ParsingException if anything goes wrong during the parsing
* of the configuration file, the class loading,
* or the factory and pdp setup
*/
public ConfigurationStore(File configFile) throws ParsingException {
// if there is no baseDir, it is assumed that the rest of the
//configuration files are stored in the same directory as the confFile
// if there is no file.separator, baseDir is set to ""
String absPath = configFile.getAbsolutePath();
String baseDir = absPath.substring(0,
absPath.lastIndexOf(System.getProperty("file.separator")) + 1 );
init(configFile, baseDir);
}
/**
* Constructor that explicitly specifies the configuration file to load.
* This is useful if your security model doesn't allow the use of
* properties, if you don't want to use a property to specify a
* configuration file, or if you want to use more then one configuration
* file. If the file can't be accessed, or if the file is invalid, then
* an exception is thrown.
*
* @param configFile The configuration file.
*
* @throws ParsingException if anything goes wrong during the parsing
* of the configuration file, the class loading,
* or the factory and pdp setup
*/
public ConfigurationStore(File configFile, String baseDir) throws ParsingException {
init(configFile, baseDir);
}
public ConfigurationStore(InputStream configFile, String baseDir) throws ParsingException {
init(configFile, baseDir);
}
public ConfigurationStore(String configuration, Map<String, Object> customAttrs) throws ParsingException {
this.customAttr = customAttrs;
if ( customAttrs.containsKey(BASEDIR)) {
this.baseDir = (String) customAttrs.get(BASEDIR);
}
init(configuration);
}
private void init(File configFile, String baseDir) throws ParsingException {
setBaseDir(baseDir);
try {
setupConfig(configFile);
} catch (ParsingException pe) {
logger.warn("Runtime config file (" + configFile + ")couldn't be loaded" +
" so no configurations will be available", pe);
throw pe;
}
}
private void init(InputStream configFile, String baseDir) throws ParsingException {
setBaseDir(baseDir);
try {
setupConfig(configFile);
} catch (ParsingException pe) {
logger.warn("Runtime config file (" + configFile + ")couldn't be loaded" +
" so no configurations will be available", pe);
throw pe;
}
}
protected void init(String configuration) throws ParsingException {
setBaseDir("");
try {
setupConfig(configuration);
} catch (ParsingException pe) {
logger.warn("Runtime configuration couldn't be loaded" +
" so no configurations will be available (" + pe.getMessage() + ")", pe);
throw pe;
}
}
private void setBaseDir(String baseDir) {
if ( baseDir == null ) {
baseDir = "";
}
this.baseDir = baseDir;
customAttr.put(BASEDIR, baseDir);
}
private void setupConfig(String configuration) throws ParsingException {
setupConfig(getRootNode(configuration));
}
private void setupConfig(InputStream configuration) throws ParsingException {
setupConfig(getRootNode(configuration));
}
private void setupConfig(File configFile) throws ParsingException {
setupConfig(getRootNode(configFile));
}
/**
* Private helper function used by both constructors to actually load the
* configuration data. This is the root of several private methods used
* to setup all the pdps and factories.
*
* @param configFile The configuration file.
*
* @throws ParsingException
*/
//private void setupConfig(File configFile) throws ParsingException {
private void setupConfig(Node root) throws ParsingException {
logger.info("Loading runtime configuration");
// load our classloader
this.loader = getClass().getClassLoader();
// get the root node from the configuration file
//Node root = getRootNode(configFile);
// initialize all the maps
this.pdpConfigMap = new HashMap<String, PDPConfig>();
this.attributeMap = new HashMap<String, AttributeFactory>();
this.combiningMap = new HashMap<String, CombiningAlgFactory>();
this.functionMap = new HashMap<String, FunctionFactoryProxy>();
if ( customAttr == null ) {
this.customAttr = new HashMap<String, Object>();
}
// get the default names
NamedNodeMap attrs = root.getAttributes();
String defaultPDP = attrs.getNamedItem("defaultPDP").getNodeValue();
String defaultAF = getDefaultFactory(attrs, "defaultAttributeFactory");
String defaultCAF = getDefaultFactory(attrs,
"defaultCombiningAlgFactory");
String defaultFF = getDefaultFactory(attrs, "defaultFunctionFactory");
// loop through all the root-level elements, for each one getting its
// name and then loading the right kind of element
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String childName = child.getNodeName();
String elementName = null;
// get the element's name
if (child.getNodeType() == Node.ELEMENT_NODE &&
child.getAttributes().getNamedItem("name") != null ) {
elementName = child.getAttributes().
getNamedItem("name").getNodeValue();
}
// see if this is a pdp or a factory, and load accordingly,
// putting the new element into the respective map...make sure
// that we're never loading something with the same name twice
if (childName.equals("pdp")) {
if (logger.isDebugEnabled()) {
logger.debug("Loading PDP: " + elementName);
}
if (this.pdpConfigMap.containsKey(elementName)) {
throw new ParsingException("more that one pdp with " +
"name \"" + elementName +"\"");
}
this.pdpConfigMap.put(elementName, parsePDPConfig(child));
} else if (childName.equals("attributeFactory")) {
if (logger.isDebugEnabled()) {
logger.debug("Loading AttributeFactory: " + elementName);
}
if (this.attributeMap.containsKey(elementName)) {
throw new ParsingException("more that one " +
"attributeFactory with name " +
elementName +"\"");
}
this.attributeMap.put(elementName,
parseAttributeFactory(child));
} else if (childName.equals("combiningAlgFactory")) {
if (logger.isDebugEnabled()) {
logger.debug("Loading CombiningAlgFactory: " +
elementName);
}
if (this.combiningMap.containsKey(elementName)) {
throw new ParsingException("more that one " +
"combiningAlgFactory with " +
"name \"" + elementName +"\"");
}
this.combiningMap.put(elementName,
parseCombiningAlgFactory(child));
} else if (childName.equals("functionFactory")) {
if (logger.isDebugEnabled()) {
logger.debug("Loading FunctionFactory: " + elementName);
}
if (this.functionMap.containsKey(elementName)) {
throw new ParsingException("more that one functionFactory"
+ " with name \"" +
elementName +"\"");
}
this.functionMap.put(elementName, parseFunctionFactory(child));
} else {
//custom attribtue
if ( child.getNodeType() == Node.ELEMENT_NODE ) {
String key = null, value = null;
if ( child.getFirstChild() != null ) {
key = childName;
if ( child.getFirstChild().getNodeType() == Node.TEXT_NODE ) {
value = child.getFirstChild().getNodeValue().trim();
}
} else {
NamedNodeMap foo = child.getAttributes();
if (foo != null ) {
key = foo.getNamedItem("key").getNodeValue();
value = foo.getNamedItem("value").getNodeValue();
}
}
if ( key != null && value != null ) {
if (logger.isDebugEnabled()) {
logger.debug("Read custom attribute " + key + " with value " + value);
}
customAttr.put(key, value);
}
}
}
}
// copy custom attributes to PDP configs
if ( customAttr.size() > 0 ) {
for (String pdpKey : this.pdpConfigMap.keySet()) {
pdpConfigMap.get(pdpKey).addCutomAttrs(customAttr);
}
}
// finally, extract the default elements
this.defaultPDPConfig = (PDPConfig)(this.pdpConfigMap.get(defaultPDP));
this.defaultAttributeFactory = (AttributeFactory)
(this.attributeMap.get(defaultAF));
if (this.defaultAttributeFactory == null) {
try {
this.defaultAttributeFactory =
AttributeFactory.getInstance(defaultAF);
} catch (Exception e) {
throw new ParsingException("Unknown AttributeFactory", e);
}
}
/* start hack: required to include datatypes configured in pdp configuration */
AttributeFactoryProxy attrProxy = new AttributeFactoryProxy() {
public AttributeFactory getFactory() {
return defaultAttributeFactory;
}
};
BaseAttributeFactory.setDefaultFactory(attrProxy);
/* end hack */
this.defaultCombiningFactory = (CombiningAlgFactory)
(this.combiningMap.get(defaultCAF));
if (this.defaultCombiningFactory == null) {
try {
this.defaultCombiningFactory =
CombiningAlgFactory.getInstance(defaultCAF);
} catch (Exception e) {
throw new ParsingException("Unknown CombininAlgFactory", e);
}
}
this.defaultFunctionFactoryProxy = (FunctionFactoryProxy)
(this.functionMap.get(defaultFF));
if (this.defaultFunctionFactoryProxy == null) {
try {
this.defaultFunctionFactoryProxy =
FunctionFactory.getInstance(defaultFF);
} catch (Exception e) {
throw new ParsingException("Unknown FunctionFactory", e);
}
}
/* start hack: required to include functions configured in pdp configuration */
BaseFunctionFactory.setDefaultFactory(defaultFunctionFactoryProxy);
/* end hack */
}
/**
* Private helper that gets a default factory identifier, or fills in
* the default value if no identifier is provided.
*
* @param attrs The XML-attributes of the configuration.
* @param factoryName The name of the default factory XML-attribute.
*
* @return The identifier of the default factory.
*/
private String getDefaultFactory(NamedNodeMap attrs, String factoryName) {
Node node = attrs.getNamedItem(factoryName);
if (node != null) {
return node.getNodeValue();
}
return Constants.XACML_1_0_IDENTIFIER;
}
private Node getRootNode(File configFile) throws ParsingException {
try {
return getRootNode(new FileInputStream(configFile));
} catch(IOException ioe) {
throw new ParsingException("failed to load the file ", ioe);
}
}
private Node getRootNode(InputStream configFile) throws ParsingException {
DocumentBuilder db = createDocumentBuilder();
Document doc = null;
try {
doc = db.parse(configFile);
} catch (IOException ioe) {
throw new ParsingException("failed to load the file ", ioe);
} catch (SAXException saxe) {
throw new ParsingException("error parsing the XML tree", saxe);
} catch (IllegalArgumentException iae) {
throw new ParsingException("no data to parse", iae);
}
return getRootNode(doc);
}
private Node getRootNode(String configuration) throws ParsingException {
DocumentBuilder db = createDocumentBuilder();
Document doc = null;
try {
doc = db.parse(new ByteArrayInputStream(configuration.getBytes()));
} catch (IOException ioe) {
throw new ParsingException("failed to load the configuration from string ", ioe);
} catch (SAXException saxe) {
throw new ParsingException("error parsing the XML tree", saxe);
} catch (IllegalArgumentException iae) {
throw new ParsingException("no data to parse", iae);
}
return getRootNode(doc);
}
private DocumentBuilder createDocumentBuilder() throws ParsingException {
DocumentBuilderFactory dbFactory =
DocumentBuilderFactory.newInstance();
dbFactory.setIgnoringComments(true);
dbFactory.setNamespaceAware(false);
dbFactory.setValidating(false);
try {
return dbFactory.newDocumentBuilder();
} catch (ParserConfigurationException pce) {
throw new ParsingException("couldn't get a document builder", pce);
}
}
/**
* Private helper that parses the file and sets up the DOM tree.
*
* @param configFile The filename of the configuration file.
*
* @return The DOM-tree containing the configuration.
*
* @throws ParsingException
*/
//private Node getRootNode(File configFile) throws ParsingException {
private Node getRootNode(Document doc) throws ParsingException {
// DocumentBuilderFactory dbFactory =
// DocumentBuilderFactory.newInstance();
//
// dbFactory.setIgnoringComments(true);
// dbFactory.setNamespaceAware(false);
// dbFactory.setValidating(false);
//
// DocumentBuilder db = null;
// try {
// db = dbFactory.newDocumentBuilder();
// } catch (ParserConfigurationException pce) {
// throw new ParsingException("couldn't get a document builder", pce);
// }
//
// Document doc = null;
// try {
// doc = db.parse(new FileInputStream(configFile));
// } catch (IOException ioe) {
// throw new ParsingException("failed to load the file ", ioe);
// } catch (SAXException saxe) {
// throw new ParsingException("error parsing the XML tree", saxe);
// } catch (IllegalArgumentException iae) {
// throw new ParsingException("no data to parse", iae);
// }
Element root = doc.getDocumentElement();
if (! root.getTagName().equals("config")) {
throw new ParsingException("unknown document type: " +
root.getTagName());
}
return root;
}
/**
* Private helper that handles the pdp elements.
*
* @param root The node containing the finder module class names.
*
* @return The PDP configuration object.
*
* @throws ParsingException
*/
private PDPConfig parsePDPConfig(Node root) throws ParsingException {
// ArrayList attrModules = new ArrayList();
// HashSet policyModules = new HashSet();
// ArrayList rsrcModules = new ArrayList();
// ArrayList revocationModules = new ArrayList();
//
// PDPConfig pdpConfig = new PDPConfig();
//
// // go through all elements of the pdp, loading the specified modules
// NodeList children = root.getChildNodes();
// for (int i = 0; i < children.getLength(); i++) {
// Node child = children.item(i);
// String name = child.getNodeName();
//
// if (name.equals("policyFinderModule")) {
// policyModules.add(loadClass("module", child));
// } else if (name.equals("attributeFinderModule")) {
// attrModules.add(loadClass("module", child));
// } else if (name.equals("resourceFinderModule")) {
// rsrcModules.add(loadClass("module", child));
// } else if (name.equals("revocationFinderModule")) {
// revocationModules.add(loadClass("module", child));
// }
// }
//
// // after loading the modules, use the collections to setup a
// // PDPConfig based on this pdp element
//
// AttributeFinder attrFinder = new AttributeFinder();
// attrFinder.setModules(attrModules);
//
// PolicyFinder policyFinder = new PolicyFinder();
// policyFinder.setModules(policyModules);
//
// ResourceFinder rsrcFinder = new ResourceFinder();
// rsrcFinder.setModules(rsrcModules);
//
// RevocationFinder revocationFinder = new RevocationFinder();
// revocationFinder.setModules(revocationModules);
//
// pdpConfig.setFinder(attrFinder, policyFinder, rsrcFinder,
// revocationFinder);
//
// return pdpConfig;
ArrayList<AttributeFinderModule> attrModules = new ArrayList<AttributeFinderModule>();
HashSet<PolicyFinderModule> policyModules = new HashSet<PolicyFinderModule>();
ArrayList<ResourceFinderModule> rsrcModules = new ArrayList<ResourceFinderModule>();
ArrayList<RevocationFinderModule> revocationModules = new ArrayList<RevocationFinderModule>();
ArrayList<RequiredAttributesModule> requiredAttrModules = new ArrayList<RequiredAttributesModule>();
// go through all elements of the pdp, loading the specified modules
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String name = child.getNodeName();
if (name.equals("policyFinderModule")) {
policyModules.add((PolicyFinderModule)loadClass("module", child));
} else if (name.equals("attributeFinderModule")) {
attrModules.add((AttributeFinderModule)loadClass("module", child));
} else if (name.equals("resourceFinderModule")) {
rsrcModules.add((ResourceFinderModule)loadClass("module", child));
} else if (name.equals("revocationFinderModule")) {
revocationModules.add((RevocationFinderModule)loadClass("module", child));
} else if (name.equals("requiredAttributesModule")) {
requiredAttrModules.add((RequiredAttributesModule) loadClass("module", child));
}
}
// after loading the modules, use the collections to setup a
// PDPConfig based on this pdp element
AttributeFinder attrFinder = new AttributeFinder();
attrFinder.setModules(attrModules);
PolicyFinder policyFinder = new PolicyFinder();
policyFinder.setModules(policyModules);
ResourceFinder rsrcFinder = new ResourceFinder();
rsrcFinder.setModules(rsrcModules);
RevocationFinder revocationFinder = new RevocationFinder();
revocationFinder.setModules(revocationModules);
AttributeDesignator.setRequiredAttrModules(requiredAttrModules);
return new PDPConfig(attrFinder, policyFinder, rsrcFinder,
revocationFinder);
}
/**
* Private helper that handles the attributeFactory elements.
*
* @param root The node containing the attribute factory definition.
*
* @return The attribute factory object.
*
* @throws ParsingException
*/
private AttributeFactory parseAttributeFactory(Node root)
throws ParsingException
{
AttributeFactory factory = null;
// check if we're starting with the standard factory setup
if (useStandard(root, "useStandardDatatypes")) {
logger.debug("Starting with standard Datatypes");
factory = StandardAttributeFactory.getNewFactory();
} else {
factory = new BaseAttributeFactory();
}
// now look for all datatypes specified for this factory, adding
// them as we go
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeName().equals("datatype")) {
// a datatype is a class with an identifier
String identifier = child.getAttributes().
getNamedItem("identifier").getNodeValue();
AttributeProxy proxy =
(AttributeProxy)(loadClass("datatype", child));
try {
factory.addDatatype(identifier, proxy);
} catch (IllegalArgumentException iae) {
throw new ParsingException("duplicate datatype: " +
identifier, iae);
}
}
}
return factory;
}
/**
* Private helper that handles the combiningAlgFactory elements.
*/
private CombiningAlgFactory parseCombiningAlgFactory(Node root)
throws ParsingException
{
CombiningAlgFactory factory = null;
// check if we're starting with the standard factory setup
if (useStandard(root, "useStandardAlgorithms")) {
logger.debug("Starting with standard Combining Algorithms");
factory = StandardCombiningAlgFactory.getNewFactory();
} else {
factory = new BaseCombiningAlgFactory();
}
CombiningAlgFactory.setAllFactories(factory);
// now look for all algorithms specified for this factory, adding
// them as we go
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
if (child.getNodeName().equals("algorithm")) {
// an algorithm is a simple class element
CombiningAlgorithm alg =
(CombiningAlgorithm)(loadClass("algorithm", child));
try {
factory.addAlgorithm(alg);
} catch (IllegalArgumentException iae) {
throw new ParsingException("duplicate combining " +
"algorithm: " +
alg.getIdentifier().toString(),
iae);
}
}
}
return factory;
}
/**
* Private helper that handles the functionFactory elements. This one
* is a little more complex than the other two factory helper methods,
* since it consists of three factories (target, condition, and general).
*/
private FunctionFactoryProxy parseFunctionFactory(Node root)
throws ParsingException
{
FunctionFactoryProxy proxy = null;
FunctionFactory generalFactory = null;
FunctionFactory conditionFactory = null;
FunctionFactory targetFactory = null;
// check if we're starting with the standard factory setup, and
// make sure that the proxy is pre-configured
if (useStandard(root, "useStandardFunctions")) {
logger.debug("Starting with standard Functions");
proxy = StandardFunctionFactory.getNewFactoryProxy();
targetFactory = proxy.getTargetFactory();
conditionFactory = proxy.getConditionFactory();
generalFactory = proxy.getGeneralFactory();
} else {
generalFactory = new BaseFunctionFactory();
conditionFactory = new BaseFunctionFactory(generalFactory);
targetFactory = new BaseFunctionFactory(conditionFactory);
proxy = new BasicFunctionFactoryProxy(targetFactory,
conditionFactory,
generalFactory);
}
// go through and load the three sections, putting the loaded
// functions into the appropriate factory
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String name = child.getNodeName();
if (name.equals("target")) {
logger.debug("Loading [TARGET] functions");
functionParserHelper(child, targetFactory);
} else if (name.equals("condition")) {
logger.debug("Loading [CONDITION] functions");
functionParserHelper(child, conditionFactory);
} else if (name.equals("general")) {
logger.debug("Loading [GENERAL] functions");
functionParserHelper(child, generalFactory);
}
}
return proxy;
}
/**
* Private helper used by the function factory code to load a specific
* target, condition, or general section.
*/
private void functionParserHelper(Node root, FunctionFactory factory)
throws ParsingException
{
// go through all elements in the section
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String name = child.getNodeName();
if (name.equals("function")) {
// a function section is a simple class element
Function function =
(Function)(loadClass("function", child));
try {
factory.addFunction(function);
} catch (IllegalArgumentException iae) {
throw new ParsingException("duplicate function", iae);
}
} else if (name.equals("abstractFunction")) {
// an abstract function is a class with an identifier
URI identifier = null;
try {
identifier = new URI(child.getAttributes().
getNamedItem("identifier").
getNodeValue());
} catch (URISyntaxException urise) {
throw new ParsingException("invalid function identifier",
urise);
}
FunctionProxy proxy =
(FunctionProxy)(loadClass("abstract function", child));
try {
factory.addAbstractFunction(proxy, identifier);
} catch (IllegalArgumentException iae) {
throw new ParsingException("duplicate abstract function",
iae);
}
} else if (name.equals("functionCluster")) {
// a cluster is a class that will give us a collection of
// functions that need to be added one by one into the factory
FunctionCluster cluster =
(FunctionCluster)(loadClass("function cluster", child));
Iterator<Function> it = cluster.getSupportedFunctions().iterator();
while (it.hasNext()) {
try {
factory.addFunction(it.next());
} catch (IllegalArgumentException iae) {
throw new ParsingException("duplicate function", iae);
}
}
}
}
}
/**
* Private helper that is used by all the code to load an instance of
* the given class...this assumes that the class is in the classpath,
* both for simplicity and for stronger security
*/
protected Object loadClass(String prefix, Node root)
throws ParsingException
{
// get the name of the class
String className =
root.getAttributes().getNamedItem("class").getNodeValue();
if (logger.isDebugEnabled()) {
logger.debug("Loading [ " + prefix + ": " + className + " ]");
}
// load the given class using the local classloader
Class<? extends Object> c = null;
try {
c = this.loader.loadClass(className);
} catch (ClassNotFoundException cnfe) {
throw new ParsingException("couldn't load class " + className,
cnfe);
}
Object instance = null;
// figure out if there are any parameters to the constructor
if (root.hasChildNodes()) {
// parse the arguments to the constructor
List<? extends Object> args = null;
try {
args = getArgs(root);
} catch (IllegalArgumentException iae) {
throw new ParsingException("illegal class arguments", iae);
}
int argLength = args.size();
// next we need to see if there's a constructor that matches the
// arguments provided...this has to be done by hand since
// Class.getConstructor(Class []) doesn't handle sub-classes and
// generic types (for instance, a constructor taking List won't
// match a parameter list containing ArrayList)
// get the list of all available constructors
Constructor<? extends Object> [] cons = c.getConstructors();
Constructor<? extends Object> constructor = null;
for (int i = 0; i < cons.length; i++) {
// get the parameters for this constructor
Class<? extends Object> [] params = cons[i].getParameterTypes();
if (params.length == argLength) {
Iterator<? extends Object> it = args.iterator();
int j = 0;
// loop through the parameters and see if each one is
// assignable from the coresponding input argument
while (it.hasNext()) {
if (! params[j].isAssignableFrom(it.next().getClass())) {
break;
}
j++;
}
// if we looked at all the parameters, then this
// constructor matches the input
if (j == argLength) {
constructor = cons[i];
}
}
// if we've found a matching constructor then stop looping
if (constructor != null) {
break;
}
}
// make sure we found a matching constructor
if (constructor == null) {
throw new ParsingException("couldn't find a matching " +
"constructor for " + c);
}
// finally, instantiate the class
try {
instance = constructor.newInstance(args.toArray());
} catch (InstantiationException ie) {
throw new ParsingException("couldn't instantiate " + className,
ie);
} catch (IllegalAccessException iae) {
throw new ParsingException("couldn't get access to instance " +
"of " + className, iae);
} catch (InvocationTargetException ite) {
throw new ParsingException("couldn't create " + className,
ite);
}
} else {
// we're using a null constructor, so this is easy
try {
instance = c.newInstance();
} catch (InstantiationException ie) {
throw new ParsingException("couldn't instantiate " + className
+ " with empty constructor", ie);
} catch (IllegalAccessException iae) {
throw new ParsingException("couldn't get access to instance " +
"of " + className, iae);
}
}
Method setConfigurationStore = null;
try {
setConfigurationStore = c.getMethod("setConfigurationStore", this.getClass());
setConfigurationStore.invoke(instance, this);
//logger.debug("set ConfigurationStore to " + c);
} catch ( NoSuchMethodException e) {
logger.debug(c + " does not have a \"setConfigurationStore\" method to retreive the current configuration");
} catch (Exception e) {
logger.debug("Error at setting the ConfigurationStore to " + c + ": " + e.getClass() + ": " + e.getMessage() + ")");
e.printStackTrace();
}
return instance;
}
/**
* Private helper that gets the constructor arguments for a given class.
* Right now this just supports String and List, but it's trivial to
* add support for other types should that be needed. Right now, it's not
* clear that there's any need for other types.
*/
private List<Object> getArgs(Node root) {
List<Object> args = new ArrayList<Object>();
NodeList children = root.getChildNodes();
for (int i = 0; i < children.getLength(); i++) {
Node child = children.item(i);
String name = child.getNodeName();
if (child.getNodeType() == Node.ELEMENT_NODE) {
if (name.equals("string")) {
args.add(child.getFirstChild().getNodeValue());
} else if (name.equals("list")) {
args.add(getArgs(child));
} else {
throw new IllegalArgumentException("unkown arg type: " +
name);
}
}
}
return args;
}
/**
* Private helper used by the three factory routines to see if the
* given factory should be based on the standard setup
*/
private boolean useStandard(Node node, String attributeName) {
NamedNodeMap map = node.getAttributes();
if (map == null) {
return true;
}
Node attrNode = map.getNamedItem(attributeName);
if (attrNode == null) {
return true;
}
return attrNode.getNodeValue().equals("true");
}
/**
* Returns the default PDP configuration. If no default was specified
* then this throws an exception.
*
* @return the default PDP configuration
*
* @throws UnknownIdentifierException if there is no default config
*/
public PDPConfig getDefaultPDPConfig() throws UnknownIdentifierException {
if (this.defaultPDPConfig == null) {
throw new UnknownIdentifierException("no default available");
}
return this.defaultPDPConfig;
}
/**
* Returns the PDP configuration with the given name. If no such
* configuration exists then an exception is thrown.
*
* @param name The name of the configuration.
*
* @return the matching PDP configuation
*
* @throws UnknownIdentifierException if the name is unknown
*/
public PDPConfig getPDPConfig(String name)
throws UnknownIdentifierException
{
Object object = this.pdpConfigMap.get(name);
if (object == null) {
throw new UnknownIdentifierException("unknown pdp: " + name);
}
return (PDPConfig)object;
}
/**
* Returns a set of identifiers representing each PDP configuration
* available.
*
* @return a <code>Set</code> of <code>String</code>s
*/
public Set<String> getSupportedPDPConfigurations() {
return Collections.unmodifiableSet(this.pdpConfigMap.keySet());
}
/**
* Returns the default attribute factory.
*
* @return The default attribute factory.
*/
public AttributeFactory getDefaultAttributeFactory() {
return this.defaultAttributeFactory;
}
/**
* Returns the attribute factory with the given name. If no such
* factory exists then an exception is thrown.
*
* @param name The name of the attribute factory.
*
* @return the matching attribute factory
*
* @throws UnknownIdentifierException if the name is unknown
*/
public AttributeFactory getAttributeFactory(String name)
throws UnknownIdentifierException
{
Object object = this.attributeMap.get(name);
if (object == null) {
throw new UnknownIdentifierException("unknown factory: " + name);
}
return (AttributeFactory)object;
}
/**
* Returns a set of identifiers representing each attribute factory
* available.
*
* @return a <code>Set</code> of <code>String</code>s
*/
public Set<String> getSupportedAttributeFactories() {
return Collections.unmodifiableSet(this.attributeMap.keySet());
}
/**
* Registers all the supported factories with the given identifiers. If
* a given identifier is already in use, then that factory is not
* registered. This method is provided only as a convenience, and
* any registration that may involve identifier clashes should be done
* by registering each factory individually.
*/
public void registerAttributeFactories() {
Iterator<String> it = this.attributeMap.keySet().iterator();
while (it.hasNext()) {
String id = it.next();
AttributeFactory af = this.attributeMap.get(id);
try {
AttributeFactory.registerFactory(id, new AFProxy(af));
} catch (IllegalArgumentException iae) {
logger.warn("Couldn't register AttributeFactory:"
+ id + " (already in use)", iae);
}
}
}
/**
* Returns the default combiningAlg factory.
*
* @return the default combiningAlg factory
*/
public CombiningAlgFactory getDefaultCombiningAlgFactory() {
return this.defaultCombiningFactory;
}
/**
* Returns the combiningAlg factory with the given name. If no such
* factory exists then an exception is thrown.
*
* @param name The name of the combining algorithm factory.
*
* @return the matching combiningAlg factory
*
* @throws UnknownIdentifierException if the name is unknown
*/
public CombiningAlgFactory getCombiningAlgFactory(String name)
throws UnknownIdentifierException
{
Object object = this.combiningMap.get(name);
if (object == null) {
throw new UnknownIdentifierException("unknown factory: " + name);
}
return (CombiningAlgFactory)object;
}
/**
* Returns a set of identifiers representing each combiningAlg factory
* available.
*
* @return a <code>Set</code> of <code>String</code>s
*/
public Set<String>getSupportedCombiningAlgFactories() {
return Collections.unmodifiableSet(this.combiningMap.keySet());
}
/**
* Registers all the supported factories with the given identifiers. If
* a given identifier is already in use, then that factory is not
* registered. This method is provided only as a convenience, and
* any registration that may involve identifier clashes should be done
* by registering each factory individually.
*/
public void registerCombiningAlgFactories() {
Iterator<String> it = this.combiningMap.keySet().iterator();
while (it.hasNext()) {
String id = it.next();
CombiningAlgFactory cf = this.combiningMap.get(id);
try {
CombiningAlgFactory.registerFactory(id, new CAFProxy(cf));
} catch (IllegalArgumentException iae) {
logger.warn("Couldn't register " +
"CombiningAlgFactory: " + id + " (already in use)",
iae);
}
}
}
/**
* Returns the default function factory proxy.
*
* @return the default function factory proxy
*/
public FunctionFactoryProxy getDefaultFunctionFactoryProxy() {
return this.defaultFunctionFactoryProxy;
}
/**
* Returns the function factory proxy with the given name. If no such
* proxy exists then an exception is thrown.
*
* @param name The name of the function factory proxy.
*
* @return the matching function factory proxy
*
* @throws UnknownIdentifierException if the name is unknown
*/
public FunctionFactoryProxy getFunctionFactoryProxy(String name)
throws UnknownIdentifierException
{
Object object = this.functionMap.get(name);
if (object == null) {
throw new UnknownIdentifierException("unknown factory: " + name);
}
return (FunctionFactoryProxy)object;
}
/**
* Returns a set of identifiers representing each function factory proxy
* available.
*
* @return a <code>Set</code> of <code>String</code>s
*/
public Set<String> getSupportedFunctionFactories() {
return Collections.unmodifiableSet(this.functionMap.keySet());
}
/**
* Registers all the supported factories with the given identifiers. If
* a given identifier is already in use, then that factory is not
* registered. This method is provided only as a convenience, and
* any registration that may involve identifier clashes should be done
* by registering each factory individually.
*/
public void registerFunctionFactories() {
Iterator<String> it = this.functionMap.keySet().iterator();
while (it.hasNext()) {
String id = it.next();
FunctionFactoryProxy ffp = this.functionMap.get(id);
try {
FunctionFactory.registerFactory(id, ffp);
} catch (IllegalArgumentException iae) {
logger.warn("Couldn't register FunctionFactory: "
+ id + " (already in use)", iae);
}
}
}
/**
* Uses the default configuration to re-set the default factories used
* by the system (attribute, combining algorithm, and function). If
* a default is not provided for a given factory, then that factory
* will not be set as the system's default.
*/
public void useDefaultFactories() {
logger.debug("Switching to default factories from configuration");
// set the default attribute factory, if it exists here
if (this.defaultAttributeFactory != null) {
AttributeFactory.setDefaultFactory(
new AFProxy(this.defaultAttributeFactory));
}
// set the default combining algorithm factory, if it exists here
if (this.defaultCombiningFactory != null) {
CombiningAlgFactory.setDefaultFactory(
new CAFProxy(this.defaultCombiningFactory));
}
// set the default function factories, if they exists here
if (this.defaultFunctionFactoryProxy != null) {
FunctionFactory.setDefaultFactory(
this.defaultFunctionFactoryProxy);
}
}
public String getBaseDir() {
return baseDir;
}
// public Map<String, String> getCustumAttrs() {
// return this.customAttr;
// }
public Object getCustomAttr(String key) {
return this.customAttr.get(key);
}
/**
*
*/
static class AFProxy implements AttributeFactoryProxy {
private AttributeFactory factory;
/**
* @param factory
*/
public AFProxy(AttributeFactory factory) {
this.factory = factory;
}
/**
* @see com.sun.xacml.attr.AttributeFactoryProxy#getFactory()
*/
public AttributeFactory getFactory() {
return this.factory;
}
}
/**
*
*/
static class CAFProxy implements CombiningAlgFactoryProxy {
private CombiningAlgFactory factory;
/**
* @param factory
*/
public CAFProxy(CombiningAlgFactory factory) {
this.factory = factory;
}
/**
* @see com.sun.xacml.combine.CombiningAlgFactoryProxy#getFactory()
*/
public CombiningAlgFactory getFactory() {
return this.factory;
}
}
}