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: PolicyEvaluator.java,v 1.7 2009/10/21 23:50:46 dillidorai Exp $
026 *
027 * Portions Copyrighted 2013-2015 ForgeRock AS.
028 */
029package com.sun.identity.policy.client;
030
031import com.sun.identity.shared.debug.Debug;
032import com.iplanet.dpro.session.SessionException;
033import com.iplanet.sso.SSOException;
034import com.iplanet.sso.SSOToken;
035import com.iplanet.sso.SSOTokenManager; 
036import com.sun.identity.policy.ActionDecision;
037import com.sun.identity.policy.PolicyDecision;
038import com.sun.identity.policy.ResBundleUtils;
039import com.sun.identity.policy.PolicyException;
040import com.sun.identity.policy.PolicyUtils;
041import com.sun.identity.policy.remote.PolicyEvaluationException;
042import com.sun.identity.security.AdminTokenAction;
043import com.sun.identity.security.AppSSOTokenProvider;
044import com.sun.identity.log.Logger;
045import com.sun.identity.log.LogRecord;
046import com.sun.identity.policy.interfaces.ResourceName;
047import org.forgerock.util.thread.listener.ShutdownListener;
048import org.forgerock.util.thread.listener.ShutdownManager;
049
050import java.util.HashSet;
051import java.util.Iterator;
052import java.util.Map;
053import java.util.Set;
054import java.util.logging.Level;
055import java.security.AccessController;
056
057/**
058 * This class provides methods to get policy decisions 
059 * for clients of policy service.
060 * This class uses XML/HTTP protocol to 
061 * communicate with the Policy Service.
062 * Policy client API implementation caches policy decision locally.
063 * The cache is updated through policy change notifications and/or
064 * polling.
065 *
066 * @supported.api
067 */
068public class PolicyEvaluator {
069
070    static Debug debug = Debug.getInstance("amRemotePolicy");
071    private PolicyProperties policyProperties;
072    private String serviceName;
073    private SSOTokenManager ssoTokenManager;
074
075    /**
076     * Reference to singleton ResourceResultCache instance
077     */
078    private ResourceResultCache resourceResultCache;
079
080    AppSSOTokenProvider appSSOTokenProvider;
081
082    /**
083     * Logger object for access messages
084     */
085    static Logger accessLogger;
086
087    /**
088     * Logger object for error messages
089     */
090    static Logger errorLogger;
091
092    private static final String GET_RESPONSE_ATTRIBUTES 
093            = "Get_Response_Attributes";
094
095    private SSOToken appSSOToken;
096
097    /*
098     * Number of attempts to make to server if policy decision received
099     * from server has expired ttl
100     */
101    private final static int RETRY_COUNT = 3;
102
103    private String logActions;
104
105    /**
106     * Creates an instance of client policy evaluator 
107     *
108     * @param serviceName name of the service for which to create 
109     *        policy evaluator.
110     * @param appSSOTokenProvider an object where application single sign on
111     *        token can be obtained.
112     * @throws PolicyException if required properties cannot be retrieved.
113     * @throws SSOException if application single sign on token is invalid.
114     */
115    private PolicyEvaluator(String serviceName, 
116            AppSSOTokenProvider appSSOTokenProvider)
117            throws PolicyException, SSOException {
118        if (debug.messageEnabled()) {
119            debug.message("PolicyEvaluator():Creating PolicyEvaluator:" 
120                    + "serviceName="+ serviceName 
121                    + ":appSSOTokenProvider=" + appSSOTokenProvider);
122        }
123        if (serviceName == null) {
124            if (debug.warningEnabled()) {
125                debug.warning("PolicyEvaluator():"
126                        + "serviceName is null");
127            }
128            return;
129        } //else do the following
130
131        init(serviceName, appSSOTokenProvider);
132    }
133    
134    /**
135     * Returns an instance of client policy evaluator 
136     *
137     * @param serviceName name of the service for which to create 
138     *        policy evaluator.
139     * @param appSSOTokenProvider an object where application single sign on
140     *        token can be obtained.
141     * @throws PolicyException if required properties cannot be retrieved.
142     * @throws SSOException if application single sign on token is invalid.
143     */
144    static PolicyEvaluator getInstance(String serviceName, 
145            AppSSOTokenProvider appSSOTokenProvider)
146        throws PolicyException, SSOException {
147        return new PolicyEvaluator(serviceName,appSSOTokenProvider);
148    }
149
150    /**
151     * Initializes an instance of client policy evaluator object
152     *
153     * @param serviceName name of the service for which to create 
154     *        policy evaluator
155     * @param appSSOTokenProvider an object where application single sign on
156     *        token can be obtained.
157     *
158     * @throws PolicyException if required properties cannot be retrieved.
159     * @throws SSOException if application single sign on token is invalid.
160     *
161     */
162    private void init(final String serviceName,
163                      AppSSOTokenProvider appSSOTokenProvider)
164            throws PolicyException, SSOException {
165        this.ssoTokenManager = SSOTokenManager.getInstance();
166        this.serviceName = serviceName;
167        this.appSSOTokenProvider = appSSOTokenProvider;
168        this.policyProperties = new PolicyProperties();
169        this.logActions = policyProperties.getLogActions();
170        this.resourceResultCache 
171                = ResourceResultCache.getInstance(policyProperties);
172        appSSOToken = getNewAppSSOToken();
173
174        if (PolicyProperties.previouslyNotificationEnabled()) {
175            if (policyProperties.useRESTProtocol()) {
176                resourceResultCache.removeRESTRemotePolicyListener(appSSOToken,
177                        serviceName, PolicyProperties.getPreviousNotificationURL());
178            } else {
179                resourceResultCache.removeRemotePolicyListener(appSSOToken,
180                        serviceName, PolicyProperties.getPreviousNotificationURL());
181            }
182        }
183
184        if (policyProperties.notificationEnabled()) {
185
186            // register remote policy listener policy service
187            if (debug.messageEnabled()) {
188                debug.message( "PolicyEvaluator.init():"
189                        + "adding remote policy listener with policy "
190                        + "service " + serviceName);
191
192            }
193            if (policyProperties.useRESTProtocol()) {
194                resourceResultCache.addRESTRemotePolicyListener(appSSOToken,
195                        serviceName, policyProperties.getRESTNotificationURL());
196            } else {
197                resourceResultCache.addRemotePolicyListener(appSSOToken,
198                        serviceName, policyProperties.getNotificationURL());
199            }
200            // Add a hook to remove our listener on shutdown.
201            ShutdownManager shutdownMan = com.sun.identity.common.ShutdownManager.getInstance();
202
203            shutdownMan.addShutdownListener(new ShutdownListener() {
204                @Override
205                public void shutdown() {
206                    if (policyProperties.useRESTProtocol()) {
207                        resourceResultCache.removeRESTRemotePolicyListener(appSSOToken,
208                                serviceName, policyProperties.getRESTNotificationURL());
209                        if (debug.messageEnabled()) {
210                            debug.message("PolicyEvaluator: called removeRESTRemotePolicyListener, service "
211                                    + serviceName + ", URL " + policyProperties.getRESTNotificationURL());
212                        }
213                    } else {
214                        resourceResultCache.removeRemotePolicyListener(appSSOToken,
215                                serviceName, policyProperties.getNotificationURL());
216                        if (debug.messageEnabled()) {
217                            debug.message("PolicyEvaluator: called removeRemotePolicyListener, service "
218                                    + serviceName + ", URL " + policyProperties.getNotificationURL());
219                        }
220                    }
221                }
222            });
223
224        }
225
226        ActionDecision.setClientClockSkew(policyProperties.getClientClockSkew());
227
228        if (debug.messageEnabled()) {
229            debug.message("PolicyEvaluator:"
230                    + "initialized PolicyEvaluator");
231        }
232    }
233
234    /**
235     * Evaluates a simple privilege of boolean type. The privilege indicates
236     * if the user can perform specified action on the specified resource.
237     *
238     * @param token single sign on token of the user evaluating policies
239     * @param resourceName name of the resource the user is trying to access
240     * @param actionName name of the action the user is trying to perform on
241     * the resource
242     *
243     * @return the result of the evaluation as a boolean value
244     * @throws PolicyException if result could not be computed for any
245     *         reason other than single sign on token problem.
246     * @throws SSOException if single sign on token is not valid 
247     *
248     */
249    public boolean isAllowed(SSOToken token, String resourceName,
250                             String actionName) throws PolicyException,
251                             SSOException {
252        return isAllowed(token, resourceName, actionName, null); //null env Map
253    }
254
255    /**
256     * Evaluates simple privileges of boolean type. The privilege indicates
257     * if the user can perform specified action on the specified resource.
258     * The evaluation also depends on user's application environment parameters.
259     *
260     * @param token single sign on token of the user evaluating policies.
261     * @param resourceName name of the resource the user is trying to access
262     * @param actionName name of the action the user is trying to perform on
263     * the resource
264     * @param envParameters run time environment parameters
265     *
266     * @return the result of the evaluation as a boolean value
267     *
268     * @throws PolicyException if result could not be computed for
269     *         reason other than single sign on token problem.
270     * @throws SSOException if single sign on token is not valid
271     *
272     * @supported.api
273     */
274    public boolean isAllowed(SSOToken token, String resourceName,
275                             String actionName,
276                             Map envParameters) throws PolicyException,
277                             SSOException {
278        if (debug.messageEnabled()) {
279            debug.message("PolicyEvaluator:isAllowed():"
280                    + "token=" + token.getPrincipal().getName() 
281                    + ":resourceName="+ resourceName 
282                    + ":actionName=" + actionName 
283                    + ":envParameters) : entering");
284        }
285
286        boolean actionAllowed = false;
287        Set actionNames = new HashSet(1);
288        actionNames.add(actionName);
289        PolicyDecision policyDecision = getPolicyDecision(token, resourceName,
290                                   actionNames, envParameters);
291        ActionDecision actionDecision = 
292                (ActionDecision) policyDecision.getActionDecisions()
293                .get(actionName);
294        String  trueValue = policyProperties.getTrueValue(serviceName,
295                actionName);
296        String  falseValue = policyProperties.getFalseValue(serviceName,
297                actionName);
298
299        if ( (actionDecision != null) && (trueValue != null) 
300                    && (falseValue != null)  ) {
301            Set set = (Set) actionDecision.getValues();
302            if ( (set != null) ) {
303                if ( set.contains(falseValue) ) {
304                    actionAllowed = false;
305                } else if ( set.contains(trueValue) ) {
306                    actionAllowed = true;
307                }
308            }
309        }
310
311        String result = actionAllowed ? "ALLOW" : "DENY";
312        String[] objs = {resourceName, actionName, result};
313        if (PolicyProperties.ALLOW.equals(logActions) && actionAllowed) {
314            logAccessMessage(Level.INFO,
315                             ResBundleUtils.getString(
316                             "policy_eval_allow", objs),token);
317        } else if (PolicyProperties.DENY.equals(logActions) && !actionAllowed) {
318            logAccessMessage(Level.INFO,
319                             ResBundleUtils.getString(
320                             "policy_eval_deny", objs),token);
321        } else if (PolicyProperties.BOTH.equals(logActions)
322                || PolicyProperties.DECISION.equals(logActions)) {
323            logAccessMessage(Level.INFO,
324                             ResBundleUtils.getString(
325                             "policy_eval_result", objs),token);
326        } //else nothing to log
327
328        if (debug.messageEnabled()) {
329            debug.message("PolicyEvaluator.isAllowed():"
330                    + "token=" + token.getPrincipal().getName() 
331                    + ":resourceName=" + resourceName 
332                    + ":actionName=" + actionName 
333                    + ":returning: " + actionAllowed);
334        }
335        return actionAllowed;
336    }
337
338    /**
339     * Evaluates privileges of the user to perform the specified actions
340     * on the specified resource. 
341     *
342     * @param token single sign on token of the user evaluating policies.
343     * @param resourceName name of the resource the user is trying to access.
344     * @param actionNames Set of action names the user is trying to perform on
345     * the resource.
346     *
347     * @return policy decision
348     * @throws PolicyException if result could not be computed for any
349     *         reason other than single sign on token problem.
350     * @throws SSOException if single sign on token is not valid
351     */
352    public PolicyDecision getPolicyDecision(SSOToken token,
353                                            String resourceName,
354                                            Set actionNames)
355        throws PolicyException, SSOException {
356        return getPolicyDecision(token, resourceName, actionNames, null);
357    }
358
359    /**
360     * Evaluates privileges of the user to perform the specified actions
361     * on the specified resource. The evaluation also depends on user's
362     * run time environment parameters.
363     *
364     * @param token single sign on token of the user evaluating policies.
365     * @param resourceName name of the resource the user is trying to access
366     * @param actionNames Set of action names the user is trying to perform on
367     *        the resource.
368     * @param envParameters run-time environment parameters
369     * @return policy decision
370     * @throws PolicyException if result could not be computed for any
371     *         reason other than single sign on token problem.
372     * @throws SSOException if single sign on token is invalid or expired.
373     *
374     * @supported.api
375     */
376    public PolicyDecision getPolicyDecision(SSOToken token,
377                                            String resourceName,
378                                            Set actionNames,
379                                            Map envParameters)
380                throws PolicyException, SSOException {
381
382        //validate the token 
383        ssoTokenManager.validateToken(token);
384
385        if (debug.messageEnabled()) {
386            debug.message("PolicyEvaluator:getPolicyDecision():"
387                    + "token=" + token.getPrincipal().getName() 
388                    + ":resourceName=" + resourceName 
389                    + ":actionName=" + actionNames + ":entering");
390        }
391
392        //We need to normalize the resourcename before sending off the policy request to ensure the policy is evaluated
393        //for the correct resource.
394        ResourceName resourceComparator = policyProperties.getResourceComparator(serviceName);
395        resourceName = resourceComparator.canonicalize(resourceName);
396
397        PolicyDecision pd = null;
398        try {
399            pd = resourceResultCache.getPolicyDecision(
400                        appSSOToken, serviceName, token, resourceName, 
401                        actionNames, envParameters, RETRY_COUNT);
402        } catch (InvalidAppSSOTokenException e) {
403            if (debug.warningEnabled()) {
404                debug.warning("PolicyEvaluator.getPolicyDecision():"
405                        + "InvalidAppSSOTokenException occured:"
406                        + "getting new appssotoken");
407            }
408            appSSOToken = getNewAppSSOToken();
409            if (policyProperties.notificationEnabled()) {
410                if (debug.warningEnabled()) {
411                    debug.warning("PolicyEvaluator.getPolicyDecision():"
412                            + "InvalidAppSSOTokenException occured:"
413                            + "reRegistering remote policy listener");
414                }
415                reRegisterRemotePolicyListener(appSSOToken);
416            }
417            pd = resourceResultCache.getPolicyDecision(
418                    appSSOToken, serviceName, token, resourceName, 
419                    actionNames, envParameters, RETRY_COUNT);
420
421        }
422
423        if (debug.messageEnabled()) {
424            debug.message("PolicyEvaluator:getPolicyDecision():"
425                    + "token=" + token.getPrincipal().getName() 
426                    + ":resourceName=" + resourceName 
427                    + ":actionNames=" + actionNames 
428                    + ":returning policyDecision:" + pd.toXML());
429        }
430
431        Object[] objs = {resourceName, actionNames, pd.toXML()};
432        if (PolicyProperties.DECISION.equals(logActions)) {
433                logAccessMessage(Level.INFO,
434                                 ResBundleUtils.getString(
435                                 "policy_eval_decision", objs),token);
436        } //else nothing to log
437
438        return pd;
439    }
440
441    /**
442     * Returns the application single sign on token, this token will be
443     * passed while initializing the <code>PolicyEvaluator</code> or 
444     * if the application session token currently being used by 
445     * this <code>PolicyEvaluator</code>  has expired
446     *
447     * @return a valid application single sign on token.
448     */
449    private synchronized SSOToken getNewAppSSOToken() throws PolicyException {
450        SSOToken token = null;
451        if (debug.messageEnabled()) {
452            debug.message("PolicyEvaluator.getNewAppSSOToken():"
453                        + "entering");
454        }
455        if (appSSOTokenProvider != null) {
456            token = appSSOTokenProvider.getAppSSOToken();
457            try {
458                ssoTokenManager.refreshSession(token);
459                if (!ssoTokenManager.isValidToken(token)) {
460                    if (debug.messageEnabled()) {
461                        debug.message("PolicyEvaluator.getNewAppSSOToken():"
462                                    + "AdminTokenAction returned "
463                                    + " expired token, trying again");
464                    }
465                    token = appSSOTokenProvider.getAppSSOToken();
466                }
467            } catch (SSOException e) {
468                if (debug.warningEnabled()) {
469                    debug.warning("PolicyEvaluator.getNewAppSSOToken():"
470                            + "could not refresh session:", e);
471                }
472                token = appSSOTokenProvider.getAppSSOToken();
473            }
474        } else {
475            token = (SSOToken) AccessController.doPrivileged(
476                AdminTokenAction.getInstance());
477            try {
478                ssoTokenManager.refreshSession(token);
479                if (!ssoTokenManager.isValidToken(token)) {
480                    if (debug.messageEnabled()) {
481                        debug.message("PolicyEvaluator.getNewAppSSOToken():"
482                                    + "AdminTokenAction returned "
483                                    + " expired token, trying again");
484                    }
485                    token = (SSOToken) AccessController.doPrivileged(
486                            AdminTokenAction.getInstance());
487                }
488            } catch (SSOException e) {
489                if (debug.warningEnabled()) {
490                    debug.warning("PolicyEvaluator.getNewAppSSOToken():"
491                            + "could not refresh session:", e);
492                }
493                token = (SSOToken) AccessController.doPrivileged(
494                        AdminTokenAction.getInstance());
495            }
496        }
497        if (token == null) {
498            debug.error("PolicyEvaluator.getNewAppSSOToken():, "
499                        + "cannot obtain application SSO token");
500            throw new PolicyException(ResBundleUtils.rbName,
501                "can_not_create_app_sso_token", null, null);
502        }
503        if (debug.messageEnabled()) {
504            debug.message("PolicyEvaluator.getNewAppSSOToken():"
505                        + "returning token");
506        }
507        return token;
508    }
509
510    /**
511     * Logs an access message from policy client api 
512     * @param level logging level
513     * @param message message string
514     * @param token single sign on token of user
515     */
516    private void logAccessMessage(Level level, String message,
517                                        SSOToken token) {
518
519        try { 
520            if (accessLogger == null) {
521                accessLogger = (com.sun.identity.log.Logger)
522                                Logger.getLogger("amRemotePolicy.access");
523                if (accessLogger == null) {
524                    if (debug.warningEnabled()) {       
525                        debug.warning("PolicyEvaluator.logAccessMessage:"
526                            + "Failed to create Logger");
527                    }
528                    return;
529                }
530            }
531            LogRecord lr = new LogRecord(level, message, token);
532            accessLogger.log(lr, appSSOToken);
533        } catch (Throwable ex) {
534            if (debug.warningEnabled()) {       
535                debug.warning("PolicyEvaluator.logAccessMessage:Error"
536                        + " writing access logs");
537            }
538        }
539
540    }
541
542    /**
543     * Returns application single sign on token provider 
544     *
545     * @return <code>AppSSOTokenProvider</code> Object.
546     */
547    AppSSOTokenProvider getAppSSOTokenProvider() {
548        return appSSOTokenProvider;
549    }
550
551    /**
552     * Gets names of policy advices that could be handled by OpenAM
553     * if PEP redirects user agent to OpenAM. If the server reports
554     * an error indicating the app sso token provided was invalid,
555     * new app sso token is obtained from app 
556     * sso token  provider and another attempt is made to get policy advices
557     * 
558     * @param refetchFromServer indicates whether to get the values fresh 
559     *      from OpenAM or return the values from local cache
560     * @return names of policy advices that could be handled by OpenAM
561     *         Enterprise if PEP redirects user agent to OpenAM.
562     * @throws InvalidAppSSOTokenException if the server reported that the
563     *         app sso token provided was invalid 
564     * @throws PolicyEvaluationException if the server reported any other error
565     * @throws PolicyException if there are problems in policy module
566     *         while getting the result
567     * @throws SSOException if there are problems with sso token
568     *         while getting the result
569     */
570    public Set getAdvicesHandleableByAM(boolean refetchFromServer) 
571            throws InvalidAppSSOTokenException, PolicyEvaluationException,
572            PolicyException, SSOException {
573        Set advicesHandleableByAM = null;
574        if (debug.messageEnabled()) {   
575            debug.message("PolicyEvaluator.getAdvicesHandleableByAM(): Entering"
576                    + "refetchFromServer=" + refetchFromServer);
577        }
578        try {
579            advicesHandleableByAM 
580                    = resourceResultCache.getAdvicesHandleableByAM(
581                    appSSOToken, refetchFromServer);
582        } catch (InvalidAppSSOTokenException e) {
583            //retry with new app sso token
584            if (debug.warningEnabled()) {
585                debug.warning("PolicyEvaluator.getAdvicesHandleableByAM():"
586                        + "got InvalidAppSSOTokenException, "
587                        + " retrying with new app token");
588            }
589            advicesHandleableByAM 
590                    = resourceResultCache.getAdvicesHandleableByAM(
591                    getNewAppSSOToken(), refetchFromServer);
592        } catch (PolicyException pe) {
593            Throwable nestedException = pe.getNestedException();
594            if ((nestedException != null) 
595                        && (nestedException instanceof SessionException)) {
596                //retry with new app sso token
597                if (debug.warningEnabled()) {
598                    debug.warning("PolicyEvaluator.getAdvicesHandleableByAM():"
599                            + "got SessionException, "
600                            + " retrying with new app token");
601                }
602                advicesHandleableByAM 
603                        = resourceResultCache.getAdvicesHandleableByAM(
604                        getNewAppSSOToken(), refetchFromServer);
605
606            } else {
607                throw pe;
608            }
609        }
610        if (debug.messageEnabled()) {   
611            debug.message("PolicyEvaluator.getAdvicesHandleableByAM():"
612                    + " Returning advicesHandleableByAM=" 
613                    + advicesHandleableByAM);
614        }
615        return advicesHandleableByAM;
616    }
617
618    /**
619     *  Returns XML string representation of advice map contained in the
620     *  actionDecision. This is a convenience method for use by PEP.
621     *
622     *  @param actionDecision actionDecision that contains the 
623     *        advices
624     *  @return XML string representation of advice map contained in the
625     *      actionDecision subject to the following rule. If the 
626     *      actionDecision is null, the return value would be null. 
627     *      Otherwise, if the actionDecision does not contain any advice, 
628     *      the return value would be null. Otherwise, actionDecision contains
629     *      advices. In this case, if the advices contains at least one advice
630     *      name that could be handled by AM, the complete advices element is 
631     *      serialized to XML and the XML string is returned. Otherwise, null
632     *      is returned.
633     *  @throws PolicyException for any abnormal condition encountered in
634     *      policy module
635     *  @throws SSOException for any abnormal condition encountered in
636     *      session module
637     */
638    public String getCompositeAdvice(ActionDecision actionDecision )
639            throws PolicyException, SSOException {
640
641        if(debug.messageEnabled()) {
642            debug.message("PolicyEvaluator.getCompositeAdvice():"
643                    + " entering, actionDecision = " + actionDecision.toXML());
644        }
645
646        String compositeAdvice = null;
647        boolean matchFound = false;
648        Map advices = null;
649        if (actionDecision != null) {
650            advices = actionDecision.getAdvices();
651        }
652
653        //false : use cached value
654        Set handleableAdvices = getAdvicesHandleableByAM(false); 
655
656        if(debug.messageEnabled()) {
657            debug.message("PolicyEvaluator.getCompositeAdvice():"
658                    + " handleableAdvices = " + handleableAdvices);
659        }
660
661        if  ((advices != null) && !advices.isEmpty() 
662                && (handleableAdvices !=null) 
663                && (!handleableAdvices.isEmpty()) ) {
664            Set adviceKeys = advices.keySet();
665
666            if(debug.messageEnabled()) {
667                debug.message("PolicyEvaluator.getCompositeAdvice():"
668                        + " adviceKeys = " + adviceKeys);
669            }
670
671            Iterator keyIter = adviceKeys.iterator();
672            while (keyIter.hasNext()) {
673                Object adviceKey = keyIter.next();
674                if (handleableAdvices.contains(adviceKey)) {
675                    matchFound = true;
676                    if(debug.messageEnabled()) {
677                        debug.message("PolicyEvaluator.getCompositeAdvice():"
678                                + " matchFound = " + matchFound);
679                        debug.message("PolicyEvaluator.getCompositeAdvice():"
680                                + " common key = " + adviceKey);
681                    }
682                    break;
683                }
684            }
685        }
686
687        if (matchFound) {
688            compositeAdvice = PolicyUtils.advicesToXMLString(advices);
689        }
690
691        if(debug.messageEnabled()) {
692            debug.message("PolicyEvaluator.getCompositeAdvice():"
693                    + " returning, compositeAdvcie = " + compositeAdvice);
694        }
695
696        return compositeAdvice;
697    }
698
699    /**
700     * Registers this client again with policy service to get policy 
701     * change notifications 
702     *
703     * @param appToken application sso token to use while registering with
704     * policy service to get notifications
705     *
706     */
707    void reRegisterRemotePolicyListener(SSOToken appToken)
708            throws PolicyException {
709        if (debug.messageEnabled()) {
710            debug.message("PolicyEvaluator.reRegisterRemotePolicyListener():"
711                    + "entering");
712        } 
713        resourceResultCache.addRemotePolicyListener(appSSOToken,
714                serviceName, policyProperties.getNotificationURL(),
715                true); //reRegister
716
717        //clear policy decision cache
718        resourceResultCache.clearCachedDecisionsForService(serviceName);
719
720        if (debug.messageEnabled()) {
721            debug.message("PolicyEvaluator.reRegisterRemotePolicyListener():"
722                    + "returning");
723        } 
724    }
725
726}