package com.sun.xacml.debug;
import org.apache.log4j.Logger;
import org.w3c.dom.Node;
import com.sun.xacml.Constants;
/**
* This class has two purposes:
* <ul>
* <li>Manage the information captured at PDP startup, i.e., the parsing
* information (for now: fileName and lineNumber; may change with
* new/other PolicyLoaders)</li>
* <li>Manage the call stack information at Runtime</li>
* </ul>
*
*
* At runtime, this class is responsible for managing the Runtime Information
* within the XACML Engine. This class itself is <b>NOT</b> able to capture all
* required information at runtime. Instead, it has to be called from the outside and
* provided with the required information.
* <br/>
* This may be implemented by external code, e.g., by using aspectJ, capturing
* all relevant events and provide the according information to
* <ul>
* <li>setCalledFrom(Object calledFrom), when an object is called, whereas
* calledFrom is the calling (XACML) object</li>
* <li>unsetCalledFrom(Object calledFrom), when a call returns, i.e., the
* evaluation is finished. This function is mainly to detect bugs in
* the implementation and may be removed in productive environments</li>
* </ul>
* <br/>
* To capture all required events, the following functions have to be monitored:
* <ul>
* <li>PolicyTreeElement.evaluate(EvaluationCtx), which includes
* Policy, PolicySet, PolicyReference, Rule </li>
* <li>Evaluatable.evaluate(EvaluationCtx), which includes AttributeDesignator,
* AttributeSelector, AttributeValue, VariableReference, Condition, Apply</li>
* <li>MatchElement.match(EvaluationCtx ), which includes Target, TargetMatch,
* TargetMatchGroup, TargetSection</li>
* <li>Function.evaluate(List<Expression>, EvaluationCtx) which includes all
* Functions</li>
* <li>CombiningAlgorithm.combine(EvaluationCtx, List<CombinerParameter>, List<CombinerElement>)
* which includes all Combing Algorithms</li>
* </ul>
*
*
*
*/
public class RuntimeInfo {
/**
* line number, -1 if undefined
*/
private int lineNumber = -1;
/**
* File name, null if undefined
*/
private String fileName = null;
/**
* Defines the element type this RuntimeInfo object is assigned to
*/
private ELEMENT_TYPE eleType;
private boolean indirectLocateable = false;
/**
* needed to create the call stack
*/
private RuntimeInfo calledFrom;
/**
*
*/
private Locatable xacmlObject;
private static Logger logger = Logger.getLogger(RuntimeInfo.class);
public enum SOURCE_TYPE {
FILE
}
public enum ELEMENT_TYPE {
POLICY_SET ("Policy Set"), //Locatable
POLICY ("Policy"), //Locatable
POLICY_REFERENCE ("Policy Reference"), //Locatable
TARGET ("Target"), //Locatable
TARGET_SECTION ("Target Section"), //Locatable
TARGET_MATCH_GROUP ("Target Match Group"), //Locatable
TARGET_MATCH ("Target Match"), //Locatable
RULE ("Rule"), //Locatable
ATTRIBUTE_VALUE ("Attribute Value"), //Locatable
ATTRIBUTE_DESIGNATOR ("Attribute Designator"), //Locatable
ATTRIBUTE_SELECTOR ("Attribtue Selector"), //Locatable
VARIABLE_REFERENCE ("Variable Reference"), //Locatable
VARIABLE_DEFINITION ("Variable Definition"), //Locatable
CONDITION ("Condition"), //Locatable
APPLY ("Apply"), //Locatable
FUNCTION ("Function"), //IndirectLocatable
COMBINING_ALG ("Combining Algorithm"), //IndirectLocatable
OBLIGATION ("Obligation"), //Locatable
COMBINER_PARAMETER ("Combiner Parameter"); //Locatable
//COMBINER_ELEMENT ("Cimbiner Element"); //Locatable
private String name;
ELEMENT_TYPE(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
protected RuntimeInfo(Locatable xacmlElement, Node node, ELEMENT_TYPE type) {
this.xacmlObject = xacmlElement;
Object tmp = node.getUserData(Constants.LINE_NUMBER);
if (tmp instanceof Integer ) {
lineNumber = ((Integer) tmp).intValue();
}
tmp = node.getUserData(Constants.SOURCE_FILE);
if ( tmp instanceof String ) {
fileName = (String) tmp;
}
this.eleType = type;
}
/**
* returns the line number of the object within the
* XACML source file
* @return
*/
public int getLineNumber() {
return this.lineNumber;
}
/**
* returns the source file name
* @return
*/
public String getFileName() {
return this.fileName;
}
public ELEMENT_TYPE getElementType() {
return this.eleType;
}
/**
* create a new Runtime Information object or null, if no source locations are available within
* the node
* @param node
* @return
*/
public static RuntimeInfo getRuntimeInfo(Node node, ELEMENT_TYPE type ) {
if ( node.getUserData(Constants.LINE_NUMBER) != null ||
node.getUserData(Constants.SOURCE_FILE) != null ) {
return new RuntimeInfo(null, node, type);
} else {
return null;
}
}
/**
* create a new Runtime Information object or null, if no source locations are available within
* the node
* @param node
* @return
*/
public static RuntimeInfo getRuntimeInfo(Locatable xacmlObject, Node node, ELEMENT_TYPE type ) {
if ( node.getUserData(Constants.LINE_NUMBER) != null ||
node.getUserData(Constants.SOURCE_FILE) != null ) {
return new RuntimeInfo(xacmlObject, node, type);
} else {
return null;
}
}
//public void setXACMLObject(Object xacmlObject) {
public void setXACMLObject(Locatable xacmlObject) {
this.xacmlObject = xacmlObject;
}
public Object getXACMLObject() {
return xacmlObject;
}
private RuntimeInfo() {
//empty constructor
}
/**
* This function provides a new SourceLocator object which is used to set and unset
* the SourceLocator objects of IndirectLoatable elements, (i.e., used as parameter
* for setSourceLocator and unsetSourceLocator methods).
* <br/>
* Note: it is mainly a copy constructor which sets the new element type, thus, allow
* to differentiate
*
* @param contextSrcLocator
* @return
*/
public RuntimeInfo getIndirectRuntimeInfo(IndirectLocatable xacmlObject, ELEMENT_TYPE type) {
RuntimeInfo src = new RuntimeInfo();
src.fileName = this.fileName;
src.lineNumber = this.lineNumber;
src.indirectLocateable = true;
src.calledFrom = this;
src.xacmlObject = xacmlObject;
src.eleType = type;
return src;
}
/**
* This function provides a new SourceLocator object with the same location
* information as it is based on.
* <br/> This function should be used at parsing time and not at runtime,
* as the calledFrom information is not set (this is done at runtime).
* Currently, the only case where it is used is for XACML1.0 when an apply
* element is created to wrap a function defined by the FunctionId of the
* Condition element.
* @param xacmlObject
* @param type
* @return
*/
public RuntimeInfo getSourceLocator(Locatable xacmlObject, ELEMENT_TYPE type) {
RuntimeInfo src = new RuntimeInfo();
src.fileName = this.fileName;
src.lineNumber = this.lineNumber;
src.xacmlObject = xacmlObject;
src.eleType = type;
return src;
}
public boolean isIndirectLocateable() {
return indirectLocateable;
}
public String getLocationMsgForError() {
return " near line " + ( lineNumber == -1 ? "unkown" : lineNumber )
+ " from file " + ( fileName == null ? "unkown" : fileName );
//+ ( eleType == null ? "" : " (" + eleType.getName() + ")");
}
public String getLocationMsgShort() {
return " (line " + ( lineNumber == -1 ? "unkown" : lineNumber )
+ "@" + ( fileName == null ? "unkown" : fileName ) + ")";
//+ ( eleType == null ? "" : " (" + eleType.getName() + ")");
// + (this.calledFrom != null ? " from " + calledFrom.getClass().getSimpleName() : "")
}
public String getLocationMsgRuntime() {
if ( calledFrom != null && calledFrom.xacmlObject != null ) {
return getLocationMsgShort()
+ " called from " + calledFrom.xacmlObject.getClass().getSimpleName()
+ ( calledFrom instanceof Locatable && ((Locatable) calledFrom).getRuntimeInfo() != null ?
((Locatable) calledFrom).getRuntimeInfo().getLocationMsgShort() : "" ) ;
} else {
return getLocationMsgShort();
}
}
public String getElementDescr() {
return xacmlObject.getClass().getSimpleName() + getLocationMsgForError();
}
public void setCalledFrom(RuntimeInfo calledFrom) {
if ( indirectLocateable ) {
if ( calledFrom != this.calledFrom ) {
logger.warn("For indirect locateable RuntimeInfo objects, calledFrom provided to this" +
"function should be the same als already stored");
}
} else if ( this.calledFrom != null ) {
logger.warn("Overwriting calledFrom object: " + this.calledFrom.getElementDescr() + " overwritten with " + calledFrom.getElementDescr());
if (logger.isDebugEnabled()) {
try {
throw new RuntimeException("getStackTrace for Overwriting calledFrom object");
} catch(RuntimeException e) {
e.printStackTrace();
}
}
}
this.calledFrom = calledFrom;
}
public void unsetCalledFrom(RuntimeInfo calledFrom) {
if ( this.calledFrom == null ) {
logger.warn("calledFrom is already unset!");
} else if ( this.calledFrom != calledFrom) {
logger.warn("Unsetting different object (" + calledFrom.getClass().getSimpleName()
+ ") than was set (" + this.calledFrom.getClass().getSimpleName() + ")!");
}
this.calledFrom = null;
}
public RuntimeInfo getCalledFrom() {
return calledFrom;
}
}