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