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