Coverage Report - pt.digitalis.dif.controller.AbstractDIFDispatcher
 
Classes in this File Line Coverage Branch Coverage Complexity
AbstractDIFDispatcher
0%
0/329
0%
0/186
4,613
 
 1  0
 /**
 2  
  * - Digitalis Internal Framework v2.0 - (C) 2007, Digitalis Informatica. Distribuicao e Gestao de Informatica, Lda.
 3  
  * Estrada de Paco de Arcos num.9 - Piso -1 2780-666 Paco de Arcos Telefone: (351) 21 4408990 Fax: (351) 21 4408999
 4  
  * http://www.digitalis.pt
 5  
  */
 6  
 package pt.digitalis.dif.controller;
 7  
 
 8  
 import java.util.Map;
 9  
 import java.util.Map.Entry;
 10  
 
 11  
 import pt.digitalis.dif.controller.interfaces.IAuthenticationPlugin;
 12  
 import pt.digitalis.dif.controller.interfaces.IControllerCleanupTask;
 13  
 import pt.digitalis.dif.controller.interfaces.IDIFContext;
 14  
 import pt.digitalis.dif.controller.interfaces.IDIFDispatcher;
 15  
 import pt.digitalis.dif.controller.interfaces.IDIFRequest;
 16  
 import pt.digitalis.dif.controller.interfaces.IDIFSession;
 17  
 import pt.digitalis.dif.controller.interfaces.IPrivateDIFSession;
 18  
 import pt.digitalis.dif.controller.objects.Constants;
 19  
 import pt.digitalis.dif.controller.objects.ControllerExecutionStep;
 20  
 import pt.digitalis.dif.controller.objects.DIFContext;
 21  
 import pt.digitalis.dif.controller.objects.DIFResponse;
 22  
 import pt.digitalis.dif.controller.objects.DIFSession;
 23  
 import pt.digitalis.dif.controller.objects.DispatcherAuthenticationResult;
 24  
 import pt.digitalis.dif.controller.objects.SSOInfo;
 25  
 import pt.digitalis.dif.controller.security.managers.IAuthenticationManager;
 26  
 import pt.digitalis.dif.controller.security.managers.IAuthorizationManager;
 27  
 import pt.digitalis.dif.controller.security.managers.IIdentityManager;
 28  
 import pt.digitalis.dif.controller.security.managers.ISessionManager;
 29  
 import pt.digitalis.dif.controller.security.objects.IDIFUser;
 30  
 import pt.digitalis.dif.dem.DEMRegistryImpl;
 31  
 import pt.digitalis.dif.dem.annotations.controller.Channel;
 32  
 import pt.digitalis.dif.dem.interfaces.IStage;
 33  
 import pt.digitalis.dif.dem.interfaces.IStageInstance;
 34  
 import pt.digitalis.dif.dem.managers.IMessageManager;
 35  
 import pt.digitalis.dif.dem.objects.ViewObject;
 36  
 import pt.digitalis.dif.dem.objects.messages.MessageList;
 37  
 import pt.digitalis.dif.exception.controller.BusinessFlowException;
 38  
 import pt.digitalis.dif.exception.controller.ControllerException;
 39  
 import pt.digitalis.dif.exception.controller.LicenseViolationException;
 40  
 import pt.digitalis.dif.exception.controller.SessionTimeoutException;
 41  
 import pt.digitalis.dif.exception.controller.UserHasNoAccessToStage;
 42  
 import pt.digitalis.dif.exception.security.AuthenticationManagerException;
 43  
 import pt.digitalis.dif.exception.security.IdentityManagerException;
 44  
 import pt.digitalis.dif.ioc.DIFIoCRegistry;
 45  
 import pt.digitalis.dif.startup.DIFInitializer;
 46  
 import pt.digitalis.dif.startup.DIFStartupConfiguration;
 47  
 import pt.digitalis.dif.utils.logging.DIFLogger;
 48  
 import pt.digitalis.utils.common.StringUtils;
 49  
 import pt.digitalis.utils.config.IConfigurations;
 50  
 
 51  
 import com.newrelic.api.agent.NewRelic;
 52  
 import com.newrelic.api.agent.Trace;
 53  
 
 54  
 /**
 55  
  * This class is the heart of the framework. Its responsibilities include receive and validate the incoming DIFRequest,
 56  
  * the request processing (if the request is valid) and the routing of the stage-generated DIFResponse.
 57  
  * AbstractDIFDispatcher implements the TEMPLATE METHOD design pattern (see GoF's "Design Patterns: Elements of Reusable
 58  
  * Object-Oriented Software"). As such, the <code> executeTaskSteps() </code> method is declared final. This method
 59  
  * defines the dispatching-cycle steps, and these steps must be always run in the same order to all the requests. There
 60  
  * is a set of protected methods that correspond to the aforementioned dispatching-cycle steps. These methods can be
 61  
  * overridden in derived classes if one wants to modify the behavior associated with a given step. ChALs should call a
 62  
  * particular controller so this class (the superclass) is declared <code>abstract</code> to inhibit instantiation.
 63  
  * 
 64  
  * @author Rodrigo Goçalves <a href="mailto:rgoncalves@digitalis.pt">rgoncalves@digitalis.pt</a>
 65  
  * @author Pedro Viegas <a href="mailto:pviegas@digitalis.pt">pviegas@digitalis.pt</a>
 66  
  * @created 2007/03/16
 67  
  */
 68  
 abstract public class AbstractDIFDispatcher implements IDIFDispatcher {
 69  
 
 70  
     /** The messae for invalid stage requests */
 71  
     final public static String INVALID_STAGE_ERROR_MESSAGE = "The stage is not registered in the DEM. Check the stage id.";
 72  
 
 73  
     /** The message manager to get class messages */
 74  
     static IMessageManager messageManager;
 75  
 
 76  
     /** Messages cache for all constraints */
 77  
     static private MessageList messages;
 78  
 
 79  
     /**
 80  
      * Get's the Message Manager from the IoC
 81  
      * 
 82  
      * @return the message manager instance
 83  
      */
 84  
     static private IMessageManager getMessageManager()
 85  
     {
 86  0
         if (messageManager == null)
 87  0
             messageManager = DIFIoCRegistry.getRegistry().getImplementation(IMessageManager.class);
 88  
 
 89  0
         return messageManager;
 90  
     }
 91  
 
 92  
     /**
 93  
      * Perform any clean up tasks that have been contributed by ...
 94  
      * 
 95  
      * @param context
 96  
      *            the context
 97  
      * @param success
 98  
      *            T if the stage was successfully executed
 99  
      * @throws ControllerException
 100  
      */
 101  
     static public final void performCleanup(IDIFContext context, boolean success) throws ControllerException
 102  
     {
 103  0
         ControllerException controllerException = null;
 104  
 
 105  0
         for (IControllerCleanupTask task: DIFIoCRegistry.getRegistry().getImplementations(IControllerCleanupTask.class))
 106  
             try
 107  
             {
 108  0
                 task.doTask(context, success);
 109  
             }
 110  0
             catch (Exception e)
 111  
             {
 112  0
                 if (controllerException == null)
 113  0
                     controllerException = new ControllerException(ControllerExecutionStep.DISPATCHER_CONCLUDE, e);
 114  
             }
 115  
 
 116  0
         if (controllerException != null)
 117  0
             throw controllerException;
 118  0
     }
 119  
 
 120  
     /** If an authentication error has occurred */
 121  0
     protected ControllerException authenticationException = null;
 122  
 
 123  
     /** the channel ID */
 124  0
     private String channelID = null;
 125  
 
 126  
     /** The execution context. */
 127  
     protected IDIFContext difContext;
 128  
 
 129  
     /** The Authentication Manager */
 130  
     protected IAuthenticationManager theAuthenticationManager;
 131  
 
 132  
     /** The authorization manager. */
 133  
     protected IAuthorizationManager theAuthorizationManager;
 134  
 
 135  
     /** The configuration manager. */
 136  
     protected IConfigurations theConfigurations;
 137  
 
 138  
     /** The identity manager. */
 139  
     protected IIdentityManager theIdentityManager;
 140  
 
 141  
     /** The session manager. */
 142  
     protected ISessionManager theSessionManager;
 143  
 
 144  
     /** The stage to be executed. */
 145  
     protected IStage theStage;
 146  
 
 147  
     /**
 148  
      * Default constructor. This constructor must be inherited in the extended classes and injected with the instances
 149  
      * from the IoC
 150  
      * 
 151  
      * @param theIdentityManager
 152  
      *            the identity manager
 153  
      * @param theAuthenticationManager
 154  
      *            the authentication manager
 155  
      * @param theAuthorizationManager
 156  
      *            the authorization manager
 157  
      * @param theSessionManager
 158  
      *            the authentication manager
 159  
      * @param theConfigurationManager
 160  
      *            the configurations manager
 161  
      */
 162  0
     public AbstractDIFDispatcher(IIdentityManager theIdentityManager, IAuthenticationManager theAuthenticationManager,
 163  
             IAuthorizationManager theAuthorizationManager, ISessionManager theSessionManager,
 164  
             IConfigurations theConfigurationManager)
 165  
     {
 166  0
         this.theIdentityManager = theIdentityManager;
 167  0
         this.theAuthorizationManager = theAuthorizationManager;
 168  0
         this.theSessionManager = theSessionManager;
 169  0
         this.theConfigurations = theConfigurationManager;
 170  0
         this.theAuthenticationManager = theAuthenticationManager;
 171  0
     }
 172  
 
 173  
     /**
 174  
      * Authenticates a user on the framework. The method is defined with protected scope to allow overriding on the
 175  
      * subclasses.
 176  
      * 
 177  
      * @throws ControllerException
 178  
      *             when any runtime exception is thrown or the authentication process has failed
 179  
      */
 180  
     @Trace(metricName = "DIF:Dispatcher:Authenticate", dispatcher = true)
 181  
     protected void authenticate() throws ControllerException
 182  
     {
 183  
         // 1. perform the necessary operations before authentication execution
 184  0
         this.preAuthentication();
 185  
         // 2. execute the authentication
 186  0
         DispatcherAuthenticationResult authenticationResult = this.performAuthentication();
 187  
         // 3. perform the post authentication operations
 188  0
         this.postAuthentication(authenticationResult);
 189  0
     }
 190  
 
 191  
     /**
 192  
      * Check the user permissions to run a given stage. The method is defined with protected scope to allow overriding
 193  
      * on the subclasses.
 194  
      * 
 195  
      * @throws ControllerException
 196  
      *             when any runtime exception is thrown
 197  
      */
 198  
     @Trace(metricName = "DIF:Dispatcher:Authorize", dispatcher = true)
 199  
     protected void authorize() throws ControllerException
 200  
     {
 201  0
         IDIFSession session = null;
 202  0
         IDIFUser user = null;
 203  0
         boolean authorized = false;
 204  0
         String reason = "";
 205  0
         Exception exception = null;
 206  
 
 207  
         try
 208  
         {
 209  0
             session = this.getContext().getSession();
 210  
 
 211  0
             if (session != null)
 212  
             {
 213  0
                 user = session.getUser();
 214  
 
 215  0
                 if (user != null)
 216  
                 {
 217  0
                     if (user.canAccess(this.getStage()))
 218  
                     {
 219  0
                         authorized = true;
 220  
                     }
 221  
                     else
 222  
                     {
 223  0
                         authorized = false;
 224  0
                         reason = getMessages().get("noAccess").replace("${user}", "\"" + user.getID() + "\"")
 225  0
                                 .replace("${name}", this.getStage().getName());
 226  
                     }
 227  
                 }
 228  
                 else
 229  
                 {
 230  0
                     if (!theAuthorizationManager.hasAccessPublic(this.getStage()))
 231  
                     {
 232  0
                         authorized = false;
 233  0
                         reason = getMessages().get("noAccess").replace("${user}", "")
 234  0
                                 .replace("${name}", this.getStage().getName());
 235  
                     }
 236  
                     else
 237  0
                         authorized = true;
 238  
                 }
 239  
             }
 240  
             else
 241  
             {
 242  
                 // No session! Do not grant access! This is an error. Must
 243  
                 // always exist a session.
 244  0
                 authorized = false;
 245  0
                 reason = getMessages().get("noSession").replace("${name}", this.getStage().getName());
 246  
             }
 247  
 
 248  
         }
 249  0
         catch (RuntimeException runtimeException)
 250  
         {
 251  0
             authorized = false;
 252  0
             exception = runtimeException;
 253  
         }
 254  
 
 255  0
         if (!authorized)
 256  
         {
 257  0
             UserHasNoAccessToStage controllerException = new UserHasNoAccessToStage(
 258  0
                     ControllerExecutionStep.DISPATCHER_AUTHORIZATION, reason, exception);
 259  
 
 260  0
             controllerException.addToExceptionContext("Context", this.getContext());
 261  
 
 262  0
             throw controllerException;
 263  
         }
 264  0
     }
 265  
 
 266  
     /**
 267  
      * This method checks if the license is valid.
 268  
      * 
 269  
      * @return T if stage is registered, F otherwise
 270  
      */
 271  
     protected boolean checkLicense()
 272  
     {
 273  0
         return this.getStage().isRegistered();
 274  
     }
 275  
 
 276  
     /**
 277  
      * Concludes the execution cycle. Touches the session to ensure it stays alive and returns any service objects to
 278  
      * the pool (not implemented as of version 0.0.1). If session is not active, it's not kept alive and thus on the
 279  
      * next request the access will be denied. 'Finalize' would be better name but it's forbidden due to a name-clash
 280  
      * with <code>java.lang.Object's finalize()</code> method.
 281  
      * 
 282  
      * @throws ControllerException
 283  
      *             when any runtime exception is thrown
 284  
      */
 285  
     protected void conclude() throws ControllerException
 286  
     {
 287  
         try
 288  
         {
 289  0
             ((IPrivateDIFSession) this.getContext().getRequest().getSession()).forceKeepAlive();
 290  
             // Keep session alive if it has not timed out
 291  0
             theSessionManager.update(this.getContext().getSession());
 292  
         }
 293  0
         catch (RuntimeException runtimeException)
 294  
         {
 295  0
             ControllerException controllerException = new ControllerException(
 296  0
                     ControllerExecutionStep.DISPATCHER_CONCLUDE, runtimeException);
 297  
 
 298  0
             controllerException.addToExceptionContext("Context", this.getContext());
 299  
 
 300  0
             throw controllerException;
 301  
         }
 302  0
     }
 303  
 
 304  
     /**
 305  
      * Receives a BaseDIFRequest object as parameter and creates a service execution context for the request.
 306  
      * 
 307  
      * @param difRequest
 308  
      *            the request received from AbstractChAL
 309  
      * @return The request's context for service execution
 310  
      * @throws ControllerException
 311  
      *             when any runtime exception is thrown
 312  
      */
 313  
     final private DIFContext createContext(IDIFRequest difRequest) throws ControllerException
 314  
     {
 315  0
         DIFContext theContext = null;
 316  
 
 317  
         try
 318  
         {
 319  0
             theContext = new DIFContext();
 320  
 
 321  0
             theContext.setStage(difRequest.getStage());
 322  0
             theContext.setRequest(difRequest);
 323  
 
 324  0
             return theContext;
 325  
 
 326  
         }
 327  0
         catch (RuntimeException runtimeException)
 328  
         {
 329  0
             ControllerException controllerException = new ControllerException(
 330  0
                     ControllerExecutionStep.DISPATCHER_CREATE_CONTEXT, runtimeException);
 331  
 
 332  0
             controllerException.addToExceptionContext("Context", theContext);
 333  
 
 334  0
             throw controllerException;
 335  
         }
 336  
 
 337  
     }
 338  
 
 339  
     /**
 340  
      * Generates the response that will be sent back to AbstractChAL from the request's execution context.
 341  
      * 
 342  
      * @param stageInstance
 343  
      *            the executed stage instance
 344  
      * @return The response to send back to AbstractChAL
 345  
      * @throws ControllerException
 346  
      *             if an exception has occurred
 347  
      */
 348  
     protected DIFResponse createResponse(IStageInstance stageInstance) throws ControllerException
 349  
     {
 350  
         try
 351  
         {
 352  0
             DIFResponse difResponse = new DIFResponse();
 353  
 
 354  0
             difResponse.setView(this.getContext().getView());
 355  0
             difResponse.setResultMessage(this.getContext().getResultMessage());
 356  0
             difResponse.setStageResults(this.getContext().getStageResults());
 357  0
             difResponse.setRequest(this.getContext().getRequest());
 358  0
             difResponse.setStageInstance(stageInstance);
 359  
 
 360  0
             return difResponse;
 361  
         }
 362  0
         catch (RuntimeException runtimeException)
 363  
         {
 364  0
             ControllerException controllerException = new ControllerException(
 365  0
                     ControllerExecutionStep.DISPATCHER_CREATE_RESPONSE, runtimeException);
 366  
 
 367  0
             controllerException.addToExceptionContext("Context", this.getContext());
 368  0
             controllerException.addToExceptionContext("Stage Instance", stageInstance);
 369  
 
 370  0
             throw controllerException;
 371  
         }
 372  
     }
 373  
 
 374  
     /**
 375  
      * @see pt.digitalis.dif.controller.interfaces.IDIFDispatcher#dispatch(pt.digitalis.dif.controller.interfaces.IDIFRequest)
 376  
      */
 377  
     final public DIFResponse dispatch(IDIFRequest difRequest) throws BusinessFlowException, ControllerException
 378  
     {
 379  
         // Initialize framework
 380  0
         DIFInitializer.initialize(true, true);
 381  
 
 382  
         // Create an execution context for the incoming request
 383  0
         this.setContext(createContext(difRequest));
 384  
 
 385  
         // Create the response
 386  0
         DIFResponse difResponse = null;
 387  0
         IStageInstance stageInstance = null;
 388  0
         boolean firstTime = true;
 389  
 
 390  0
         while (firstTime || this.getContext().hasRedirection())
 391  
         {
 392  0
             if (!firstTime)
 393  
             {
 394  
                 try
 395  
                 {
 396  0
                     this.getContext().handleRedirection();
 397  
 
 398  0
                     if (stageInstance != null)
 399  0
                         stageInstance.setContext(this.getContext());
 400  
 
 401  
                 }
 402  0
                 catch (RuntimeException runtimeException)
 403  
                 {
 404  0
                     ControllerException controllerException = new ControllerException(
 405  0
                             ControllerExecutionStep.DISPATCHER_REDIRECTION, runtimeException);
 406  
 
 407  0
                     controllerException.addToExceptionContext("Context", this.getContext());
 408  
 
 409  0
                     throw controllerException;
 410  
                 }
 411  
             }
 412  
             try
 413  
             {
 414  
                 // Run the task steps
 415  0
                 stageInstance = runDispatchingSteps(!firstTime);
 416  
 
 417  
             }
 418  0
             catch (ControllerException exception)
 419  
             {
 420  
 
 421  0
                 if (ControllerExecutionStep.DISPATCHER_AUTHORIZATION.equals(exception.getStep()))
 422  
                 {
 423  
                     // If the stage has no access we must determine what to
 424  
                     // do...
 425  0
                     handleNonAuthorizedAccess(stageInstance, exception);
 426  
                 }
 427  
                 else
 428  0
                     throw exception;
 429  
             }
 430  0
             firstTime = false;
 431  
         }
 432  
 
 433  
         // Create a response to send back to AbstractChAL
 434  0
         difResponse = createResponse(stageInstance);
 435  
 
 436  0
         return difResponse;
 437  
     }
 438  
 
 439  
     /**
 440  
      * Actions to perform after a successful login
 441  
      * 
 442  
      * @param ssoInfo
 443  
      *            the single sign-on object
 444  
      */
 445  
     protected void doAfterLogin(SSOInfo ssoInfo)
 446  
     {
 447  
         // Nothing to do. This is here to facilitate SSOs integrations
 448  0
     }
 449  
 
 450  
     /**
 451  
      * Actions to perform after a successful logout
 452  
      * 
 453  
      * @param ssoInfo
 454  
      *            the single sign-on object
 455  
      */
 456  
     protected void doAfterLogout(SSOInfo ssoInfo)
 457  
     {
 458  
         // Nothing to do. This is here to facilitate SSOs integrations
 459  0
     }
 460  
 
 461  
     /**
 462  
      * Executes the appropriate tasks to serve the request.
 463  
      * 
 464  
      * @return the executed stage instance
 465  
      * @throws BusinessFlowException
 466  
      *             when an exception is thrown by the stage's init/execute steps
 467  
      * @throws ControllerException
 468  
      *             when any runtime exception is thrown
 469  
      */
 470  
     @Trace(metricName = "DIF:Dispatcher:Execute", dispatcher = true)
 471  
     protected IStageInstance execute() throws BusinessFlowException, ControllerException
 472  
     {
 473  0
         IStageInstance stage = null;
 474  0
         ViewObject theView = null;
 475  
 
 476  
         try
 477  
         {
 478  0
             stage = this.getStage().getInstance();
 479  
 
 480  
             // If the stage is configured for authentication error injection...
 481  0
             if (stage.hasAuthenticationErrorInjection())
 482  0
                 stage.setAuthenticationError(getAuthenticationException());
 483  
 
 484  
             // Business initialization
 485  0
             stage._CG_init(this.getContext());
 486  
 
 487  0
             if (!stage.hasParameterErrorInjection() && stage.getParameterErrors().hasErrors())
 488  0
                 DIFLogger.getLogger().warn(
 489  0
                         "There were errors in the parameter validation witch will be ignored since"
 490  
                                 + " there is no @InjectParameterErrors anotated attribute on the stage "
 491  0
                                 + stage.getID());
 492  
 
 493  
             // Business logic execution
 494  0
             theView = stage._CG_execute(this.getContext());
 495  
 
 496  
             // Business finalization
 497  0
             stage._CG_finalize(this.getContext());
 498  
 
 499  
             // Set the View in context
 500  0
             this.getContext().setView(theView);
 501  
 
 502  
             // Add the current stage to the navigation history object
 503  0
             this.getContext().getSession().getNavigationHistory().addStage(stage);
 504  
 
 505  0
             return stage;
 506  
         }
 507  0
         catch (RuntimeException runtimeException)
 508  
         {
 509  0
             ControllerException controllerException = new ControllerException(
 510  0
                     ControllerExecutionStep.DISPATCHER_EXECUTE, runtimeException);
 511  
 
 512  0
             controllerException.addToExceptionContext("Context", this.getContext());
 513  0
             controllerException.addToExceptionContext("Stage", stage);
 514  0
             controllerException.addToExceptionContext("View object to Render", theView);
 515  
 
 516  0
             throw controllerException;
 517  
         }
 518  
     }
 519  
 
 520  
     /**
 521  
      * @return the authenticationException
 522  
      */
 523  
     public ControllerException getAuthenticationException()
 524  
     {
 525  0
         return authenticationException;
 526  
     }
 527  
 
 528  
     /**
 529  
      * @return the channel ID read from the {@link Channel} annotation
 530  
      */
 531  
     public String getChannelID()
 532  
     {
 533  0
         if (channelID == null)
 534  0
             channelID = this.getClass().getAnnotation(Channel.class).value();
 535  
 
 536  0
         return channelID;
 537  
     }
 538  
 
 539  
     /**
 540  
      * Inspector for the execution context.
 541  
      * 
 542  
      * @return the execution context
 543  
      */
 544  
     protected IDIFContext getContext()
 545  
     {
 546  0
         return this.difContext;
 547  
     }
 548  
 
 549  
     /**
 550  
      * Inspector for the identity manager.
 551  
      * 
 552  
      * @return the defined identity manager
 553  
      */
 554  
     protected IIdentityManager getIdentityManager()
 555  
     {
 556  0
         return this.theIdentityManager;
 557  
     }
 558  
 
 559  
     /**
 560  
      * Lazy loading getter of messages
 561  
      * 
 562  
      * @return the messages
 563  
      */
 564  
     protected Map<String, String> getMessages()
 565  
     {
 566  
         // If the messages have not yet been loaded do it now
 567  0
         if (messages == null)
 568  
         {
 569  0
             messages = getMessageManager().collectEntityMessagesFromRepository(AbstractDIFDispatcher.class);
 570  
         }
 571  
 
 572  0
         return messages.getMessages(getContext().getLanguage());
 573  
     }
 574  
 
 575  
     /**
 576  
      * Used to inject the SSO Info for the dispatcher authentication process
 577  
      * 
 578  
      * @return the SSOInfo if available
 579  
      */
 580  
     protected SSOInfo getSSOInfo()
 581  
     {
 582  
         // Nothing to do. This is here to facilitate SSOs integrations
 583  0
         return null;
 584  
     }
 585  
 
 586  
     /**
 587  
      * Inspector for the stage to execute.
 588  
      * 
 589  
      * @return the stage to execute
 590  
      */
 591  
     private IStage getStage()
 592  
     {
 593  0
         return this.theStage;
 594  
     }
 595  
 
 596  
     /**
 597  
      * Handles the occurred exception to redirect to a given handler and perform cleanup actions
 598  
      * 
 599  
      * @param exception
 600  
      *            the raised exception
 601  
      * @return T if the exception was handled and should not be re-throwned
 602  
      * @throws BusinessFlowException
 603  
      * @throws ControllerException
 604  
      */
 605  
     protected boolean handleException(Exception exception) throws BusinessFlowException, ControllerException
 606  
     {
 607  
 
 608  0
         performCleanup(this.getContext(), false);
 609  
 
 610  0
         if (ExceptionHandlers.hasHandler(exception))
 611  
         {
 612  0
             ExceptionHandlers.handleException(this.getContext(), exception);
 613  
 
 614  0
             return true;
 615  
         }
 616  
         else
 617  0
             return false;
 618  
     }
 619  
 
 620  
     /**
 621  
      * Handles a non authorized access
 622  
      * 
 623  
      * @param stageInstance
 624  
      *            the stage instance requested
 625  
      * @param exception
 626  
      *            the exception thrown
 627  
      * @throws ControllerException
 628  
      *             if the error was not a non authorized access
 629  
      */
 630  
     abstract protected void handleNonAuthorizedAccess(IStageInstance stageInstance, ControllerException exception)
 631  
             throws ControllerException;
 632  
 
 633  
     /**
 634  
      * Implements the authentication execution logic
 635  
      * 
 636  
      * @return the authentication result
 637  
      * @throws ControllerException
 638  
      */
 639  
     protected DispatcherAuthenticationResult performAuthentication() throws ControllerException
 640  
     {
 641  0
         DispatcherAuthenticationResult dispatcherResult = DispatcherAuthenticationResult.NO_ACTION;
 642  
 
 643  0
         if (this.getContext().getSession().isLogged())
 644  
         {
 645  
             /*
 646  
              * The logout action must be performed before any of the other operations. There are two ways to invoke
 647  
              */
 648  0
             if ("true".equals(this.getContext().getRequest().getParameter(IDIFRequest.LOGOUT_PARAMETER_ID)))
 649  
             {
 650  
 
 651  0
                 String providerId = (String) this.getContext().getSession()
 652  0
                         .getAttribute(DIFSession.REMOTE_AUTHENTICATION_PROVIDER_LOGOUT);
 653  0
                 if (providerId != null)
 654  
                 {
 655  0
                     String title = getMessages().get("remoteAuthenticationLogoutTitle").replace("${providerid}",
 656  0
                             providerId);
 657  0
                     String text = getMessages().get("remoteAuthenticationLogoutText").replace("${providerid}",
 658  0
                             providerId);
 659  
 
 660  0
                     this.getContext().addResultMessage("warn", title, text, true);
 661  0
                     this.getContext().getSession().addAttribute(DIFSession.REMOTE_AUTHENTICATION_PROVIDER_LOGOUT, null);
 662  
                 }
 663  
 
 664  0
                 theSessionManager.logOut(this.getContext().getSession().getSessionID());
 665  0
                 dispatcherResult = DispatcherAuthenticationResult.LOGOUT;
 666  
             }
 667  
         }
 668  
 
 669  
         /*
 670  
          * The stage requires authentication
 671  
          */
 672  0
         else if (this.getStage().hasAuthentication())
 673  
         {
 674  0
             Object userID = null;
 675  0
             Object suppliedPassword = null;
 676  0
             String reason = "";
 677  0
             Exception exception = null;
 678  
 
 679  
             try
 680  
             {
 681  
                 // ...get credentials from the request...
 682  0
                 userID = this.getContext().getRequest().getParameter(IDIFRequest.USER_PARAMETER_ID);
 683  0
                 suppliedPassword = this.getContext().getRequest().getParameter(IDIFRequest.PASSWORD_PARAMETER_ID);
 684  
 
 685  
                 // The user is logged and is the same user, unnecessary login the user again
 686  0
                 if (this.getContext().getSession().isLogged()
 687  0
                         && this.getContext().getSession().getUser().getID().equals(userID))
 688  
                 {
 689  
 
 690  
                     /* There is no need to authenticate the same user! */
 691  0
                     userID = null;
 692  0
                     suppliedPassword = null;
 693  
                 }
 694  
 
 695  0
                 if ((userID == null || suppliedPassword == null)
 696  0
                         && dispatcherResult == DispatcherAuthenticationResult.NO_ACTION)
 697  
                     // No user logged and no authentication asked, so,
 698  
                     // authenticate does nothing but has passed
 699  0
                     return dispatcherResult;
 700  
                 else
 701  
                 {
 702  
 
 703  
                     // If user's credentials are valid
 704  0
                     if (userID != null && suppliedPassword != null
 705  0
                             && this.validateUserCredentials(userID.toString(), suppliedPassword.toString()))
 706  
                     {
 707  
                         // Log user
 708  0
                         IDIFSession session = theSessionManager.logIn(getContext().getSession().getSessionID(),
 709  0
                                 userID.toString(), suppliedPassword.toString());
 710  0
                         this.difContext.setSession(session);
 711  
 
 712  0
                         if (session.isLogged())
 713  
                         {
 714  0
                             dispatcherResult = DispatcherAuthenticationResult.LOGIN;
 715  
                         }
 716  
                         else
 717  
                         {
 718  0
                             dispatcherResult = DispatcherAuthenticationResult.FAILED;
 719  
                             // result = false;
 720  0
                             reason = getMessages().get("failedWithGoodUser");
 721  
                         }
 722  
                     }
 723  
                     else
 724  
                     {
 725  
                         // A bad user/pass was given. Authentication has failed
 726  
                         // result = false;
 727  0
                         dispatcherResult = DispatcherAuthenticationResult.FAILED;
 728  
 
 729  0
                         if (userID != null && this.getIdentityManager().userExists(userID.toString()))
 730  0
                             reason = getMessages().get("wrongPass");
 731  
                         else
 732  0
                             reason = getMessages().get("noUser");
 733  
                     }
 734  
                 }
 735  
             }
 736  0
             catch (IdentityManagerException identityManagerException)
 737  
             {
 738  
                 // result = false;
 739  0
                 dispatcherResult = DispatcherAuthenticationResult.FAILED;
 740  0
                 reason = "Could not access identity manager to validate the user's credentials! ";
 741  0
                 exception = identityManagerException;
 742  
             }
 743  0
             catch (AuthenticationManagerException authenticationManagerException)
 744  
             {
 745  
                 // result = false;
 746  0
                 dispatcherResult = DispatcherAuthenticationResult.FAILED;
 747  0
                 reason = "Could not access authentication manager to validate the user's credentials! ";
 748  0
                 exception = authenticationManagerException;
 749  
             }
 750  0
             catch (RuntimeException runtimeException)
 751  
             {
 752  
                 // result = false;
 753  0
                 dispatcherResult = DispatcherAuthenticationResult.FAILED;
 754  0
                 reason = null;
 755  0
                 exception = runtimeException;
 756  
             }
 757  0
             catch (ControllerException controllerException)
 758  
             {
 759  0
                 dispatcherResult = DispatcherAuthenticationResult.FAILED;
 760  0
                 reason = controllerException.getMessage();
 761  0
                 exception = controllerException;
 762  
             }
 763  
 
 764  0
             if (dispatcherResult != DispatcherAuthenticationResult.FAILED)
 765  
             {
 766  0
                 return dispatcherResult;
 767  
             }
 768  
             else
 769  
             {
 770  
                 ControllerException controllerException;
 771  0
                 if (exception instanceof ControllerException)
 772  
                 {
 773  0
                     controllerException = (ControllerException) exception;
 774  
                 }
 775  
                 else
 776  
                 {
 777  
 
 778  0
                     controllerException = new ControllerException(ControllerExecutionStep.DISPATCHER_AUTHENTICATION,
 779  0
                             reason, exception);
 780  
 
 781  0
                     controllerException.addToExceptionContext("Context", this.getContext());
 782  0
                     controllerException.addToExceptionContext("Supplied User ID", userID);
 783  
 
 784  0
                     if (suppliedPassword != null)
 785  0
                         controllerException.addToExceptionContext("Supplied User Password",
 786  0
                                 StringUtils.getRepeatedString("*", suppliedPassword.toString().length()));
 787  
                 }
 788  0
                 if (reason != null)
 789  
                 {
 790  
 
 791  
                     // A known error occurred.
 792  0
                     if (this.getStage().hasAuthenticationErrorInjection())
 793  
                     {
 794  
                         // If the stage has asked authentication error injection...
 795  
                         // capture, set it and proceed with no
 796  
                         // errors
 797  0
                         setAuthenticationException(controllerException);
 798  
 
 799  0
                         return dispatcherResult;
 800  
 
 801  
                     }
 802  
                     else
 803  
                         // else proceed with normal error launch...
 804  0
                         throw controllerException;
 805  
 
 806  
                 }
 807  
                 else
 808  
                 {
 809  0
                     throw controllerException;
 810  
                 }
 811  
             }
 812  
         }
 813  
 
 814  0
         return dispatcherResult;
 815  
     }
 816  
 
 817  
     /**
 818  
      * Post Authentication execution
 819  
      * 
 820  
      * @param result
 821  
      *            the DispatcherAuthenticationResult
 822  
      */
 823  
     protected void postAuthentication(DispatcherAuthenticationResult result)
 824  
     {
 825  0
         if (result != DispatcherAuthenticationResult.NO_ACTION)
 826  
         {
 827  
             // For each IAuthenticationSSO will execute the result action
 828  0
             for (Entry<String, IAuthenticationPlugin> entry: DIFIoCRegistry.getRegistry()
 829  0
                     .getImplementationsMap(IAuthenticationPlugin.class).entrySet())
 830  
             {
 831  
                 // The result action was a login, so will execute is after execution
 832  0
                 if (result == DispatcherAuthenticationResult.LOGIN)
 833  0
                     entry.getValue().doAfterLogin(this.getContext());
 834  
                 // The result action was a logout, so will execute is after execution
 835  0
                 else if (result == DispatcherAuthenticationResult.LOGOUT)
 836  0
                     entry.getValue().doAfterLogout(this.getContext());
 837  
             }
 838  
         }
 839  
 
 840  0
         String providerId = (String) this.getContext().getSession()
 841  0
                 .getAttribute(DIFSession.REMOTE_AUTHENTICATION_PROVIDER_LOGIN);
 842  
 
 843  
         /* The Remote Authentication Servlet publish that a authentication was performed by a external provider */
 844  0
         if (providerId != null)
 845  
         {
 846  0
             String title = getMessages().get("remoteAuthenticationLoginTitle").replace("${providerid}", providerId);
 847  0
             String text = getMessages().get("remoteAuthenticationLoginText").replace("${providerid}", providerId);
 848  
 
 849  0
             this.getContext().addResultMessage("warn", title, text, true);
 850  0
             this.getContext().getSession().addAttribute(DIFSession.REMOTE_AUTHENTICATION_PROVIDER_LOGIN, null);
 851  0
             this.getContext().getSession().addAttribute(DIFSession.REMOTE_AUTHENTICATION_PROVIDER_LOGOUT, providerId);
 852  
         }
 853  
 
 854  0
     }
 855  
 
 856  
     /**
 857  
      * Pre Authentication execution
 858  
      */
 859  
     protected void preAuthentication()
 860  
     {
 861  
         // For each IAuthenticationSSO will execute is before login or before logout action
 862  0
         for (Entry<String, IAuthenticationPlugin> entry: DIFIoCRegistry.getRegistry()
 863  0
                 .getImplementationsMap(IAuthenticationPlugin.class).entrySet())
 864  
         {
 865  
             // execute the before login sso login
 866  0
             entry.getValue().doBeforeLogin(this.getContext());
 867  
             // execute the before logout logic
 868  0
             entry.getValue().doBeforeLogout(this.getContext());
 869  
         }
 870  0
     }
 871  
 
 872  
     /**
 873  
      * Execute the dispatching cycle steps according to established rules for the business object. This method defines
 874  
      * the dispatching cycle steps order of execution. This sequence should not change and thus method overriding on
 875  
      * subclasses is forbidden by declaring the method <code>final</code>.
 876  
      * 
 877  
      * @param isRedirection
 878  
      * @return the executed stage instance
 879  
      * @throws BusinessFlowException
 880  
      *             when an exception is thrown by the stage's init/execute steps
 881  
      * @throws ControllerException
 882  
      *             when any runtime exception is thrown
 883  
      */
 884  
     @Trace(metricName = "DIF:Dispatcher", dispatcher = true)
 885  
     final private IStageInstance runDispatchingSteps(Boolean isRedirection) throws BusinessFlowException,
 886  
             ControllerException
 887  
     {
 888  0
         IStageInstance stageInstance = null;
 889  
 
 890  
         try
 891  
         {
 892  
             // Get an instance of the stage.
 893  0
             this.setStage(DEMRegistryImpl.getRegistry().getStage(this.getContext().getStage()));
 894  
 
 895  
             // MonitorUtil.detailLogEntry("DIF:Dispatcher", "Run: " + this.getStage().getID());
 896  
 
 897  
             // 1st: Validates the session and context. Checks if the
 898  
             // application/service/stage is registered
 899  
 
 900  0
             validate(isRedirection);
 901  
 
 902  
             // 2nd: Authenticate
 903  0
             authenticate();
 904  
 
 905  
             // Report NewRelic if in test or develop mode...
 906  0
             if (DIFStartupConfiguration.getTestingMode() || DIFStartupConfiguration.getDeveloperMode())
 907  
             {
 908  0
                 IDIFUser user = this.getContext().getSession().getUser();
 909  
 
 910  0
                 if (user != null)
 911  0
                     NewRelic.addCustomParameter("User", user.getID() + " [" + user.getName() + "]");
 912  
             }
 913  
 
 914  
             // 3rd: Authorize
 915  0
             if (this.getStage().hasAuthorization())
 916  0
                 authorize();
 917  
 
 918  
             // 4th: Execute business logic
 919  0
             stageInstance = execute();
 920  
 
 921  
             // 5th: End execution
 922  0
             conclude();
 923  
 
 924  0
             performCleanup(this.getContext(), true);
 925  
 
 926  
         }
 927  0
         catch (BusinessFlowException e)
 928  
         {
 929  0
             if (!handleException(e))
 930  0
                 throw e;
 931  
         }
 932  0
         catch (ControllerException e)
 933  
         {
 934  0
             if (!handleException(e))
 935  0
                 throw e;
 936  
         }
 937  0
         catch (Exception e)
 938  
         {
 939  0
             if (!handleException(e))
 940  0
                 throw new ControllerException(ControllerExecutionStep.DISPATCHER_EXECUTE, e);
 941  
         }
 942  
         finally
 943  0
         {
 944  
             // If the session isMarked for removal, it means that in the process of processing the current request (or
 945  
             // one of the redirection requests within, the session has timed out. Since the time out informs the user of
 946  
             // this process, we can get a new one.
 947  0
             if (this.getContext().getSession().isMarkedForRemoval())
 948  
             {
 949  0
                 this.getContext().setSession(
 950  0
                         theSessionManager.createSession(this.getContext().getSession().getSessionID()));
 951  0
                 theSessionManager.logOut(this.getContext().getSession().getSessionID());
 952  0
                 postAuthentication(DispatcherAuthenticationResult.LOGOUT);
 953  
             }
 954  
 
 955  0
         }
 956  
 
 957  0
         return stageInstance;
 958  
     }
 959  
 
 960  
     /**
 961  
      * @param authenticationException
 962  
      *            the authenticationException to set
 963  
      */
 964  
     public void setAuthenticationException(ControllerException authenticationException)
 965  
     {
 966  0
         this.authenticationException = authenticationException;
 967  0
     }
 968  
 
 969  
     /**
 970  
      * Modifier for the execution context.
 971  
      * 
 972  
      * @param newContext
 973  
      *            the new execution context to set
 974  
      */
 975  
     private void setContext(DIFContext newContext)
 976  
     {
 977  0
         this.difContext = newContext;
 978  0
     }
 979  
 
 980  
     /**
 981  
      * Modifier for the stage to execute.
 982  
      * 
 983  
      * @param newStage
 984  
      *            the new stage to execute
 985  
      */
 986  
     private void setStage(IStage newStage)
 987  
     {
 988  0
         this.theStage = newStage;
 989  0
     }
 990  
 
 991  
     /**
 992  
      * Validates the session and the stage. This method always runs. To avoid overriding on base classes it is declared
 993  
      * <code>final</code>. Session validation includes checking if the stage is active and hasn't timed out. Stage
 994  
      * validation includes checking if the stage is registrable and if so checks if the license is valid.
 995  
      * 
 996  
      * @param isRedirection
 997  
      * @throws ControllerException
 998  
      *             when any runtime exception is thrown
 999  
      */
 1000  
     @Trace(metricName = "DIF:Dispatcher:Validate", dispatcher = true)
 1001  
     final protected void validate(Boolean isRedirection) throws ControllerException
 1002  
     {
 1003  
         try
 1004  
         {
 1005  0
             if (this.getStage() == null)
 1006  
             {
 1007  
                 // the stage does not exist
 1008  0
                 ControllerException exception = new ControllerException(
 1009  0
                         ControllerExecutionStep.DISPATCHER_VALIDATE_REQUEST, INVALID_STAGE_ERROR_MESSAGE);
 1010  0
                 exception.addToExceptionContext("Stage", getContext().getStage());
 1011  
 
 1012  0
                 throw exception;
 1013  
             }
 1014  
 
 1015  0
             if (!isRedirection && this.getContext().getSession().hasTimedOut()
 1016  0
                     && this.getContext().getSession().isLogged()
 1017  0
                     && !"true".equals(this.getContext().getRequest().getParameter(IDIFRequest.LOGOUT_PARAMETER_ID))
 1018  0
                     && !this.getContext().getRequest().isAjaxMode() && !this.getContext().getRequest().isTemplateMode())
 1019  
             {
 1020  0
                 SessionTimeoutException exception = new SessionTimeoutException(
 1021  0
                         ControllerExecutionStep.DISPATCHER_VALIDATE_REQUEST);
 1022  0
                 exception.addToExceptionContext("Stage", getContext().getStage());
 1023  
 
 1024  0
                 ((IPrivateDIFSession) (this.getContext().getSession())).setMarkedForRemoval(true);
 1025  
 
 1026  0
                 throw exception;
 1027  
             }
 1028  
 
 1029  0
             Object invalidBrowserFlagAccepted = this.getContext().getSession()
 1030  0
                     .getAttribute(Constants.INVALID_BROWSER_ACCEPTED);
 1031  0
             Object clientValidations = this.getContext().getRequest()
 1032  0
                     .getAttribute(IDIFRequest.CLIENT_VALIDATIONS_ATTRIBUTE_ID);
 1033  0
             Boolean allowClientValidations = true;
 1034  0
             if (clientValidations != null)
 1035  
             {
 1036  0
                 allowClientValidations = (Boolean) clientValidations;
 1037  
             }
 1038  
 
 1039  0
             if (!this.getContext().getRequest().isComponentMode() && !this.getContext().getRequest().isTemplateMode()
 1040  0
                     && invalidBrowserFlagAccepted == null
 1041  0
                     && !this.getContext().getRequest().getClient().getSupportedBrowser()
 1042  0
                     && this.getContext().getStage() != "BrowserValidator" && allowClientValidations)
 1043  0
                 this.getContext().redirectTo("BrowserValidator");
 1044  
 
 1045  0
             if (!checkLicense())
 1046  
             {
 1047  0
                 LicenseViolationException exception = new LicenseViolationException(
 1048  0
                         ControllerExecutionStep.DISPATCHER_VALIDATE_REQUEST);
 1049  0
                 exception.addToExceptionContext("Stage", getContext().getStage());
 1050  
 
 1051  0
                 throw exception;
 1052  
             }
 1053  
         }
 1054  0
         catch (RuntimeException runtimeException)
 1055  
         {
 1056  0
             ControllerException controllerException = new ControllerException(
 1057  0
                     ControllerExecutionStep.DISPATCHER_VALIDATE_REQUEST, runtimeException);
 1058  
 
 1059  0
             controllerException.addToExceptionContext("Context", this.getContext());
 1060  0
             controllerException.addToExceptionContext("Stage", this.getContext());
 1061  
 
 1062  0
             throw controllerException;
 1063  
         }
 1064  0
     }
 1065  
 
 1066  
     /**
 1067  
      * Validate the user credentials
 1068  
      * 
 1069  
      * @param userId
 1070  
      *            the userId
 1071  
      * @param password
 1072  
      *            the password
 1073  
      * @return validation result
 1074  
      * @throws IdentityManagerException
 1075  
      *             if a IdentityManager Exception occurs.
 1076  
      * @throws ControllerException
 1077  
      */
 1078  
     protected boolean validateUserCredentials(String userId, String password) throws IdentityManagerException,
 1079  
             ControllerException
 1080  
     {
 1081  0
         boolean result = this.getIdentityManager().isIdentityValid(userId, password);
 1082  
 
 1083  0
         if (result)
 1084  
         {
 1085  
             // For each IAuthenticationPlugin will execute validateUser action
 1086  0
             for (Entry<String, IAuthenticationPlugin> entry: DIFIoCRegistry.getRegistry()
 1087  0
                     .getImplementationsMap(IAuthenticationPlugin.class).entrySet())
 1088  
             {
 1089  
                 // all the authentication plugin's must return true, so that the authentication proceed
 1090  0
                 result = entry.getValue().validateUser(this.getContext(), userId, password);
 1091  0
                 if (!result)
 1092  
                 {
 1093  0
                     break;
 1094  
                 }
 1095  
             }
 1096  
         }
 1097  
 
 1098  0
         return result;
 1099  
     }
 1100  
 }