IntroductionAs the design page suggests we have a problem with uniquely identifying resources in the search results in a user friendly way (i.e. not by id but rather by context). On various places in the UI different approaches were taken to mitigate this or the problem was just ignored. Also on different UI pages, different contextual data (apart from the name, type and parent) need to be displayed according to the purpose of each page. So in another words we need a mechanism of updating search results of unknown "format" with disambiguation data in such a way that would require minimal updates to the existing APIs and UI pages. SolutionOne way of doing things was to update each JPQL/native query we do to contain the disambiguating data and update all the SLSBs and UIBeans to use those instead. The problem with this approach is that the queries are usually used in a number of situations not all of which need disambiguation data, so we would have to "fork" the queries, the SLSB methods using them and update the users of the original method to use the new alternative if needed. Another problem is that of the legacy Struts-based UI that we have conserved and don't want to touch unless absolutely necessary. This would need to be updated in non-trivial ways to support the new data format. The other way of approaching this is to separate out the disambiguation into a separate call that would operate on original result list. This way, the only places we need to update are the UIBeans and associated pages. The rest of the API stays the same as it was, the legacy UI can stay there, no breakage in sight. The price for this is two calls into the EJB layer instead of just one and performing 3 or 4 SQL queries instead of just 1 or 2 (the data and the count query) per search. APIThe ResourceManagerLocal SLSB has a new method
/**
* Given a list of results, this method produces an object
* decorates the provided original results with data needed
* to disambiguate the results with respect to resource names,
* their types and ancestory.
* <p>
* The disambiguation result contains information on what types
* of information are needed to make the resources in the original
* result unambiguous and contains the decorated original data in
* the same order as the supplied result list.
*
* @see ResourceNamesDisambiguationResult
*
* @param <T> the type of the result elements
* @param results the results to disambiguate
* @param alwayIncludeParent if true, the parent disambiguation
* will always be included in the result even if the results
* wouldn't have to be disambiguated using parents.
* @param resourceIdExtractor an object able to extract resource
* id from an instance of type parameter.
* @return the disambiguation result or null on error
*/
ResourceNamesDisambiguationResult<T> disambiguate(List<T> results,
boolean alwaysIncludeParent,
IntExtractor<T> resourceIdExtractor);
This method, as one would hope, performs the disambiguation. The result list contains objects of any type and the resourceIdExtractor is used to extract a resource id out from those objects. The resource id is needed in the disambiguation procedure and since we have to support varied types of results (as mentioned in the introduction) the disambiguation method needs to be told how to work with those. It would be very cumbersome to define an overload of this method for every conceivable type of search results that need disambiguated. Each "user" of the disambiguate method needs to supply an instance of an implementation of the IntExtractor<T> interface. This is a simple one-method interface perfectly implemented using an anonymous inner class in a somewhat functional style of programming. The disambiguation result consists of the following:
The getResolution() method of the disambiguation result returns a List<DisambiguationReport<T>>. The disambiguation report contains the appropriate element from the original result list along with the type and plugin name of the corresponding resource and a list of parents that are needed to disambiguate the element in the result list (the parents are actually not the full blown resource objects but rather only an id and name "flyweight" representation of them). Application in the UISo now we have a way of disambiguating the search results and we want to update the UI to show them. Most of the results in the RHQ GUI are represented as PagedListDataModel<T> that is the RHQ way of paging through search results. We can take advantage of that and extend this class to automatically provide the disambiguation functionality (to reduce the amount of work needed in the UIBeans themselves):
/**
* This is an extension to the {@link PagedListDataModel} that
* automatically performs the disambiguation of the resource
* names contained in the pages of fetched data.
* <p>
* This class implements the {@link PagedListDataModel#fetchPage(PageControl)}
* method and defers the actual loading of the data to a new
* {@link #fetchDataForPage(PageControl)} method. The result of that call
* is supplied to the {@link ResourceManagerLocal#disambiguate(List, boolean, IntExtractor)}
* method and the disambiguated results are then returned from the
* {@link #fetchPage(PageControl)} method.
*
* @author Lukas Krejci
*/
public abstract class ResourceNameDisambiguatingPagedListDataModel<T> extends
PagedListDataModel<DisambiguationReport<T>> {
...
}
The UIBeans usually have a getDataModel() method providing data to the results table. This method returns an instance of a private class of the UIBean that fetches the actual data (this class extends from the PagedListDataModel<T>). So all we need to do is to make that class to inherit from the new ResourceNameDisambiguatingPagedListDataModel<T> and modify it slightly to conform to that new super class (i.e. suggest in the constructor whether to alwaysLoadParents, change the name of the fetchPage method to fetchDataForPage and implement the getResourceIdExtractor() method). With that the UIBean is converted. The XHTML page using that UIBean to show the results needs to be updated to reflect the changed data types and to actually show the disambiguated parents. Suppose that the rich:dataTable showing the results stores the current row in a variable called item. Inside the table we need to change references to properties of the item from #{item.property} to #{item.original.property}. To reference the same data as before the change.
<rich:column>
<f:facet name="header">
<h:outputText value="Parent"/>
</f:facet>
<onc:resourcePartialLineage parents="#{item.parents}" renderLinks="true"/>
</rich:column>
The important thing to note here is that the implementation of the disambiguation disallows the results to be sorted by the ancestry, so you cannot define any sorting on that column. Based on the actual disambiguation results it is possible to render the columns in the results table conditionally.
<ui:param name="model" value="#{MyUIBean.dataModel}"/>
use it in the rich:dataTable by specifying it as its value (value="#{model}") and then you can do: <rich:column rendered="#{model.currentPageNeedsTypeResolution}"> <f:facet name="header"> <h:outputText value="Resource Type"> </f:facet> <h:outputText value="#{item.resourceType}"/> </rich:column> RHQ4 Changes new!There remains a need for disambiguation (hereafter D12N) in the new GWT-based GUI for RHQ4. The RHQ3 solution has some disadvantages that we wanted to address as we move forward, given that we will now need to work D12N into the new GUI from scratch. Although the RHQ3 design is flexible in the varying D12N it can perform, it has the following disadvantages:
Additionally, in the GWT-based RHQ4 interface the additional database calls adds more complexity to the async services. The new approach - Pre-Computed AncestryThe solution is to take advantage of the fact that resource ancestry rarely changes and is already established in a top-down fashion. So, determining the ancestry in advance and keeping it persisted allows it to be returned along with the Resource entity itself. How it WorksBasically, the ancestry is determined and stored at resource-creation time. It is an encoded value define recursively as follows:
resourceAncestry=parentResourceResourceTypeId_:_parentResourceId_:_parentResourceName_::_parentResourceAncestry
NOTE: platform resources have no parent and therefore have a null ancestry.
Because parent resources are always inventoried prior to children, we can assume the parent's ancestry is already set. The value is stored as a varchar2(4000), providing worst-case space for seven levels of parentage (which is eight levels of hierarchy). Of course resource names rarely come close to the max-length of 500 so in reality there really is no practical depth limit. For cases where Resource entities are returned from the original query (almost always, especially in the RHQ4 interface and when using Criteria fetch) this approach eliminates additional db-round trips. The only exception being if you want to display the type ancestry and the necessary ResourceType entities have yet to be cached. Since the resource names are stores verbatim a displayable name-ancestry can be created very quickly. And it can include links to the resource views utilizing the provided resource ids. Given a Set of result resources it is also possible (although probably not necessary) to perform set-wise operations to trim the ancestry of repeated ancestry (for example, if all resources have the same platform, eliminate it from all the display strings). Updating the AncestryThere is a minimal cost associated with maintaining the pre-computed ancestry in the case of ancestry change. Ancestry can change in the following situations:
Neither of these is a common occurrence and meta-data update can only occur at plugin upgrade-time. And update time should not be a problem, even for large inventories. RHQ3 UpgradeFor RHQ3 to RHQ4 upgrades the legacy inventory will need to be updated with pre-computed ancestry values. This one-time process will be performed as an upgrade JavaTask. RHQ4 ApproachThis is a current design discussion. Current Design (subject to change)Thanks for the input so far. What I'm hearing:
So, after playing with this stuff for a bit I've been looking at two different formats for the Resource/Name column in various views. In the Inventory Views we have just the resource name and the ancestry resource names (note that here we also have Plugin and Type columns but that is more the exception than the rule):
And in other places, like portlets, we have a more verbose resource field:
Currently, in both views the Ancestry provides "hover" information that gives type info to supplement the resource name info:
Note that there has been no attempt made in rhq4 to mimic the contextual, varying display, approach used in rhq3. At the moment the column formats for name and ancestry are fixed. I think I'm in favor of the following approach:
So, the approach is basically to favor a clean, uniform look over immediate coverage of all D12N scenarios. And to use hover for more verbosity when required. Coding TipsThis section is for any tips for usage of ancestry strings in RHQ4. AncestryUtil classThis class holds any general purpose methods for processing ancestry strings. Typically decoding options or possibly trim utilities. ResourceDatasource and ResourceCompositeDatasourceThese are the main datasource classes fetching Resource entities and will embed logic to provide decoded ancestry strings for display. PerformanceSome minor befor/after performance tests have been performed on the ProblemResourcesPortlet. This portlet had already been updated with the rhq3 DisambiguationReport approach. A comparison and analysis follows: The test was simple and performed with a small inventory of 319 resources, all DOWN, and therefore all problem resources.
Analyisis
|


