I have below snippet of code which gets all the available menu items for a zone(top or left)and adds the widgets for which user has access to an ArrayList collection. For now the getWidgetsByZone(zone) returns 64 items and iterates over them. Am seeing some performance lag in this method(in a tool called GlowRoot which logs the time trace for each user action) I dont know why. Am trying to improve the performance by switching to other optimal collection. Can someone help me to choose the optimal collection for my scenario?
AM using JDK 7, Hibernate 3.6 and Spring 3.1
Here is DashboardService.getMenuItems() implementation
public List<IContentWidget> getMenuItems(String zone) {
List<IContentWidget> widgets = new ArrayList<IContentWidget>();
if (zone != null) {
List<IPersistentEntityInstance> instances = getWidgetsByZone(zone);
for (IPersistentEntityInstance instance : instances) {
IContentWidget contentWidget = (IContentWidget) instance;
if (contentWidget.getZones() == null) continue;
// block widgets that should only show up in mobile / responsive ui
if (contentWidget.getZones().contains(RESPONSIVE_VISIBLE)) continue;
// Add widget only if the current user has read permission on the entity.
if (contentWidget.getTargetItemScreen() != null || contentWidget.getTargetListScreen() != null) {
if (isAccessible(contentWidget)) {
widgets.add(contentWidget);
}
}
else {
widgets.add(contentWidget);
}
}
}
Collections.sort(widgets, new Comparator<IContentWidget>() {
public int compare(IContentWidget o1, IContentWidget o2) {
int i = o1.getOrderNum() - o2.getOrderNum();
return i == 0 ? 0 : i < 0 ? -1 : 1;
}
});
return widgets;
}
Implementation of DashboardService.isAccesible()
private boolean isAccessible(IContentWidget contentWidget) {
boolean isWidgetAccessible = false;
String permission = contentWidget.getDisplayPermission();
if (permission != null) {
isWidgetAccessible = authorizationService.userHasPermission(SecurityHelper.getAuthenticatedUser(),
permission);
}
else {
IBaseScreen screen = contentWidget.getTargetItemScreen() == null ? contentWidget.getTargetListScreen()
: contentWidget.getTargetItemScreen();
// return true when target screen is 'null', this means that target link cannot be secured because it is not
// associated with any entity
if (screen == null) {
isWidgetAccessible = true;
}
else {
IAccessEntry access = authorizationService.getAccessForEntityMetadata(screen.getEntityMetadata());
// fetching metadata from entityMetadataService again to trigger population of facade
if (screen instanceof IListScreen && access.getIsReadable()) {
isWidgetAccessible = true;
}
else if (screen instanceof IItemScreen && access.getIsCreatable()) {
isWidgetAccessible = true;
}
}
}
return isWidgetAccessible;
}
Implementation of getWidgetsByZone method
public List<IPersistentEntityInstance> getWidgetsByZone(String zone) {
IEntityMetadata entity = entityService.findEntityMetadataByName(ContentWidget.class.getSimpleName());
return entityService.runNamedQuery(entity, NamedQueryList.WIDGETS_BY_ZONE, new Object[] { zone });
}
Here is my ContentWidget Entity
@LocalOnly
@Entity
@EntityMetadataDefaults(editable = false)
@Audited
@NamedQueries({
@NamedQuery(name = NamedQueryList.DASHBOARD_WIDGETS, query = "from ContentWidget where zones like '%dashboard%' and dashboardContexts.size = 0 order by orderNum", hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Metadata") }),
@NamedQuery(name = NamedQueryList.WIDGETS_BY_ZONE, query = "from ContentWidget where zones like '%' || ? || '%' order by orderNum", hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Metadata") }),
@NamedQuery(name = NamedQueryList.WIDGETS_BY_ZONE_ORDER_BY_NAME, query = "from ContentWidget where zones like '%' || ? || '%' order by displayName", hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Metadata") }),
@NamedQuery(name = NamedQueryList.WIDGETS_BY_DASHBOARD_URL, query = "from ContentWidget where dashboardUrl like ? || '%'", hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Metadata") }),
@NamedQuery(name = NamedQueryList.WIDGETS_BY_NAME, query = "from ContentWidget where name = ?", hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Metadata") }),
@NamedQuery(name = NamedQueryList.WIDGETS_BY_CONTEXT, query = "from ContentWidget where zones like '%dashboard%' and ? in elements(dashboardContexts) order by orderNum", hints = {
@QueryHint(name = "org.hibernate.cacheable", value = "true"),
@QueryHint(name = "org.hibernate.cacheRegion", value = "Metadata") }) })
@Cacheable
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE, region = "Metadata")
@EventDefaults({
@EventHandlerDefaults(beanName = "contentWidgetPermissionCommand", eventType = EventTypeEnum.OnBeforeCreate),
@EventHandlerDefaults(beanName = "contentWidgetPermissionCommand", eventType = EventTypeEnum.OnBeforeUpdate) })
@ReadPermission(name = AuthorizationService.ANONYMOUS_PERMISSION)
public class ContentWidget implements IContentWidget {
private static final long serialVersionUID = 1680304771254400928L;
private String packageGuid;
private Long id;
private String name; // unique name to reconcile imported data
private String displayName; // used by UI
private String description; // anchor title attribute
private int orderNum; // universal ordering
private String zones; // csv: "top,left,dashboard,context,..."
private String iconClass;
private String displayPermission;
// menu settings
private IContentWidget parent;
private String targetUrl;
private IListScreen targetListScreen;
private IItemScreen targetItemScreen;
private boolean isCacheable;
private boolean isDivider;
private boolean isPopup;
private List<IEntityMetadata> contextEntities; // for contextual menus
protected IFilterDefinition entityFilterDefinition;
// dashboard settings
private int dashboardWidth = 1;
private String dashboardUrl;
private String dashboardWidgetType;
private IListScreen dashboardListScreen;
private IItemScreen dashboardItemScreen;
private List<IEntityMetadata> dashboardContexts; // for item screen dashboards
private ISessionService sessionService;
@Autowired
private IPassportContextService passportContextService;
@Autowired
private IReportingConfiguration reportingConfiguration;
private Timestamp createdAt;
private Timestamp updatedAt;
private ICustomNamedQuery menuCountQuery;
private Set<IPassportContext> passportContexts;
}
Performance trace Update:
The method performance trace in GlowRoot is as below
60.0% com.dc.core.presentation.presenter.impl.WebContentPresenter.getMenuHTML(WebContentPresenter.java:435)
50.0% com.dc.core.presentation.service.impl.DashboardService.getMenuItems(DashboardService.java:258)
30.0% com.dc.core.presentation.service.impl.DashboardService.isAccessible(DashboardService.java:382)
Here is my WebContentPresenter.getMenuHTML() implementation
public String getMenuHTML(String baseUrl, String zone, String cssClass, IPersistentEntityInstance entityInstance) {
(line 435) List<IContentWidget> instances = dashboardService.getMenuItems(zone);
StringBuffer html = new StringBuffer();
if (instances == null || instances.isEmpty()) {
html.append(" ");
}
else {
Map<Long, List<IContentWidget>> treeData = new HashMap<Long, List<IContentWidget>>();
for (IContentWidget instance : instances) {
BeanWrapperImpl bean = new BeanWrapperImpl(instance);
Object parent = bean.getPropertyValue("parent");
Long parentId = -1L;
if (passportContextService.getIsInContext(instance)) {
if (parent != null) {
parentId = ((IContentWidget) parent).getId();
}
List<IContentWidget> children = treeData.get(parentId);
if (children == null) {
children = new ArrayList<IContentWidget>();
}
children.add(instance);
treeData.put(parentId, children);
}
}
generateTreeHtml(html, treeData, -1L, baseUrl, "parent", entityInstance,
authorizationService.userHasAdminPermission(SecurityHelper.getAuthenticatedUser()));
}
return String.format("<ul class=\"%s\">%s</ul>", cssClass, html.toString());
}