0

I am trying to execute this Apex code but getting view state size of 196 KB and Heap size error. I tried to find where the bulk of view state is going to and it seems to be this controller.

Could someone please let me know how we could improve this controller

global virtual with sharing class ReportController {

  public ReportController() {
    this(true);
  }
  public ReportController(boolean includeAccountFilters) {
    this.includeAccountFilters = includeAccountFilters;
    // Paul Kreis: 6/6/14: Added Hierarchy fields, removed Country field 
    addFilter(new ReportFilters.TerritoryFilter(this, 'Calculated_Area_Text__c', system.label.Area, 'Area__c'), 'Territory');
    addFilter(new ReportFilters.TerritoryFilter(this, 'Hierarchy_Region_Text__c', 'Region', 'Region__c'), 'Territory');
    addFilter(new ReportFilters.TerritoryFilter(this, 'Hierarchy_Parent_Territory_Text__c', 'Country or Sub Region', 'Parent_Territory__c'), 'Territory');
    addFilter(new ReportFilters.TerritoryFilter(this, 'Sales_Region_Text__c', 'Sales Region', 'Sales_Region__c'), 'Territory');
    addFilter(new ReportFilters.TerritoryFilter(this, 'Sales_District_Text__c', 'Sales District', 'Sales_District__c'), 'Territory');
    addFilter(new ReportFilters.TerritoryFilter(this, 'Territory_Name_and_Owner__c', 'Territory', 'Territory__c'), 'Territory');
    if (includeAccountFilters) {
      addFilter(new ReportFilters.PicklistFilter(this, 'Account', 'Account_Classification__c', 'Account Classification', true, 'Account_Classification__c'), 'Account');
      addFilter(new ReportFilters.AccountFilter(this, 'Account_Name_And_Owner__c', 'Account'), 'Account');
    }
    if (ApexPages.currentPage() != null) {
      /*  String browserType = ApexPages.currentPage().getHeaders().get('USER-AGENT'); //gets the browser name
        if (browserType != null && browserType.contains('MSIE')) {
          ApexPages.currentPage().getHeaders().put('X-UA-Compatible', 'IE=8');
        }
      */
      Map<String, String> params = ApexPages.currentPage().getParameters();

      if (params.keyset().isEmpty() == false) {

        // make sure all select options get populated before we filter them
        for (ReportFilter filter : filters) {
          //system.assertequals('Params : ',string.valueOf(filter));
          filter.getSelectOptions();
        }

        // now go through and set selected values from the URL. Filters through list & not params to ensure we do things in order
        for (ReportFilter filter : filters) {
          String encodedKey  = filter.label;//EncodingUtil.urlEncode( filter.label, 'UTF-8');
          if (params.containsKey(encodedKey)) {
            if (filter.encodeValuesInSelect) {
              filter.selectedValue = EncodingUtil.urlEncode(params.get(encodedKey), 'UTF-8');
            } else {
              filter.selectedValue = EncodingUtil.urlDecode(params.get(encodedKey), 'UTF-8');
            }
            filter.refreshDependentFilters(true);
          }
        }
      }
    }
  }
  public boolean isReportingEnabled {
    get {
      return [select Enable_Reporting__c from user where id = :Userinfo.getUserId()].Enable_Reporting__c;
    }
  }
  public boolean includeAccountFilters {get; set;}
  public ReportController getThis() {
    return this;
  }

  public string ReportName {
    get {return (Report != null ? report.name : '');}
  }
  public string FilterSectionName {
    get {return (HideInPdf || hasPopulatedFilters ? System.Label.Filter : '');}
  }
  public string ReportSectionName {
    get {return (HideInPdf ? ReportName : '');}
  }

  public integer RecordLimit {
    get {
      if (test.isRunningTest()) {
        return 500; //artificial limit since apex:page read-only flag doesn't translate to unit tests
      } else {
        return (Report != null && Report.Record_Limit__c != null ? Integer.ValueOf(Report.Record_Limit__c) : 0);
      }
    }
  }

  public integer RecordLimitWithDefault {
    get {return (RecordLimit > 0 ? RecordLimit : 2000);}
  }

  public string RecordLimitString {
    get {return (RecordLimit > 0 ? ' LIMIT ' + RecordLimit : '');}
  }

  public List<string> Log {
    get {
      if (Log == null) {
        Log = new List<string>();
      }
      return Log;
    }
    private set;
  }
  public list<String> GroupBy {
    get {
      if (GroupBy == null) {
        GroupBy = new List<String>();
      }
      return GroupBy;
    }
    protected set;
  }

  public list<SelectOption> getGroupByOptions() {
    list<selectOption> options = new List<selectOption>();
    for (string g : groupBy) {
      options.add(new SelectOption(g, g));
    }
    return options;
  }

  public boolean hasGroupBy {
    get { return GroupBy.isEmpty() == false;}
  }

  public string selectedGroupBy {get; set;}

  public list<string> keyList {
    get {
      if (keyList == null) {
        keyList = new String[] {'Territory', 'Account'};
      }

      return keyList;
    }
    private set;
  }

  public map<string, ReportFilter> filtersByName {
    get {
      if (filtersByName == null) {
        filtersByName = new Map<string, ReportFilter>();
      }
      return filtersByName;
    }
    private set;
  }
  public map<string, list<ReportFilter>> filtersByType {
    get{
      if (filtersByType == null) {
        filtersByType = new Map<String, List<ReportFilter>>();
      }
      return filtersByType;
    }
    private set;
  }
  public list<ReportFilter> filters {
    get {
      if ( filters == null) {
        filters = new List<ReportFilter>();
      }
      return filters;
    }
    private set;
  }

  public boolean hasPopulatedFilters {
    get {return populatedFilters.IsEmpty() == false;}
  }

  public list<ReportFilter> populatedFilters {
    get {
      List<ReportFilter> pFilters = new List<ReportFilter>();
      for (ReportFilter filter : filters) {
        if (filter.hasFilter) {
          pFilters.add(filter);
        }
      }
      return pFilters;
    }
  }
  public Report__c report {
    get {
      if (report == null && ApexPages.currentPage() != null) {
        String[] url = ApexPages.currentPage().getUrl().replaceFirst('/apex/', '').split('\\?');

        list<report__c> reports = [select id, name, record_limit__c, enable_debug__c,
        report_type__r.area__c, report_type__r.country__c, report_type__r.sales_region__c,
        report_type__r.sales_district__c, report_type__r.territory__c, report_type__r.account_classification__c,
        report_type__r.account__c, report_type__r.owner__c, report_type__r.assigned_username__c
        from report__c
        where VF_Page__c = :url[0] LIMIT 1];

        if (reports.IsEmpty() == false) {
          report = reports[0];
        }
      }
      return report;
    }
    private set;
  }

  public string getVisualForceParameters() {
    string params = '';
    for (String key : filtersByName.keyset()) {
      ReportFilter filter = filtersByName.get(key);
      string selectedValue = filter.decodedSelectedValue;
      if (selectedValue != null && selectedValue != '') {
        if (params == '') {
          params = '?';
        } else {
          params += '&';
        }

        params += EncodingUtil.urlEncode( key, 'UTF-8' ) + '=' + EncodingUtil.urlEncode( selectedValue, 'UTF-8');
      }
    }
    return params;
  }

  public string getStandardReportParameters(Report__c report) {
    list<string> params = new list<string>();
    string returnParam = '';
    if (AccountCriteria != '') {
      SOQLBuilder sb = AccountSOQLBuilder.clone();
      sb.RowLimit = 1;
      sb.addField('Owner_Name__c');
      sb.addField('Owner_Alias__c');
      sb.addField('Owner_Username__c'); // need Username instead of Alias
      List<SObject> accounts = database.query(sb.getSOQL());

      if (report.report_type__r.owner__c && accounts != null && accounts.IsEmpty() == false &&
          (filtersByName.get('Territory').selectedValue != null || filtersByName.get('Account').selectedValue != null)) {
        //params.add('pc9=ASSIGNED_ALIAS&pn9=eq&pv9=' + accounts[0].get('Owner_Alias__c')); // original line
        // Paul Kreis: 5/22/14: changed from using Alias
        params.add('pc9=' + report.getSObject('Report_Type__r').get('Assigned_Username__c') + '&pn9=eq&pv9=' + accounts[0].get('Owner_Username__c'));   
      }

      integer idx = 10;
      for (ReportFilter filter : filters) {
        if (filter.decodedselectedValue != null && filter.parameterField != null
            &&  report.getSObject('Report_Type__r') != null &&  report.getSObject('Report_Type__r').get(filter.parameterField) != null) {
          params.add('pc' + idx + '=' + report.getSObject('Report_Type__r').get(filter.parameterField)
                     + '&pn' + idx + '=' + filter.parameterOperator + '&pv' + idx
                     + '=' +  EncodingUtil.urlEncode( '"' + filter.decodedSelectedValue + '"', 'UTF-8'));
          idx += 1;
        }
      }

      if (params.IsEmpty() == false) {
        returnParam = '?' + String.join(params, '&');
      }
    }
    return returnParam;
  }

  public string AccountCriteria {
    get {
      string criteria = AccountSOQLBuilder.getCriteria(false);
      Log.add(criteria);
      return criteria;
    }
    set;
  }
  public string AccountIDInCriteria {
    get {
      return  'AccountId in (SELECT ID FROM Account' + (accountCriteria != null && AccountCriteria != '' ? ' WHERE ' + accountCriteria  : '') + ') ';
    }
  }

  public string CustomAccountIDInCriteria {
    get {
      return  'Account__c in (SELECT ID FROM Account' + (accountCriteria != null && AccountCriteria != '' ? ' WHERE ' + accountCriteria  : '') + ') ';
    }
  }
  public SOQLBuilder TerritorySOQLBuilder {
    get {
      TerritorySOQLBuilder = new SOQLBuilder('Territory__c', false);
      if (includeAccountFilters) TerritorySOQLBuilder.addCriteria('id in :availableTerritoryIds', true);
      List<ReportFilter> territoryFilters =  filtersByType.get('Territory');
      for (ReportFilter f : territoryFilters) {
        ReportFilters.TerritoryFilter tf = (ReportFilters.TerritoryFilter) f;
        tf.updateSOQLBuilder(TerritorySOQLBuilder, false, null);
      }
      if (test.isRunningTest()) TerritorySOQLBuilder.rowLimit = 500; //artificial limit since apex:page read-only flag doesn't translate to unit tests

      return TerritorySOQLBuilder;
    }
    private set;
  }
  public string TerritoryCriteria {
    get {
      return TerritorySOQLBuilder.getCriteria(false);
    }
    set;
  }
  public Set<Id> TerritoryIds {
    get {
      SoqlBuilder sb = TerritorySOQLBuilder;
      sb.addField('Id');
      return sCollection.idSet(Database.query(sb.getSoql()), 'Id');
    }
  }
  public list<aggregateResult> filterRecords {
    get {
      if (filterRecords == null) {
        SOQLBuilder sb = new SOQLBuilder('Territory__c', false);
        if (includeAccountFilters) sb.addCriteria('id in :availableTerritoryIds', true);
        List<ReportFilter> territoryFilters =  filtersByType.get('Territory');
        for (ReportFilter f : territoryFilters) {
          ReportFilters.TerritoryFilter tf = (ReportFilters.TerritoryFilter) f;
          tf.updateSOQLBuilder(sb, false, null);
        }

        AggregateResult counts = database.query(sb.getUniqueCounts());
        sb = new SOQLBuilder('Territory__c', false);
        if (includeAccountFilters) sb.addCriteria('id in :availableTerritoryIds', true);
        for (ReportFilter f : territoryFilters) {
          ReportFilters.TerritoryFilter tf = (ReportFilters.TerritoryFilter) f;
          tf.updateSOQLBuilder(sb, counts, null);
        }

        if (test.isRunningTest()) sb.rowLimit = 500; //artificial limit since apex:page read-only flag doesn't translate to unit tests
        filterRecords = database.query(sb.getSOQL());
      }

      return filterRecords;
    }
    public set;
  }

  private transient Set<id> mAvailableTerritoryIds;
  public Set<id> availableTerritoryIds {
    get {
      if (mAvailableTerritoryIds == null) {
        mAvailableTerritoryIds = sCollection.idSet([select territory__c from account group by territory__c], 'territory__c');
      }
      return mAvailableTerritoryIds;
    }
    private set {mAvailableTerritoryIds = value;}
  }
  public SOQLBuilder accountSOQLBuilder {
    get {
      if (accountSOQLBuilder == null) {
        accountSOQLBuilder = new SOQLBuilder('Account', true);
        accountSOQLBuilder.rowlimit = 1000;
        accountSOQLBuilder.addSortField('Account_Name_And_Owner__c');
        accountSOQLBuilder.addCriteria('RecordType.Name!=\'Competitor\'', true);
        List<ReportFilter> territoryFilters =  filtersByType.get('Territory');
        for (ReportFilter f : territoryFilters) {
          ReportFilters.TerritoryFilter tf = (ReportFilters.TerritoryFilter) f;
          tf.updateSOQLBuilder(accountSOQLBuilder, false, 'Territory__r');
        }

        List<ReportFilter> accountFilters = (list<ReportFilter>)filtersByType.get('Account');
        for (ReportFilter f : accountFilters) {
          f.updateSOQLBuilder(accountSOQLBuilder);
        }

        if (test.isRunningTest()) accountSOQLBuilder.rowLimit = 500; //artificial limit since apex:page read-only flag doesn't translate to unit tests

      }
      return accountSOQLBuilder;
    }
    public set;
  }
  public integer AccountCount {
    get {
      if (AccountCount == null) {
        AccountCount = Database.countQuery(AccountSOQLBuilder.getCountSOQL());
      }
      return AccountCount;
    }
    public set;
  }

  private transient  list<Sobject> mAccounts;
  public list<Sobject> Accounts {
    get {
      if (mAccounts == null) {
        mAccounts = new list<Sobject>();

        if (AccountCount <= 1000) {
          mAccounts = database.query(accountSOQLBuilder.getSOQL());
        }
      }
      return mAccounts;
    }
    public set {mAccounts = value;}
  }

  public set<id> AccountIds {
    get { return sCollection.idSet(Accounts, 'Id');}
  }

  public String getUserCurrency() {
    return UserInfo.getDefaultCurrency();
  }
  protected integer offset {
    get {
      if (offset == null) {
        offset = 0;
      }
      return offset;
    }
    private set;
  }

  public String OffsetString {
    get {
      if (offset > 0) {
        offsetString = ' OFFSET ' + offset;
      } else {
        offsetString = '';
      }
      return offsetString;
    }
    private set;
  }

  public PageReference getNextPage() {
    offset += RecordLimit;
    return refreshReportData(false);
  }

  public PageReference getPreviousPage() {
    offset -= RecordLimit;
    return refreshReportData(false);
  }

  public virtual PageReference refreshReportData(boolean resetOffset) {
    if (resetOffset) {
      offset = 0;
    }

    return null;
  }

  public virtual PageReference refreshReportData() {
    return refreshReportData(true);
  }

  public string RenderAs {
    get {
      if (ApexPages.currentPage() != null && ApexPages.currentPage().getParameters().containsKey('RenderAs')) {
        RenderAs = ApexPages.currentPage().getParameters().get('RenderAs');
      } else {
        RenderAs = '';
      }

      return RenderAs;
    }
    private set;
  }

  public boolean hideInPdf {
    get {
      return RenderAs != 'pdf';
    }
  }
  public boolean showInPdf {
    get {
      return RenderAs == 'pdf';
    }
  }

  public string style {
    get { return (showInPdf ? pdfStyle : displayStyle);}
  }

  public string pdfStyle {
    get {
      return  '<head>' +
      '  <style> ' +
      '       @page{size: landscape; ' +
      '           @bottom-right {font-family: "Arial Unicode MS"; font-size:10px;content: counter(page) " of " counter(pages); }' +
      '       } ' +
      '       tr td {  page-break-inside: avoid;} ' +
      '       .report {width:100%;border-collapse:collapse;} ' +
      '       .report tr th, .report tr td {border: 1px solid black;}' +
      '       .center {text-align:center!important;}' +
      '       .left {text-align:left!important;}' +
      '       .right {text-align:right!important;}' +
      '       body { font-family: "Arial Unicode MS"; font-size:10px;} ' +
      '       .report tr th, .bold {font-weight:bold;font-family: "Arial" "Sans-Serif";}' +
      '       .cHeader {font-family: "Arial" "Sans-Serif";background:#538DD5!important;color:white!important;font-weight:bold!important;text-align:center!important;}' +
      '       .pdfFilter {font-weight:bold;} ' +
      '       a { text-decoration: none;color:black;} ' +
      '   </style>' +
      '</head>';
    }
  }

  public string displayStyle {
    get {
      return  '<style>' +
      '.filter {width:300px;} ' +
      '.filterLabel { font-weight:bold;} ' +
      '.Custom18Tab .tertiaryPalette {background-color:#549117;} ' +
      ' .cHeader, .cHeader td, .cheader th, td.cHeader, th.cHeader { ' +
      '            background-color:#538DD5!important;' +
      '            color:white!important;' +
      '            font-weight:550!important;' +
      '            vertical-align:middle!important;' +
      '            text-align:center!important;' +
      '          }' +
      ' .updateDialog {height:200px;} ' +
      '</style>';
    }
  }
  public virtual PageReference getPdfPage() {
    PageReference page;

    if (ApexPages.currentPage() != null) {
      page = ApexPages.currentPage();
      page.getParameters().put('RenderAs', 'pdf');
    }
    return page;

  }
  protected ReportFilter addFilter(ReportFilter newFilter, String filterType) {
    if (filtersByType.containsKey(filterType) == false) {
      filtersByType.put(filterType, new List<ReportFilter>());
    }
    filtersByType.get(filterType).add(newFilter);
    filtersByName.put(newFilter.label, newFilter);
    filters.add(newFilter);
    return newFilter;
  }

  public PageReference refresh(Integer index, boolean rebuildAllFilters) {
    if (filters != null && index < filters.size() ) {
      if (filters[index].refreshFilterRecords) {
        refreshFilters();
      }
      for (integer idx = index + 1; idx < filters.size(); idx++) {
        ReportFilter filter = filters[idx];
        filter.refreshFilter();

        if (filter.refreshFilterRecords) {
          refreshFilters();
        }

        if (rebuildAlLFilters) {
          filter.getSelectOptions();
        }

      }
    }
    return null;
  }
  public void refreshFilters() {
    filterRecords = null;
    accountSOQLBuilder = null;
    AccountCount = null;
    Accounts = null;
  }

}
  • Dude, edit this down. If you want to include the whole class, put it on github or something similar and link to it. It's impossible to read and you will get downvoted for this. – Caspar Harmer Jul 30 '14 at 19:48
  • It is a very large class, but a little context would help as well. To try to just dive in an infer everything the class does is a bit much and most people will skip over it. Add a paragraph or two about what the class does or is supposed to do, so we at least have a starting point. – Chris Duncombe Jul 30 '14 at 19:49
  • 1
    Have you tried using the viewstate explorer? This will enable you to see what variables contain the most data and then take steps to cut their size. – Caspar Harmer Jul 30 '14 at 19:55
2

As @CaspNZ pointed out, the first and foremost option to debug the heap issue is to view the viewstate explorer. There are some other important tweaks you can do on your code such as,

  1. Try to covert the variables which are not maintaining the state to Transient variables. This would definitely give you code some breath.

  2. Limit your results that would be displayed on the page and try alternative approach to slice down the data by giving various filter options for the user to select. Notes on reduce heap size.

  3. If none of the approach works, try to go for javascript remoting where you can build the content in javascript and writing it to the DOM. This approach is lightning fast and better scale.

|improve this answer|||||

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.