001/** 002 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. 003 * 004 * Copyright (c) 2006 Sun Microsystems Inc. All Rights Reserved 005 * 006 * The contents of this file are subject to the terms 007 * of the Common Development and Distribution License 008 * (the License). You may not use this file except in 009 * compliance with the License. 010 * 011 * You can obtain a copy of the License at 012 * https://opensso.dev.java.net/public/CDDLv1.0.html or 013 * opensso/legal/CDDLv1.0.txt 014 * See the License for the specific language governing 015 * permission and limitations under the License. 016 * 017 * When distributing Covered Code, include this CDDL 018 * Header Notice in each file and include the License file 019 * at opensso/legal/CDDLv1.0.txt. 020 * If applicable, add the following below the CDDL Header, 021 * with the fields enclosed by brackets [] replaced by 022 * your own identifying information: 023 * "Portions Copyrighted [year] [name of copyright owner]" 024 * 025 * $Id: ResourceResult.java,v 1.5 2009/10/12 17:53:05 dillidorai Exp $ 026 * 027 */ 028 029/* 030 * Portions Copyrighted [2011] [ForgeRock AS] 031 */ 032package com.sun.identity.policy; 033 034import java.util.*; 035 036import org.w3c.dom.*; 037import com.sun.identity.shared.debug.Debug; 038import com.sun.identity.shared.xml.XMLUtils; 039import com.sun.identity.policy.interfaces.ResourceName; 040 041 042/** 043 * Class that encapsulates a tree of resource names, with each node 044 * having an associated policy decision. 045 * @supported.api 046 */ 047public class ResourceResult { 048 049 /** 050 * Constant to indicate subtree level scope for ResourceResult evaluation 051 * 052 * @supported.api 053 */ 054 public static final String SUBTREE_SCOPE = "subtree"; 055 056 /** 057 * Constant to indicate strict subtree level scope for 058 * <code>ResourceResult</code> evaluation 059 * 060 * @supported.api 061 */ 062 public static final String STRICT_SUBTREE_SCOPE = "strict-subtree"; 063 064 /** 065 * Constant to indicate base (self) level scope for 066 * <code>ResourceResult</code> evaluation 067 * 068 * @supported.api 069 */ 070 public static final String SELF_SCOPE = "self"; 071 072 /** 073 * Constant used internally as a place holder for all encompassing root 074 * resoure name. Any resource name is considered to be sub resource of 075 * this resource name. 076 */ 077 static final public String VIRTUAL_ROOT = "-__viRTuAl-rOot--_"; 078 079 static final String RESOURCE_RESULT = "ResourceResult"; 080 static final String RESOURCE_NAME = "name"; 081 static final String POLICY_DEBUG_NAME = "amPolicy"; 082 static final Debug DEBUG = Debug.getInstance(POLICY_DEBUG_NAME); 083 084 private String resourceName = null; 085 private PolicyDecision policyDecision = null; 086 private Set resourceResults = new HashSet(); 087 private long timeToLive = Long.MAX_VALUE; 088 private boolean advicesAreSet = false; 089 private String stringForm = null; 090 private String xmlForm = null; 091 092 //tracks the envMap used to compute ResourceResult 093 private Map envMap = null; 094 095 /** 096 * Used in remote api for result caching 097 */ 098 private boolean stale = false; 099 100 /** 101 * 102 * No argument constructor 103 */ 104 ResourceResult() { 105 } 106 107 /** 108 * Constructs a resource result given the resource name and policy decison 109 * @param resourceName resource name for this resource result 110 * @param policyDecision policy decision associated with the resource name 111 */ 112 public ResourceResult(String resourceName, PolicyDecision 113 policyDecision ) { 114 this.resourceName = resourceName; 115 setPolicyDecision(policyDecision); 116 } 117 118 /** 119 * Returns the resource name of this resource result 120 * @return resource name of this resource result 121 * @supported.api 122 */ 123 public String getResourceName() { 124 return resourceName; 125 } 126 127 128 /** 129 * Sets the resource name of this resource result 130 * @param resourceName resource name for this resource result 131 */ 132 void setResourceName(String resourceName) { 133 this.resourceName = resourceName; 134 this.stringForm = null; 135 this.xmlForm= null; 136 } 137 138 139 /** 140 * Returns the policy decision associated with this resource result 141 * @return policy decision associated with this resource result 142 * @supported.api 143 */ 144 public PolicyDecision getPolicyDecision() { 145 return policyDecision; 146 } 147 148 149 /** 150 * Sets the policy decision for this resource result 151 * @param policyDecision policy decision for this resource result 152 */ 153 public void setPolicyDecision(PolicyDecision policyDecision) { 154 this.policyDecision = policyDecision; 155 long pdTtl = policyDecision.getTimeToLive(); 156 if ( pdTtl < timeToLive ) { 157 timeToLive = pdTtl; 158 } 159 advicesAreSet = advicesAreSet || policyDecision.hasAdvices(); 160 this.stringForm = null; 161 this.xmlForm= null; 162 } 163 164 165 /** 166 * Returns the child resource results of this resource result 167 * @return child resource results of this resource result 168 * @supported.api 169 */ 170 public Set getResourceResults() { 171 return resourceResults; 172 } 173 174 175 /** 176 * Sets the child resource results of this resource result 177 * @param resourceResults child resource results of this resource result 178 */ 179 void setResourceResults(Set resourceResults) { 180 if (resourceResults == null) { 181 this.resourceResults.clear(); 182 } else { 183 this.resourceResults = resourceResults; 184 if (policyDecision != null) { 185 timeToLive = policyDecision.getTimeToLive(); 186 } 187 Iterator iter = resourceResults.iterator(); 188 while (iter.hasNext()) { 189 ResourceResult rr = (ResourceResult)iter.next(); 190 long ttl = rr.getTimeToLive(); 191 if ( ttl < timeToLive) { 192 timeToLive = ttl; 193 } 194 advicesAreSet = advicesAreSet || rr.hasAdvices(); 195 } 196 } 197 198 this.stringForm = null; 199 this.xmlForm= null; 200 } 201 202 /** 203 * Converts an XML representation of resource result to ResourceResult 204 * @param resourceResultNode XML DOM node representing resource result 205 * @return <code>ResourceResult</code> object representation of resource 206 * result 207 * @throws PolicyException if the conversion fails 208 */ 209 public static ResourceResult parseResourceResult(Node resourceResultNode) 210 throws PolicyException { 211 ResourceResult resourceResult = new ResourceResult(); 212 String resourceName = XMLUtils.getNodeAttributeValue(resourceResultNode, 213 RESOURCE_NAME); 214 if (resourceName == null) { 215 DEBUG.error("ResourceResult: missing attribute " + RESOURCE_NAME); 216 Object[] objs = {RESOURCE_NAME}; 217 throw new PolicyException(ResBundleUtils.rbName, 218 "missing_attribute_in_resourceresult", objs, null); 219 } 220 resourceResult.setResourceName(resourceName); 221 222 Node node = XMLUtils.getChildNode(resourceResultNode, 223 PolicyDecision.POLICY_DECISION); 224 if (node == null) { 225 DEBUG.error("ResourceResult: missing element " + 226 PolicyDecision.POLICY_DECISION); 227 Object[] objs = {PolicyDecision.POLICY_DECISION}; 228 throw new PolicyException(ResBundleUtils.rbName, 229 "missing_attribute_in_resourceresult", objs, null); 230 } else { 231 resourceResult.setPolicyDecision( 232 PolicyDecision.parsePolicyDecision(node)); 233 } 234 235 Set nodeSet = XMLUtils.getChildNodes( 236 resourceResultNode, RESOURCE_RESULT); 237 if (nodeSet != null) { 238 Iterator nodes = nodeSet.iterator(); 239 while (nodes.hasNext()) { 240 node = (Node)nodes.next(); 241 ResourceResult rRes = ResourceResult.parseResourceResult(node); 242 resourceResult.resourceResults.add(rRes); 243 } 244 } 245 246 return resourceResult; 247 } 248 249 250 /** 251 * Returns a string representation of this resource result 252 * @return a string representation of this resource result 253 * @supported.api 254 */ 255 public String toString() { 256 if (stringForm == null) { 257 StringBuilder sb = new StringBuilder(200); 258 sb.append("Resource Result for resourceName : ") 259 .append(resourceName) 260 .append(PolicyUtils.CRLF) 261 .append("PolicyDecision : ") 262 .append(policyDecision) 263 .append("Nested ResourceResults : ") 264 .append(resourceResults); 265 stringForm = sb.toString(); 266 } 267 return stringForm; 268 } 269 270 /** 271 * Returns an XML representation of this resource result 272 * @return an XML representation of this resource result 273 * @supported.api 274 */ 275 public String toXML() { 276 if (xmlForm == null) { 277 StringBuilder xmlsb = new StringBuilder(1000); 278 279 xmlsb.append("<") 280 .append(RESOURCE_RESULT) 281 .append(" ").append(RESOURCE_NAME) 282 .append("=\"") 283 .append(XMLUtils.escapeSpecialCharacters(resourceName)) 284 /* 285 .append("\" ").append("timeToLive") 286 .append("=\"").append(timeToLive).append("\"") 287 .append( " ").append("hasAdvices") 288 .append("=\"").append(hasAdvices()) 289 */ 290 .append("\">") 291 .append(PolicyUtils.CRLF); 292 if (policyDecision != null) { 293 xmlsb.append(policyDecision.toXML()); 294 } 295 296 Iterator rrIter = resourceResults.iterator(); 297 while ( rrIter.hasNext() ) { 298 ResourceResult rr = (ResourceResult) rrIter.next(); 299 xmlsb.append(rr.toXML()); 300 } 301 302 xmlsb.append("</") 303 .append(RESOURCE_RESULT) 304 .append( ">") 305 .append(PolicyUtils.CRLF); 306 307 xmlForm = xmlsb.toString(); 308 } 309 return xmlForm; 310 } 311 312 /** 313 * Adds a resource result to the resource result sub tree rooted at 314 * this ResourceResult 315 * @param resourceResult resource result to be added 316 * @param serviceType service type of the resource result being added 317 * @throws PolicyException if the resourceResult could not be added 318 */ 319 public void addResourceResult( ResourceResult resourceResult, 320 ServiceType serviceType) throws PolicyException { 321 addResourceResult(resourceResult, 322 serviceType.getResourceNameComparator()); 323 } 324 325 /** 326 * Adds a resource result to the resource result sub tree rooted at 327 * this ResourceResult 328 * @param resourceResult resource result to be added 329 * @param resourceComparator resource name comparator 330 * @throws PolicyException if the resourceResult could not be added 331 */ 332 public void addResourceResult( ResourceResult resourceResult, 333 ResourceName resourceComparator) throws PolicyException { 334 if (!this.isSuperResourceResultOf(resourceResult, 335 resourceComparator)) { 336 String[] objs = {this.resourceName, resourceResult.resourceName}; 337 throw new PolicyException(ResBundleUtils.rbName, 338 "invalid_sub_resourceresult", objs, null); 339 } else { 340 Iterator resourceResultIter = resourceResults.iterator(); 341 boolean directChild = true; 342 while (resourceResultIter.hasNext()) { 343 ResourceResult rResult = 344 (ResourceResult) resourceResultIter.next(); 345 if (rResult.isSuperResourceResultOf(resourceResult, 346 resourceComparator)) { 347 rResult.addResourceResult(resourceResult, 348 resourceComparator); 349 directChild = false; 350 break; 351 } 352 } 353 if (directChild) { 354 Set childrenToBeMoved = new HashSet(); 355 Iterator rrIter = resourceResults.iterator(); 356 while (rrIter.hasNext()) { 357 ResourceResult rResult = 358 (ResourceResult) rrIter.next(); 359 if (resourceResult.isSuperResourceResultOf(rResult, 360 resourceComparator)) { 361 childrenToBeMoved.add(rResult); 362 } 363 } 364 resourceResults.removeAll(childrenToBeMoved); 365 resourceResult.resourceResults.addAll(childrenToBeMoved); 366 resourceResults.add(resourceResult); 367 } 368 } 369 long rrTtl = resourceResult.getTimeToLive(); 370 if ( rrTtl < timeToLive ) { 371 timeToLive = rrTtl; 372 } 373 advicesAreSet = advicesAreSet || resourceResult.hasAdvices(); 374 this.stringForm = null; 375 this.xmlForm= null; 376 } 377 378 /** 379 * Marks result as stale 380 */ 381 public void markStale() { 382 stale = true; 383 } 384 385 /** 386 * Determines if result is stale 387 * 388 * @return true if result is stale 389 */ 390 public boolean isStale() { 391 return stale; 392 } 393 394 /** 395 * Checks if this resource result is a super resource result of 396 * the argument resource result 397 * @param resourceResult resource result for which we want to check 398 * whether this resource result is a super resource result 399 * @param resourceComparator - resource comparator 400 * @return <code>true</code> if this resource result is a super 401 * resource result of resourceResult, else returns 402 * <code>false</code> 403 * @throws PolicyException if there is any error while comparing the 404 * resourceResult 405 */ 406 private boolean isSuperResourceResultOf(ResourceResult resourceResult, 407 ResourceName resourceComparator) throws PolicyException { 408 boolean isSuperResource = false; 409 if (VIRTUAL_ROOT.equals(resourceName)) { 410 isSuperResource = true; 411 } else if (resourceComparator != null) { 412 boolean interpretWildCard = false; 413 ResourceMatch resourceMatch = 414 resourceComparator.compare(resourceName, 415 resourceResult.resourceName, interpretWildCard); 416 if (resourceMatch.equals(ResourceMatch.SUB_RESOURCE_MATCH)) { 417 isSuperResource = true; 418 } 419 // Results will contain both incoming URL as well as matched policy URL with decision. 420 // The problem is we don't know which order results will come in. 421 // Incoming URL will be missing policyDecision so we need to check resourceResult contains '*' 422 // and if it does, check if resourceResult is parent of incoming URL and use parent's 423 // policyDecision for this object. 424 else if (resourceResult.resourceName.indexOf('*') != -1 ) { 425 String resResultResName = resourceResult.resourceName; 426 String substrResultResName = resResultResName.substring(0, resResultResName.indexOf('*')); 427 if (resourceName.startsWith(substrResultResName)) { 428 //check if policyDecision is null 429 //if null, then copy policyDecision from parents 430 if (policyDecision==null || policyDecision.getActionDecisions().isEmpty()) { 431 policyDecision = resourceResult.policyDecision; 432 } 433 } 434 } 435 } else { 436 isSuperResource = 437 resourceResult.resourceName.startsWith(resourceName); 438 } 439 return isSuperResource; 440 } 441 442 /** 443 * Returns the GMT time in milliseconds since epoch when this object is to 444 * be treated as expired. That is the resource result would 445 * likely be different after that time. 446 * This is computed as a result of time conditions specified in the Policy 447 * definitions. 448 * 449 * @return time to live 450 */ 451 public long getTimeToLive() { 452 return timeToLive; 453 } 454 455 /** 456 * Checks wether advices are set in this object 457 * @return <code>true</code>, if advices are set, else <code>false</code> 458 */ 459 public boolean hasAdvices() { 460 return advicesAreSet; 461 } 462 463 /** 464 * Sets the environment map that was used while computing the 465 * resource result 466 * @param envMap the environment map that was used while computing the 467 * resource result 468 */ 469 void setEnvMap(Map envMap) { 470 this.envMap = envMap; 471 472 } 473 474 /** 475 * Returns the environment map that was used while computing the 476 * resource result 477 * @param the environment map that was used while computing the 478 * resource result 479 */ 480 Map getEnvMap() { 481 return envMap; 482 } 483 484} 485