Kuali Rice Development
  1. Kuali Rice Development
  2. KULRICE-8946

A few small things that can improve KRAD performance


    • Type: Improvement Improvement
    • Status: Closed Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: 2.2
    • Fix Version/s: 2.3.1
    • Security Level: Public (Public: Anyone can view)
    • Labels:
    • Similar issues:
      KULRICE-9597Script logic performance pass
      KULRICE-10260Analyze performance in targeted KRAD views
      KULRICE-12476KRAD performance issues with OLE
      KULRICE-3515Performance improvements to creation of ValidActionsDTO
      KULRICE-14239Performance improvements of AFT tests
      KULRICE-8918Improved KRAD collections performance
      KULRICE-1130improve performance of xml ingestion
      KULRICE-8324Performance improvements for totalling and group totalling
      KULRICE-14090Make improvements to KIM integration performance
      KULRICE-6670Rice - KRAD Performance Improvements
    • Epic Link:
    • Rice Module:
    • KRAD Feature Area:
      UIF MVC
    • Application Requirement:
    • Sprint:
      2.3.1 Sprint 2
    • KAI Review Status:
      Not Required
    • KTI Review Status:
      Not Required


      1 - make the parser static. It's supposedly threadsafe.

          protected static ExpressionParser parser = new SpelExpressionParser();

      2 - add custom functions is expensive and is called over and over.
      use static variables to store all the methods registered:

          private static Method isAssignableFrom;
          private static Method empty;
          private static Method emptyList;
          private static Method listContains;
          private static Method getName;
          private static Method getParm;
          private static Method getParmInd;
          private static Method hasPerm;
          private static Method hasPermDtls;
          private static Method hasPermTmpl;
          private static Method sequence;
                  isAssignableFrom = ExpressionFunctions.class.getDeclaredMethod("isAssignableFrom", new Class[]{Class.class, Class.class});
                  empty = ExpressionFunctions.class.getDeclaredMethod("empty", new Class[]{Object.class});
                  emptyList = ExpressionFunctions.class.getDeclaredMethod("emptyList", new Class[]{List.class});
                  listContains = ExpressionFunctions.class.getDeclaredMethod("listContains", new Class[]{List.class, Object[].class});
                  getName = ExpressionFunctions.class.getDeclaredMethod("getName", new Class[]{Class.class});
                  getParm = ExpressionFunctions.class.getDeclaredMethod("getParm", new Class[]{String.class, String.class, String.class});
                  getParmInd = ExpressionFunctions.class.getDeclaredMethod("getParmInd", new Class[]{String.class, String.class, String.class});
                  hasPerm = ExpressionFunctions.class.getDeclaredMethod("hasPerm", new Class[]{String.class, String.class});
                  hasPermDtls = ExpressionFunctions.class.getDeclaredMethod("hasPermDtls", new Class[]{String.class, String.class, Map.class, Map.class});
                  hasPermTmpl = ExpressionFunctions.class.getDeclaredMethod("hasPermTmpl", new Class[]{String.class, String.class, Map.class, Map.class});
                  sequence = ExpressionFunctions.class.getDeclaredMethod("sequence", new Class[]{String.class});
              }catch(NoSuchMethodException e){
                  LOG.error("Custom function for el expressions not found: " + e.getMessage());
                  throw new RuntimeException("Custom function for el expressions not found: " + e.getMessage(), e);
          protected void addCustomFunctions(StandardEvaluationContext context) {
                  // TODO: possibly reflect ExpressionFunctions and add automatically
              context.registerFunction("isAssignableFrom", isAssignableFrom);
              context.registerFunction("empty", empty);
              context.registerFunction("emptyList", emptyList);
              context.registerFunction("listContains", listContains);
              context.registerFunction("getName", getName);
              context.registerFunction("getParm", getParm);
              context.registerFunction("getParmInd", getParmInd);
              context.registerFunction("hasPerm", hasPerm);
              context.registerFunction("hasPermDtls", hasPermDtls);
              context.registerFunction("hasPermTmpl", hasPermTmpl);
              context.registerFunction("sequence", sequence);

      3 - creating evaluation context multiple times is expensive, especially if it does not change.
      ViewHelperServiceImpl.performComponentApplyModel calls evaluateExpressionsOnConfigurable multiple times within loops and recreates the same context instance each time.
      consider pulling out the logic to create a context so that the context instance can be reused.

       protected void performComponentApplyModel(View view, Component component, Object model,
                  Map<String, Integer> visitedIds) {
              if (component == null) {
              // set context on component for evaluating expressions
              component.pushAllToContext(getCommonContext(view, component));
              StandardEvaluationContext context = getExpressionEvaluatorService().getContext(model,component.getContext());
              for (PropertyReplacer replacer : component.getPropertyReplacers()) {
                  getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, replacer, context);
              for (ComponentModifier modifier : component.getComponentModifiers()) {
                  getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, modifier, context);
              getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, component, context);
              // evaluate expressions on component security
              ComponentSecurity componentSecurity = component.getComponentSecurity();
              getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, componentSecurity, context);
              // evaluate expressions on the binding info object
              if (component instanceof DataBinding) {
                  BindingInfo bindingInfo = ((DataBinding) component).getBindingInfo();
                  getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, bindingInfo, context);
              // set context evaluate expressions on the layout manager
              if (component instanceof Container) {
                  LayoutManager layoutManager = ((Container) component).getLayoutManager();
                  if (layoutManager != null) {
                      layoutManager.getContext().putAll(getCommonContext(view, component));
                      layoutManager.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, component);
                      layoutManager.pushObjectToContext(UifConstants.ContextVariableNames.MANAGER, layoutManager);
                      getExpressionEvaluatorService().evaluateExpressionsOnConfigurable(view, layoutManager, model,
                      layoutManager.setId(adjustIdIfNecessary(layoutManager.getId(), visitedIds));
              // sync the component with previous client side state
              syncClientSideStateForComponent(component, ((ViewModel) model).getClientStateForSyncing());
              // invoke authorizer and presentation controller to set component state
              applyAuthorizationAndPresentationLogic(view, component, (ViewModel) model);
              // adjust ids for duplicates if necessary
              //component.setId(adjustIdIfNecessary(component.getId(), visitedIds));
              // invoke component to perform its conditional logic
              Component parent = (Component) component.getContext().get(UifConstants.ContextVariableNames.PARENT);
              component.performApplyModel(view, model, parent);
              // invoke service override hook
              performCustomApplyModel(view, component, model);
              // invoke component modifiers configured to run in the apply model phase
              runComponentModifiers(view, component, model, UifConstants.ViewPhases.APPLY_MODEL);
              // get children and recursively perform conditional logic
              for (Component nestedComponent : component.getComponentsForLifecycle()) {
                  if (nestedComponent != null) {
                      nestedComponent.pushObjectToContext(UifConstants.ContextVariableNames.PARENT, component);
                      performComponentApplyModel(view, nestedComponent, model, visitedIds);

      This class is called dozens of times for each page, especially if there are collection controllers.

        Issue Links


          Brian Smith (Inactive) added a comment -

          I dont think the 3rd suggestion applies anymore

          Brian Smith (Inactive) added a comment - I dont think the 3rd suggestion applies anymore


            • Assignee:
              Brian Smith (Inactive)
              Daniel Epstein (Inactive)
            • Votes:
              0 Vote for this issue
              1 Start watching this issue


              • Created:

                Time Tracking

                Original Estimate - 1 hour
                Remaining Estimate - 0 minutes
                Time Spent - 1 hour


                    Structure Helper Panel